Commit d9015520 authored by Sebastien Robin's avatar Sebastien Robin

erp5_test_result: run first test suite with no results

Optimize sorting of list of test suite to be executed by test nodes.
We give priority to test suite with older test results. This allows
to have test node resources in a more fair way.
parent e3ea4b39
......@@ -2,6 +2,7 @@ from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from zLOG import LOG,INFO,ERROR
import json
from Products.ERP5Type.Log import log
from time import sleep
class TestTaskDistribution(ERP5TypeTestCase):
def afterSetUp(self):
......@@ -181,6 +182,69 @@ class TestTaskDistribution(ERP5TypeTestCase):
[('COMP32-Node1',set([u'B1'])), ('COMP32-Node2',set([u'B0']))]],
"%r" % ([config1, config2],))
def test_04b_startTestSuiteOrder(self):
"""
When we have many test suites associated to one test nodes, the method
startTestSuite should give first test suites with oldest test results. Like
this we stop using the random order that was unfair for unlucky peoples
"""
config_list = json.loads(self.distributor.startTestSuite(
title="COMP42-Node1"))
self.assertEqual([], config_list)
self._createTestSuite(quantity=3)
self.tic()
self._callOptimizeAlarm()
def getTestSuiteList():
config_list = json.loads(self.distributor.startTestSuite(
title="COMP42-Node1"))
return ["%s" % x["test_suite_title"] for x in config_list]
# By default we have random order between test suites
self.assertEquals(set(["test suite 1", "test suite 2", "test suite 3"]),
set(getTestSuiteList()))
# Check that if test suite 1 and test suite 2 are recently processed,
# then next work must be test suite 3
def processTest(test_title, revision):
status_dict = {}
test_result_path, revision = self._createTestResult(revision=revision,
test_list=['testFoo', 'testBar'], test_title=test_title)
line_url, test = self.tool.startUnitTest(test_result_path)
next_line_url, next_test = self.tool.startUnitTest(test_result_path)
self.assertEqual(set(['testFoo', 'testBar']), set([test, next_test]))
self.tool.stopUnitTest(line_url, status_dict)
self.tool.stopUnitTest(next_line_url, status_dict)
test_result = self.portal.restrictedTraverse(test_result_path)
self.assertEquals(test_result.getSimulationState(), "stopped")
processTest("test suite 1", "r0=a")
self.tic()
sleep(1) # needed because creation date sql value does not record millesecond
processTest("test suite 2", "r0=b")
self.tic()
sleep(1)
self.assertEquals(getTestSuiteList()[0], "test suite 3")
processTest("test suite 3", "r0=b")
# after test suite 3, we now have to process test suite 1
# since it is the oldest one
self.tic()
sleep(1)
self.assertEquals(getTestSuiteList()[0], "test suite 1")
processTest("test suite 1", "r0=c")
# after test suite 2, we now have to process test suite 2
# since it is the oldest one
self.tic()
sleep(1)
self.assertEquals(getTestSuiteList()[0], "test suite 2")
processTest("test suite 2", "r0=d")
self.tic()
sleep(1)
# now let's say for any reasyon test suite 1 has been done
processTest("test suite 1", "r0=e")
self.tic()
sleep(1)
# we should then have by order 3, 2, 1
self.assertEquals(["test suite 3", "test suite 2", "test suite 1"],
getTestSuiteList())
def _cleanupTestResult(self):
self.tic()
cleanup_state_list = ['started', 'stopped']
......@@ -192,10 +256,11 @@ class TestTaskDistribution(ERP5TypeTestCase):
self.tic()
def _createTestResult(self, revision="r0=a,r1=a", node_title="Node0",
test_list=None, tic=1, allow_restart=False):
test_list=None, tic=1, allow_restart=False,
test_title="TEST FOO"):
result = self.tool.createTestResult(
"", revision, test_list or [], allow_restart,
test_title="TEST FOO", node_title=node_title)
test_title=test_title, node_title=node_title)
# we commit, since usually we have a remote call only doing this
(self.tic if tic else self.commit)()
return result
......@@ -739,4 +804,4 @@ class TestTaskDistribution(ERP5TypeTestCase):
# log(configuration_list)
def test_19_testMultiDistributor(self):
pass
pass
\ No newline at end of file
259
\ No newline at end of file
260
\ No newline at end of file
......@@ -37,6 +37,7 @@ import string
from zLOG import LOG,INFO,ERROR
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
from Products.ZSQLCatalog.SQLCatalog import Query
TEST_SUITE_MAX = 4
# Depending on the test suite priority, we will affect
# more or less cores
......@@ -228,6 +229,34 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
return test_node
return None
def _getSortedNodeTestSuiteToRun(self, test_node):
"""
Returned ordered list of test suites of a test node. More the
latest test result is old, more it will have priority. Like this
we try to run first test suites that have no results since a long
time
"""
portal = self.getPortalObject()
test_suite_list = test_node.getAggregateValueList()
# Do not take results older than one month to avoid killing the
# sql server
now = DateTime()
from_date = now - 30
def getTestSuiteSortKey(test_suite):
test_result = portal.portal_catalog(portal_type="Test Result",
title='="%s"' % test_suite.getTitle(),
modification_date=Query(**{"creation_date": from_date,
"range": "min"}),
sort_on=[("modification_date", "descending")],
limit=1)
if len(test_result):
key = test_result[0].getObject().getModificationDate().timeTime()
else:
key = random.random()
return key
test_suite_list.sort(key=getTestSuiteSortKey)
return test_suite_list
security.declarePublic("startTestSuite")
def startTestSuite(self,title, batch_mode=0):
"""
......@@ -252,18 +281,7 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
activate_kw={'tag': tag})
self.activate(after_tag=tag).optimizeConfiguration()
test_node.setPingDate()
test_suite_list = test_node.getAggregateList()
# We sort the list according to timestamp
choice_list = []
if len(test_suite_list):
choice_list = [x.getObject() for x in test_suite_module.searchFolder(
relative_url=test_suite_list,
sort_on=[('indexation_timestamp','ascending')],
)]
# XXX we should have first test suite with no test node working on
# them since a long time. However we do not have this information yet,
# so random sort is better for now.
choice_list.sort(key=lambda x: random.random())
choice_list = self._getSortedNodeTestSuiteToRun(test_node)
for test_suite in choice_list:
config = {}
config["project_title"] = test_suite.getSourceProjectTitle()
......
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