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 @@
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
<value> <float>2.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
......
......@@ -48,6 +48,7 @@
<portal_type id="Test Suite">
<item>Arrow</item>
<item>Reference</item>
<item>ScalabilityTestSuite</item>
<item>SortIndex</item>
<item>TestSuite</item>
<item>TestSuiteConstraint</item>
......
......@@ -36,9 +36,7 @@
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
<value> <string>TestSuite_init</string> </value>
</item>
<item>
<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 @@
<item>
<key> <string>center</string> </key>
<value>
<list/>
<list>
<string>my_cluster_configuration</string>
</list>
</value>
</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):
int_index = priority,
specialise_value = specialise_value,
)
test_suite.setClusterConfiguration(cluster_configuration)
if portal_type == "Scalability Test Suite":
test_suite.setGraphCoordinate(graph_coordinate)
test_suite.setClusterConfiguration(cluster_configuration)
test_suite.newContent( portal_type= 'Test Suite Repository',
......@@ -391,6 +392,19 @@ class TestTaskDistribution(ERP5TypeTestCase):
def test_10_cancelTestResult(self):
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):
self.tic()
self._callOptimizeAlarm()
......
......@@ -26,6 +26,7 @@ Test Suite Repository | TestSuiteRepository
Test Suite Repository | TestSuiteRepositoryConstraint
Test Suite | Arrow
Test Suite | Reference
Test Suite | ScalabilityTestSuite
Test Suite | SortIndex
Test Suite | TestSuite
Test Suite | TestSuiteConstraint
\ No newline at end of file
......@@ -58,7 +58,6 @@ class SlapOSInstance(object):
def _checkData(self):
pass
class NodeTestSuite(SlapOSInstance):
"""
......
......@@ -332,7 +332,7 @@ class SlapOSControler(object):
def spawn(self, *args, **kw):
return self.process_manager.spawn(*args, **kw)
def runSoftwareRelease(self, config, environment):
def runSoftwareRelease(self, config, environment, **kw):
self.log("SlapOSControler.runSoftwareRelease")
cpu_count = os.sysconf("SC_NPROCESSORS_ONLN")
os.putenv('MAKEFLAGS', '-j%s' % cpu_count)
......@@ -352,31 +352,14 @@ class SlapOSControler(object):
return status_dict
def runComputerPartition(self, config, environment,
stdout=None, stderr=None):
self.log("SlapOSControler.runComputerPartition")
# 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,
}})
stdout=None, stderr=None, cluster_configuration=None, **kw):
self.log("SlapOSControler.runComputerPartition with cluster_config: %r" % (cluster_configuration,))
for path in self.software_path_list:
try:
self.slap.registerOpenOrder().request(path,
partition_reference='testing partition %s' % \
self.software_path_list.index(path),
partition_parameter_kw=config['instance_dict'])
partition_parameter_kw=cluster_configuration)
except:
self.log("SlapOSControler.runComputerPartition, \
exception in registerOpenOrder", exc_info=sys.exc_info())
......
......@@ -89,7 +89,7 @@ class UnitTestRunner():
log("Before status_dict = slapos_method(...)")
status_dict = slapos_method(self.testnode.config,
environment=self.testnode.config['environment'],
)
**kw)
log(status_dict)
log("After status_dict = slapos_method(...)")
if status_dict['status_code'] != 0:
......@@ -105,9 +105,18 @@ class UnitTestRunner():
We will build slapos software needed by the testnode itself,
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'],
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):
"""
......@@ -118,7 +127,8 @@ class UnitTestRunner():
log = self.testnode.log
return self._prepareSlapOS(node_test_suite.working_directory,
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):
config = self.testnode.config
......
......@@ -393,6 +393,11 @@ from the distributor.")
self.registerSuiteLog(test_result, node_test_suite)
self.checkRevision(test_result,node_test_suite)
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
status_dict = runner.prepareSlapOSForTestSuite(node_test_suite)
# Give some time so computer partitions may start
......
......@@ -351,8 +351,9 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
title=SimpleQuery(comparison_operator='=', title=suite_title),
validation_state='validated')
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()
return test_suite
security.declarePublic("startUnitTest")
def startUnitTest(self,test_result_path,exclude_list=()):
......@@ -373,3 +374,20 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
test_result = portal.unrestrictedTraverse(test_path)
test_suite_title = test_result.getTitle()
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