Commit d48f8bb7 authored by Sebastien Robin's avatar Sebastien Robin

test_result: allow to define slapos parameters in test suites

parent 5f21add3
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
</item> </item>
<item> <item>
<key> <string>priority</string> </key> <key> <string>priority</string> </key>
<value> <float>1.0</float> </value> <value> <float>2.0</float> </value>
</item> </item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
<portal_type id="Test Suite"> <portal_type id="Test Suite">
<item>Arrow</item> <item>Arrow</item>
<item>Reference</item> <item>Reference</item>
<item>ScalabilityTestSuite</item>
<item>SortIndex</item> <item>SortIndex</item>
<item>TestSuite</item> <item>TestSuite</item>
<item>TestSuiteConstraint</item> <item>TestSuiteConstraint</item>
......
...@@ -36,9 +36,7 @@ ...@@ -36,9 +36,7 @@
</item> </item>
<item> <item>
<key> <string>init_script</string> </key> <key> <string>init_script</string> </key>
<value> <value> <string>TestSuite_init</string> </value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>permission</string> </key> <key> <string>permission</string> </key>
......
import json
result = True
try:
json.loads(json_string)
except ValueError:
result = False
return result
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>json_string, request</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_validateJSON</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# Required to avoid useless writes on disk
# and to have some database for tests
context.setClusterConfiguration("""{"mariadb": {
"relaxed-writes": true,
"mariadb-relaxed-writes": true,
"test-database-amount": 30}
}""")
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>*args, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>TestSuite_init</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -82,7 +82,9 @@ ...@@ -82,7 +82,9 @@
<item> <item>
<key> <string>center</string> </key> <key> <string>center</string> </key>
<value> <value>
<list/> <list>
<string>my_cluster_configuration</string>
</list>
</value> </value>
</item> </item>
<item> <item>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TextAreaField" module="Products.Formulator.StandardFields"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>my_cluster_configuration</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>Please use correct JSON syntax.</string> </value>
</item>
<item>
<key> <string>line_too_long</string> </key>
<value> <string>A line was too long.</string> </value>
</item>
<item>
<key> <string>required_not_found</string> </key>
<value> <string>Input is required but no input given.</string> </value>
</item>
<item>
<key> <string>too_long</string> </key>
<value> <string>You entered too many characters.</string> </value>
</item>
<item>
<key> <string>too_many_lines</string> </key>
<value> <string>You entered too many lines.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>alternate_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>css_class</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Slapos parameters.\n
\n
Configuration structure has to look like a python dict, ex:\n
\n
{\n
"x": "xxx",\n
"y":{\n
"q": "qqq",\n
"r": "rrr"\n
},\n
"z", "zzz"\n
}</string> </value>
</item>
<item>
<key> <string>editable</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>external_validator</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>extra</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>10</int> </value>
</item>
<item>
<key> <string>hidden</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>max_length</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>max_linelength</string> </key>
<value> <int>80</int> </value>
</item>
<item>
<key> <string>max_lines</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>required</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Slapos Parameters</string> </value>
</item>
<item>
<key> <string>unicode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>80</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Method" module="Products.Formulator.MethodField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>method_name</string> </key>
<value> <string>Base_validateJSON</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -104,9 +104,10 @@ class TestTaskDistribution(ERP5TypeTestCase): ...@@ -104,9 +104,10 @@ class TestTaskDistribution(ERP5TypeTestCase):
int_index = priority, int_index = priority,
specialise_value = specialise_value, specialise_value = specialise_value,
) )
test_suite.setClusterConfiguration(cluster_configuration)
if portal_type == "Scalability Test Suite": if portal_type == "Scalability Test Suite":
test_suite.setGraphCoordinate(graph_coordinate) test_suite.setGraphCoordinate(graph_coordinate)
test_suite.setClusterConfiguration(cluster_configuration)
test_suite.newContent( portal_type= 'Test Suite Repository', test_suite.newContent( portal_type= 'Test Suite Repository',
...@@ -391,6 +392,19 @@ class TestTaskDistribution(ERP5TypeTestCase): ...@@ -391,6 +392,19 @@ class TestTaskDistribution(ERP5TypeTestCase):
def test_10_cancelTestResult(self): def test_10_cancelTestResult(self):
pass pass
def test_10b_generateConfiguration(self):
"""
It shall be possible on a test suite to define configuration we would like
to use to create slapos instance.
"""
test_suite, = self._createTestSuite(cluster_configuration=None)
self.tic()
self.assertEquals('{"configuration_list": [{}]}', self.distributor.generateConfiguration(test_suite.getTitle()))
test_suite.setClusterConfiguration("{'foo': 3}")
self.assertEquals('{"configuration_list": [{}]}', self.distributor.generateConfiguration(test_suite.getTitle()))
test_suite.setClusterConfiguration('{"foo": 3}')
self.assertEquals('{"configuration_list": [{"foo": 3}]}', self.distributor.generateConfiguration(test_suite.getTitle()))
def _checkTestSuiteAggregateList(self, *args): def _checkTestSuiteAggregateList(self, *args):
self.tic() self.tic()
self._callOptimizeAlarm() self._callOptimizeAlarm()
......
...@@ -26,6 +26,7 @@ Test Suite Repository | TestSuiteRepository ...@@ -26,6 +26,7 @@ Test Suite Repository | TestSuiteRepository
Test Suite Repository | TestSuiteRepositoryConstraint Test Suite Repository | TestSuiteRepositoryConstraint
Test Suite | Arrow Test Suite | Arrow
Test Suite | Reference Test Suite | Reference
Test Suite | ScalabilityTestSuite
Test Suite | SortIndex Test Suite | SortIndex
Test Suite | TestSuite Test Suite | TestSuite
Test Suite | TestSuiteConstraint Test Suite | TestSuiteConstraint
\ No newline at end of file
...@@ -58,7 +58,6 @@ class SlapOSInstance(object): ...@@ -58,7 +58,6 @@ class SlapOSInstance(object):
def _checkData(self): def _checkData(self):
pass pass
class NodeTestSuite(SlapOSInstance): class NodeTestSuite(SlapOSInstance):
""" """
......
...@@ -332,7 +332,7 @@ class SlapOSControler(object): ...@@ -332,7 +332,7 @@ class SlapOSControler(object):
def spawn(self, *args, **kw): def spawn(self, *args, **kw):
return self.process_manager.spawn(*args, **kw) return self.process_manager.spawn(*args, **kw)
def runSoftwareRelease(self, config, environment): def runSoftwareRelease(self, config, environment, **kw):
self.log("SlapOSControler.runSoftwareRelease") self.log("SlapOSControler.runSoftwareRelease")
cpu_count = os.sysconf("SC_NPROCESSORS_ONLN") cpu_count = os.sysconf("SC_NPROCESSORS_ONLN")
os.putenv('MAKEFLAGS', '-j%s' % cpu_count) os.putenv('MAKEFLAGS', '-j%s' % cpu_count)
...@@ -352,31 +352,14 @@ class SlapOSControler(object): ...@@ -352,31 +352,14 @@ class SlapOSControler(object):
return status_dict return status_dict
def runComputerPartition(self, config, environment, def runComputerPartition(self, config, environment,
stdout=None, stderr=None): stdout=None, stderr=None, cluster_configuration=None, **kw):
self.log("SlapOSControler.runComputerPartition") self.log("SlapOSControler.runComputerPartition with cluster_config: %r" % (cluster_configuration,))
# cloudooo-json is required but this is a hack which should be removed
config['instance_dict']['cloudooo-json'] = "{}"
# report-url, report-project and suite-url are required to seleniumrunner
# instance. This is a hack which must be removed.
config['instance_dict']['report-url'] = config.get("report-url", "")
config['instance_dict']['report-project'] = config.get("report-project", "")
config['instance_dict']['suite-url'] = config.get("suite-url", "")
# XXX: Hack to minimize writes to storage holding MySQL databases.
# Note this is something we want for all test suites, so it would
# not be better to define this parameter on each test suite.
# XXX: Also move here the number of test db to create, so that software
# release stop create ones by default.
config['instance_dict']['_'] = json.dumps({"mariadb": {
"relaxed-writes": True,
"mariadb-relaxed-writes": True, # BBB
"test-database-amount": 30,
}})
for path in self.software_path_list: for path in self.software_path_list:
try: try:
self.slap.registerOpenOrder().request(path, self.slap.registerOpenOrder().request(path,
partition_reference='testing partition %s' % \ partition_reference='testing partition %s' % \
self.software_path_list.index(path), self.software_path_list.index(path),
partition_parameter_kw=config['instance_dict']) partition_parameter_kw=cluster_configuration)
except: except:
self.log("SlapOSControler.runComputerPartition, \ self.log("SlapOSControler.runComputerPartition, \
exception in registerOpenOrder", exc_info=sys.exc_info()) exception in registerOpenOrder", exc_info=sys.exc_info())
......
...@@ -89,7 +89,7 @@ class UnitTestRunner(): ...@@ -89,7 +89,7 @@ class UnitTestRunner():
log("Before status_dict = slapos_method(...)") log("Before status_dict = slapos_method(...)")
status_dict = slapos_method(self.testnode.config, status_dict = slapos_method(self.testnode.config,
environment=self.testnode.config['environment'], environment=self.testnode.config['environment'],
) **kw)
log(status_dict) log(status_dict)
log("After status_dict = slapos_method(...)") log("After status_dict = slapos_method(...)")
if status_dict['status_code'] != 0: if status_dict['status_code'] != 0:
...@@ -105,9 +105,18 @@ class UnitTestRunner(): ...@@ -105,9 +105,18 @@ class UnitTestRunner():
We will build slapos software needed by the testnode itself, We will build slapos software needed by the testnode itself,
like the building of selenium-runner by default like the building of selenium-runner by default
""" """
# report-url, report-project and suite-url are required to seleniumrunner
# instance. This is a hack which must be removed.
cluster_configuration = {}
config = self.testnode.config
cluster_configuration['report-url'] = config.get("report-url", "")
cluster_configuration['report-project'] = config.get("report-project", "")
cluster_configuration['suite-url'] = config.get("suite-url", "")
return self._prepareSlapOS(self.testnode.config['slapos_directory'], return self._prepareSlapOS(self.testnode.config['slapos_directory'],
test_node_slapos, self.testnode.log, create_partition=0, test_node_slapos, self.testnode.log, create_partition=0,
software_path_list=self.testnode.config.get("software_list")) software_path_list=self.testnode.config.get("software_list"),
cluster_configuration=cluster_configuration
)
def prepareSlapOSForTestSuite(self, node_test_suite): def prepareSlapOSForTestSuite(self, node_test_suite):
""" """
...@@ -118,7 +127,8 @@ class UnitTestRunner(): ...@@ -118,7 +127,8 @@ class UnitTestRunner():
log = self.testnode.log log = self.testnode.log
return self._prepareSlapOS(node_test_suite.working_directory, return self._prepareSlapOS(node_test_suite.working_directory,
node_test_suite, log, node_test_suite, log,
software_path_list=[node_test_suite.custom_profile_path]) software_path_list=[node_test_suite.custom_profile_path],
cluster_configuration={'_': json.dumps(node_test_suite.cluster_configuration)})
def runTestSuite(self, node_test_suite, portal_url, log=None): def runTestSuite(self, node_test_suite, portal_url, log=None):
config = self.testnode.config config = self.testnode.config
......
...@@ -393,6 +393,11 @@ from the distributor.") ...@@ -393,6 +393,11 @@ from the distributor.")
self.registerSuiteLog(test_result, node_test_suite) self.registerSuiteLog(test_result, node_test_suite)
self.checkRevision(test_result,node_test_suite) self.checkRevision(test_result,node_test_suite)
node_test_suite.edit(test_result=test_result) node_test_suite.edit(test_result=test_result)
# get cluster configuration for this test suite, this is needed to
# know slapos parameters to user for creating instances
node_test_suite.edit(cluster_configuration=Utils.deunicodeData(
json.loads(self.test_suite_portal.generateConfiguration(
node_test_suite.test_suite_title))['configuration_list'][0]))
# Now prepare the installation of SlapOS and create instance # Now prepare the installation of SlapOS and create instance
status_dict = runner.prepareSlapOSForTestSuite(node_test_suite) status_dict = runner.prepareSlapOSForTestSuite(node_test_suite)
# Give some time so computer partitions may start # Give some time so computer partitions may start
......
...@@ -351,8 +351,9 @@ class ERP5ProjectUnitTestDistributor(XMLObject): ...@@ -351,8 +351,9 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
title=SimpleQuery(comparison_operator='=', title=suite_title), title=SimpleQuery(comparison_operator='=', title=suite_title),
validation_state='validated') validation_state='validated')
assert len(test_suite_list) == 1, "We found %i test suite for %s" % ( assert len(test_suite_list) == 1, "We found %i test suite for %s" % (
len(test_suite_list), name) len(test_suite_list), suite_title)
test_suite = test_suite_list[0].getObject() test_suite = test_suite_list[0].getObject()
return test_suite
security.declarePublic("startUnitTest") security.declarePublic("startUnitTest")
def startUnitTest(self,test_result_path,exclude_list=()): def startUnitTest(self,test_result_path,exclude_list=()):
...@@ -373,3 +374,20 @@ class ERP5ProjectUnitTestDistributor(XMLObject): ...@@ -373,3 +374,20 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
test_result = portal.unrestrictedTraverse(test_path) test_result = portal.unrestrictedTraverse(test_path)
test_suite_title = test_result.getTitle() test_suite_title = test_result.getTitle()
return portal.portal_task_distribution_tool.stopUnitTest(self,test_path,status_dict) return portal.portal_task_distribution_tool.stopUnitTest(self,test_path,status_dict)
security.declarePublic("generateConfiguration")
def generateConfiguration(self, test_suite_title, batch_mode=0):
"""
return the list of configuration to create instances, in the case of ERP5 unit tests,
we will have only one configuration (unlike scalability tests). But for API consistency,
always return a list.
"""
test_suite = self._getTestSuiteFromTitle(test_suite_title)
cluster_configuration = test_suite.getClusterConfiguration() or '{}'
try:
generated_configuration = [json.loads(cluster_configuration)]
except ValueError:
generated_configuration = [{}]
if batch_mode:
return generated_configuration
return json.dumps(generated_configuration)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment