From a9c8f1643de38ab6ca80f94e08a2ff7c2e672f1a Mon Sep 17 00:00:00 2001
From: Benjamin Blanc <benjamin.blanc@tiolive.com>
Date: Mon, 17 Jun 2013 16:02:57 +0200
Subject: [PATCH] Fix bug(s) and add a scalability unit test

---
 erp5/tests/testERP5TestNode.py              | 109 +++++++++++++++++---
 erp5/util/testnode/ScalabilityTestRunner.py |  27 ++---
 erp5/util/testnode/testnode.py              |  25 +++--
 erp5/util/testnode/testnodeUtils.py         |   8 +-
 4 files changed, 128 insertions(+), 41 deletions(-)

diff --git a/erp5/tests/testERP5TestNode.py b/erp5/tests/testERP5TestNode.py
index d0a38b540c..72d66e6f53 100644
--- a/erp5/tests/testERP5TestNode.py
+++ b/erp5/tests/testERP5TestNode.py
@@ -475,9 +475,9 @@ branch = foo
     def patch_getSlaposAccountCertificate(self, *args, **kw):
       return "Certificate"
     def patch_generateConfiguration(self, *args, **kw):
-      return '{"configuration_list": [], "involved_nodes_computer_guid"\
-: [], "error_message": "No error.", "launcher_nodes_computer_guid": {}, \
-"launchable": false, "randomized_path" : "azertyuiop"}'
+      return json.dumps({"configuration_list": [], "involved_nodes_computer_guid"\
+: [], "error_message": "No error.", "launcher_nodes_computer_guid": [], \
+"launchable": False, "randomized_path" : "azertyuiop"})
     def patch_isMasterTestnode(self, *args, **kw):
       return (grade == 'master')
     test_self = self
@@ -490,7 +490,10 @@ branch = foo
       config_list = []
       # Sclalability slave testnode is not directly in charge of testsuites
       if my_test_type == 'ScalabilityTest' and grade == 'slave':
-        return []
+        if counter == 5:
+          raise StopIteration
+        counter += 1
+        return json.dumps([])
           
       def _checkExistingTestSuite(reference_set):
         test_self.assertEquals(set(reference_set),
@@ -564,12 +567,7 @@ branch = foo
     SlapOSControler.initializeSlapOSControler = doNothing
     # Inside test_node a runner is created using new UnitTestRunner methods
     test_node.run()
-    # Doesn't have to install sofwtare themself
-    if my_test_type == 'ScalabilityTest' and grade == 'slave' :
-      self.assertEquals(0, counter)
-    else :
-      self.assertEquals(5, counter)
-
+    self.assertEquals(5, counter)
     time.sleep = original_sleep
     # Restore old class methods
     if my_test_type == "ScalabilityTest":
@@ -632,11 +630,11 @@ branch = foo
     def patch_getSlaposAccountCertificate(self, *args, **kw):
       return "Certificate"
     def patch_generateConfiguration(self, *args, **kw):
-      return '{"configuration_list": [], "involved_nodes_computer_guid"\
-: [], "error_message": "No error.", "launcher_nodes_computer_guid": {}, \
-"launchable": false, "randomized_path" : "azertyuiop"}'
+      return json.dumps({"configuration_list": [], "involved_nodes_computer_guid"\
+: [], "error_message": "No error.", "launcher_nodes_computer_guid": [], \
+"launchable": False, "randomized_path" : "azertyuiop"})
     def patch_isMasterTestnode(self, *args, **kw):
-      return (grade == 'master')
+      return grade == 'master'
     test_self = self
     test_result_path_root = os.path.join(test_self._temp_dir,'test/results')
     os.makedirs(test_result_path_root)
@@ -854,4 +852,85 @@ branch = foo
   def test_scalability_18_resetSoftwareAfterManyBuildFailures(self, my_test_type='ScalabilityTest'):
     # TODO : write own scalability test
     pass
-  #TODO : add more test for scalability case
+
+  def test_zzzz_scalability_19_xxxx(self):
+    def patch_createTestResult(self, revision, test_name_list, node_title,
+            allow_restart=False, test_title=None, project_title=None):
+      test_result_path = os.path.join(test_result_path_root, test_title)
+      result =  TestResultProxy(self._proxy, self._retry_time,
+                self._logger, test_result_path, node_title, revision)
+      return result
+    global startTestSuiteDone
+    startTestSuiteDone = False
+    def patch_startTestSuite(self,test_node_title):
+      config_list = []
+      global startTestSuiteDone
+      if not startTestSuiteDone:
+        startTestSuiteDone = True
+        config_list.append(test_self.getTestSuiteData(reference='foo')[0])
+        config_list.append(test_self.getTestSuiteData(reference='bar')[0])
+      else:
+        raise StopIteration
+      return json.dumps(config_list)
+    def patch_isMasterTestnode(self, *args, **kw):
+      return True
+    def patch_generateConfiguration(self, *args, **kw):
+      return json.dumps({"configuration_list": [{"ok":"ok"}], "involved_nodes_computer_guid"\
+: ["COMP1", "COMP2", "COMP3"], "error_message": "No error.", "launcher_nodes_computer_guid": ["COMP1"], \
+"launchable": False, "randomized_path" : "azertyuiop"})
+    def doNothing(self, *args, **kw):
+        pass
+    def patch_getSlaposAccountKey(self, *args, **kw):
+      return "key"
+    def patch_getSlaposAccountCertificate(self, *args, **kw):
+      return "Certificate"
+    def patch_getTestType(self, *args, **kw):
+      return "ScalabilityTest"
+    test_self = self
+    test_result_path_root = os.path.join(test_self._temp_dir,'test/results')
+    os.makedirs(test_result_path_root)
+    self.generateTestRepositoryList()
+    # Select the good runner to modify
+    RunnerClass = self.returnGoodClassRunner('ScalabilityTest')
+    # Patch methods
+    original_sleep = time.sleep
+    original_getSlaposAccountKey = TaskDistributor.getSlaposAccountKey
+    original_getSlaposAccountCertificate = TaskDistributor.getSlaposAccountCertificate
+    original_generateConfiguration = TaskDistributor.generateConfiguration
+    original_isMasterTestnode = TaskDistributor.isMasterTestnode
+    original_startTestSuite = TaskDistributor.startTestSuite
+    original_subscribeNode = TaskDistributor.subscribeNode
+    original_getTestType = TaskDistributor.getTestType
+    original_createTestResult = TaskDistributionTool.createTestResult
+    original_prepareSlapOS = RunnerClass._prepareSlapOS
+    original_runTestSuite = RunnerClass.runTestSuite
+    original_supply = SlapOSControler.supply
+    #
+    time.sleep = doNothing
+    TaskDistributor.getSlaposAccountKey = patch_getSlaposAccountKey
+    TaskDistributor.getSlaposAccountCertificate = patch_getSlaposAccountCertificate
+    TaskDistributor.generateConfiguration = patch_generateConfiguration
+    TaskDistributor.isMasterTestnode = patch_isMasterTestnode
+    TaskDistributor.startTestSuite = patch_startTestSuite
+    TaskDistributor.subscribeNode = doNothing
+    TaskDistributor.getTestType = patch_getTestType    
+    TaskDistributionTool.createTestResult = patch_createTestResult
+    RunnerClass._prepareSlapOS = doNothing
+    RunnerClass.runTestSuite = doNothing
+    SlapOSControler.supply = doNothing
+    # Run
+    test_node = self.getTestNode()  
+    test_node.run()
+    # Restore methods
+    TaskDistributor.getSlaposAccountKey = original_getSlaposAccountKey
+    TaskDistributor.getSlaposAccountCertificate = original_getSlaposAccountCertificate
+    TaskDistributor.generateConfiguration = original_generateConfiguration
+    TaskDistributor.isMasterTestnode = original_isMasterTestnode
+    TaskDistributor.startTestSuite = original_startTestSuite
+    TaskDistributionTool.createTestResult = original_createTestResult
+    TaskDistributionTool.subscribeNode = original_subscribeNode
+    TaskDistributionTool.getTestType = original_getTestType
+    RunnerClass._prepareSlapOS = original_prepareSlapOS
+    RunnerClass.runTestSuite = original_runTestSuite
+    SlapOSControler.supply = original_supply
+    time.sleep =original_sleep
diff --git a/erp5/util/testnode/ScalabilityTestRunner.py b/erp5/util/testnode/ScalabilityTestRunner.py
index 6ab146d15e..f590a533b5 100644
--- a/erp5/util/testnode/ScalabilityTestRunner.py
+++ b/erp5/util/testnode/ScalabilityTestRunner.py
@@ -49,17 +49,18 @@ import signal
 class ScalabilityTestRunner():
   def __init__(self, testnode):
     self.testnode =  testnode
+    self.log = self.testnode.log
+    
     self.slapos_controler = SlapOSControler.SlapOSControler(
                                   self.testnode.working_directory,
                                   self.testnode.config,
-                                  self.testnode.log)
+                                  self.log)
     # Create the slapos account configuration file and dir
     key = self.testnode.test_suite_portal.getSlaposAccountKey()
     certificate = self.testnode.test_suite_portal.getSlaposAccountCertificate()
     self.slapos_controler.createSlaposConfigurationFileAccount(key,certificate,
                                     self.testnode.config)
                               
