From 77539e6de2506190af2dbcad0a74e752466e418a Mon Sep 17 00:00:00 2001
From: Nicolas Delaby <nicolas@nexedi.com>
Date: Tue, 27 Apr 2010 11:59:02 +0000
Subject: [PATCH] Add test which check that conversion engine is able to fill
 in cache without overwrite previous conversion when processed at the same
 time. This test show that conversion cache design is not scallable.

All conversions are stored in a unique dictionary per Document.
This dictionary can not be modified by parallel threads because
multiple copies of this dictionary are distributed among threads.
When The last thread finish, it take precedence and erase previous
copies of that dictionary.



git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@34817 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5OOo/tests/testDms.py              |  83 ++++++++++++++++++
 .../test_document/Forty-Two.Pages-en-001.pdf  | Bin 0 -> 24459 bytes
 2 files changed, 83 insertions(+)
 create mode 100644 product/ERP5OOo/tests/test_document/Forty-Two.Pages-en-001.pdf

diff --git a/product/ERP5OOo/tests/testDms.py b/product/ERP5OOo/tests/testDms.py
index c9a317dbd4..835333ee27 100644
--- a/product/ERP5OOo/tests/testDms.py
+++ b/product/ERP5OOo/tests/testDms.py
@@ -57,11 +57,15 @@ from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
 from Products.ERP5Type.tests.utils import FileUpload
 from Products.ERP5Type.tests.utils import DummyLocalizer
 from Products.ERP5OOo.OOoUtils import OOoBuilder
+from Products.CMFCore.utils import getToolByName
 from AccessControl.SecurityManagement import newSecurityManager
+from AccessControl import getSecurityManager
 from zLOG import LOG
 from Products.ERP5.Document.Document import NotConvertedError
 from Products.ERP5Type.tests.backportUnittest import expectedFailure
 import os
+from threading import Thread
+import httplib
 
 QUIET = 0
 RUN_ALL_TEST = 1
@@ -1551,6 +1555,85 @@ style=3D'color:black'>05D65812<o:p></o:p></span></p>
     self.assertTrue('AZERTYY' not in safe_html)
     self.assertTrue('#FFAA44' in safe_html)
 
+  def test_parallel_conversion(self):
+    """Check that conversion engine is able to fill in
+    cache without overwrite previous conversion
+    when processed at the same time.
+    """
+    portal_type = 'PDF'
+    document_module = self.portal.getDefaultModule(portal_type)
+    document = document_module.newContent(portal_type=portal_type)
+
+    upload_file = makeFileUpload('Forty-Two.Pages-en-001.pdf')
+    document.edit(file=upload_file)
+    pages_number = int(document.getContentInformation()['Pages'])
+    transaction.commit()
+    self.tic()
+
+    document.clearConversionCache()
+    transaction.commit()
+    self.tic()
+
+    class ThreadWrappedConverter(Thread):
+      """Use this class to run different convertion
+      inside distinct Thread.
+      """
+      def __init__(self, publish_method, document_path,
+                   frame_list, credential):
+        self.publish_method = publish_method
+        self.document_path = document_path
+        self.frame_list = frame_list
+        self.credential = credential
+        Thread.__init__(self)
+
+      def run(self):
+        for frame in self.frame_list:
+          # Use publish method to dispatch conversion among
+          # all available Zserver threads.
+          response = self.publish_method('%s/index_html?format=png'\
+                              '&display=thumbnail&quality:int=75&resolution='\
+                              '&frame=%s' % (self.document_path, frame),
+                                                               self.credential)
+          assert response.getHeader('content-type') == 'image/png'
+          assert response.getStatus() ==  httplib.OK
+        transaction.commit()
+
+    # assume there is no password
+    credential = '%s:' % (getSecurityManager().getUser().getId(),)
+    tested_list = []
+    frame_list = list(xrange(pages_number))
+    # assume that ZServer is configured with 4 Threads
+    conversion_per_tread = pages_number / 4
+    while frame_list:
+      local_frame_list = [frame_list.pop() for i in\
+                            xrange(min(conversion_per_tread, len(frame_list)))]
+      instance = ThreadWrappedConverter(self.publish, document.getPath(),
+                                        local_frame_list, credential)
+      tested_list.append(instance)
+      instance.start()
+
+    # Wait until threads finishing
+    [tested.join() for tested in tested_list]
+
+    transaction.commit()
+    self.tic()
+
+    preference_tool = getToolByName(self.portal, 'portal_preferences')
+    image_size = preference_tool.getPreferredThumbnailImageHeight(),\
+                              preference_tool.getPreferredThumbnailImageWidth()
+    convert_kw = {'format': 'png',
+                  'display': 'thumbnail',
+                  'quality': 75,
+                  'image_size': image_size,
+                  'resolution': ''}
+    result_list = []
+    for i in xrange(pages_number):
+      # all conversions should succeeded and stored in cache storage
+      convert_kw['frame'] = str(i)
+      if not document.hasConversion(**convert_kw):
+        result_list.append(i)
+    self.assertEquals(result_list, [])
+
 class TestDocumentWithSecurity(ERP5TypeTestCase):
 
   username = 'yusei'
diff --git a/product/ERP5OOo/tests/test_document/Forty-Two.Pages-en-001.pdf b/product/ERP5OOo/tests/test_document/Forty-Two.Pages-en-001.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..52cc3d6647861bb15a68274009efcbad9bb5a104
GIT binary patch
literal 24459
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
Ycz9a4dlEu!7#L&%w7k3u8j7_42T&XilK=n!

literal 0
HcmV?d00001

-- 
2.30.9