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>
  • Any reason to justify the limitation of 80 characters per line in slapos parameters, @romain? My jstestnode PoC for iOS testing requires a parameter to connect to the ios emulator service that always bigger than 80 characters (key-value definition from Python). Locally I just raised the max_line_length to 999 (because it's a required limitation that cannot be explicitely set to infinity) and it worked.

  • When you discussed with me about this 80 characters limit, I was not aware that it was only a limit set in the user interface. In that case, please just remove the limit (if no limit is defined, it should be possible to have lines as long as we want), there is no reasons at all for having it. I think it is there only due to a copy/paste of another field. This field should by transformed into a proxy field to avoid such mistake.

Please register or sign in to reply
</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):
Please register or sign in to reply
"""
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