-    
     self.remaining_software_installation_dict = {}
     
     # Protection to prevent installation of softwares after checking
@@ -72,7 +73,7 @@ class ScalabilityTestRunner():
     """
     A proxy to supply : Install a software on a specific node
     """
-    self.testnode.log("testnode, supply : %s %s", software_path, computer_guid)
+    self.log("testnode, supply : %s %s", software_path, computer_guid)
     if self.authorize_supply :
       self.remaining_software_installation_dict[computer_guid] = software_path
       self.slapos_controler.supply(software_path, computer_guid)
@@ -94,7 +95,7 @@ class ScalabilityTestRunner():
 
   # Dummy slapos answering
   def _getSignal(self, signal, frame):
-    self.testnode.log("Dummy SlapOS Master answer received.")
+    self.log("Dummy SlapOS Master answer received.")
     self.last_slapos_answer.append(True)
   def _prepareDummySlapOSAnswer(self):
     print "Dummy slapOS answer enabled, send signal to %s (=PID) to simu\
@@ -155,21 +156,21 @@ late a SlapOS (positive) answer." %(str(os.getpid()),)
 
       # Avoid the test if it is not launchable
       if not self.launchable:
-        self.testnode.log("Test suite %s is not actually launchable with \
+        self.log("Test suite %s is not actually launchable with \
   the current cluster configuration." %(node_test_suite.test_suite_title,))
-        self.testnode.log("ERP5 Master indicates : %s" %(self.error_message,))
+        self.log("ERP5 Master indicates : %s" %(self.error_message,))
         # error : wich code to return ?
         return {'status_code' : 1}
 
       # create an obfuscated link to the testsuite directory
-      self.testnode.log("self.testnode.config['software_directory']\
+      self.log("self.testnode.config['software_directory']\
  : %s" %(self.testnode.config['software_directory']))
-      self.testnode.log("self.randomized_path\
+      self.log("self.randomized_path\
  : %s" %(self.randomized_path))
       path_to_suite = os.path.join(
                       self.testnode.config['working_directory'],
                       node_test_suite.reference)
-      self.testnode.log("path_to_suite\
+      self.log("path_to_suite\
  : %s" %(path_to_suite))
       self.ofuscated_link_path = os.path.join(
                       self.testnode.config['software_directory'],
@@ -181,7 +182,7 @@ late a SlapOS (positive) answer." %(str(os.getpid()),)
         except :
           raise ValueError("testnode, Unable to create symbolic link to the testsuite.")
           
-      self.testnode.log("Sym link : %s %s" %(path_to_suite, self.ofuscated_link_path))
+      self.log("Sym link : %s %s" %(path_to_suite, self.ofuscated_link_path))
       
 
       involved_nodes_computer_guid = test_configuration['involved_nodes_computer_guid']
@@ -192,7 +193,7 @@ late a SlapOS (positive) answer." %(str(os.getpid()),)
       self.reachable_profile = os.path.join(
         "https://","["+self.testnode.config['httpd_ip']+"]"+":"+self.testnode.config['httpd_software_access_port'],
         self.randomized_path, "software.cfg")
-      self.testnode.log("Software reachable profile path is : %s "
+      self.log("Software reachable profile path is : %s "
                               %(self.reachable_profile,))
       software_path_list.append(self.reachable_profile)
       # Ask for softwares installation
@@ -206,7 +207,7 @@ late a SlapOS (positive) answer." %(str(os.getpid()),)
       # Waiting until all softwares are installed
       while ( self.remainSoftwareToInstall() 
          and (max_time > (time.time()-start_time))):
-        self.testnode.log("Master testnode is waiting\
+        self.log("Master testnode is waiting\
  for the end of all software installation (for %ss).",
           str(int(time.time()-start_time)))
         time.sleep(interval_time)
@@ -215,7 +216,7 @@ late a SlapOS (positive) answer." %(str(os.getpid()),)
       self._comeBackFromDummySlapOS()
       if self.remainSoftwareToInstall() :
         return {'status_code' : 1}
-    self.testnode.log("Softwares installed")
+    self.log("Softwares installed")
     return {'status_code' : 0}
 
   def _cleanUpNodesInformation(self):
diff --git a/erp5/util/testnode/testnode.py b/erp5/util/testnode/testnode.py
index 4621aa7850..a10e6f10aa 100644
--- a/erp5/util/testnode/testnode.py
+++ b/erp5/util/testnode/testnode.py
@@ -38,6 +38,9 @@ import logging
 import string
 import random
 import testnodeUtils
+
+import traceback
+
 from ProcessManager import SubprocessError, ProcessManager, CancellationError
 from subprocess import CalledProcessError
 from Updater import Updater
@@ -298,7 +301,6 @@ branch = %(branch)s
     test_node_slapos = SlapOSInstance()
     test_node_slapos.edit(working_directory=self.config['slapos_directory'])
     ## /BLOCK OK
-
     try:
       while True:
         try:
@@ -310,9 +312,10 @@ branch = %(branch)s
           portal_url = config['test_suite_master_url']
           portal = taskdistribution.TaskDistributionTool(portal_url, logger=DummyLogger(log))
           self.test_suite_portal = taskdistribution.TaskDistributor(portal_url, logger=DummyLogger(log))
-          self.test_suite_portal.subscribeNode(config['test_node_title'], config['computer_id'])        
-          
+          self.test_suite_portal.subscribeNode(config['test_node_title'], config['computer_id'])                  
+
           test_suite_json =  self.test_suite_portal.startTestSuite(config['test_node_title'])
+
           test_suite_data = testnodeUtils.deunicodeData(json.loads(test_suite_json))
           log("Got following test suite data from master : %r" % \
               (test_suite_data,))
@@ -337,8 +340,11 @@ from the distributor.")
           # master testnode gets test_suites, slaves get nothing
           runner.prepareSlapOSForTestNode(test_node_slapos)
           # Clean-up test suites
-          self.checkOldTestSuite(test_suite_data)
           
+          self.checkOldTestSuite(test_suite_data)
+
+
+                
           for test_suite in test_suite_data:
             remote_test_result_needs_cleanup = False
             node_test_suite = self.getNodeTestSuite(
@@ -360,13 +366,15 @@ from the distributor.")
                      node_test_suite.project_title)
             remote_test_result_needs_cleanup = True
             log("testnode, test_result : %r" % (test_result, ))
-            
+
             if test_result is not None:
               self.registerSuiteLog(test_result, node_test_suite)
               self.checkRevision(test_result,node_test_suite)
+
               # Now prepare the installation of SlapOS and create instance
               status_dict = runner.prepareSlapOSForTestSuite(node_test_suite)
-              
+
+                
               # Give some time so computer partitions may start
               # as partitions can be of any kind we have and likely will never have
               # a reliable way to check if they are up or not ...
@@ -396,6 +404,9 @@ from the distributor.")
           node_test_suite.retry = True
           continue
         except:
+            log("Errror")
+            ex_type, ex, tb = sys.exc_info()
+            traceback.print_tb(tb)
             log("erp5testnode exception", exc_info=sys.exc_info())
             raise
         now = time.time()
@@ -407,6 +418,8 @@ from the distributor.")
     except:
       log("Exception in error handling", exc_info=sys.exc_info())
     finally:
+      if 'tb' in locals():
+        del tb
       # Nice way to kill *everything* generated by run process -- process
       # groups working only in POSIX compilant systems
       # Exceptions are swallowed during cleanup phase
diff --git a/erp5/util/testnode/testnodeUtils.py b/erp5/util/testnode/testnodeUtils.py
index 2fb8c12bd7..efd46890ae 100644
--- a/erp5/util/testnode/testnodeUtils.py
+++ b/erp5/util/testnode/testnodeUtils.py
@@ -4,13 +4,7 @@ import shutil
 import string
 
 def deunicodeData(data):
-  if isinstance(data, int):
-    new_data = data
-  elif isinstance(data, str):
-    new_data = data
-  elif isinstance(data, bool):
-    new_data = data
-  elif isinstance(data, list):
+  if isinstance(data, list):
     new_data = []
     for sub_data in data:
       new_data.append(deunicodeData(sub_data))
-- 
2.30.9