[autotest] Suite class should pull control files from the dev server
When running a test suite, we should pull build-appropriate control files
from the dev server, instead of finding them in the local, ToT
checkout.
BUG=None
TEST=./server/autoserv test_suites/dev_harness
Change-Id: Icd9061afdb4d86bd84f59ac8842d130211d20afe
Reviewed-on: https://gerrit.chromium.org/gerrit/16371
Commit-Ready: Chris Masone <cmasone@chromium.org>
Reviewed-by: Chris Masone <cmasone@chromium.org>
Tested-by: Chris Masone <cmasone@chromium.org>
diff --git a/server/cros/dynamic_suite.py b/server/cros/dynamic_suite.py
index 6e9873e..d184e68 100644
--- a/server/cros/dynamic_suite.py
+++ b/server/cros/dynamic_suite.py
@@ -6,6 +6,7 @@
import compiler, logging, os, random, re, time
from autotest_lib.client.common_lib import control_data, global_config, error
from autotest_lib.client.common_lib import utils
+from autotest_lib.client.common_lib.cros import dev_server
from autotest_lib.server.cros import control_file_getter, frontend_wrappers
from autotest_lib.server import frontend
@@ -207,6 +208,7 @@
ControlData representation of a control file that should be in
this Suite.
@var _tag: a string with which to tag jobs run in this suite.
+ @var _build: the build on which we're running this suite.
@var _afe: an instance of AFE as defined in server/frontend.py.
@var _tko: an instance of TKO as defined in server/frontend.py.
@var _jobs: currently scheduled jobs, if any.
@@ -215,19 +217,17 @@
@staticmethod
- def create_fs_getter(autotest_dir):
+ def create_cf_getter(build):
"""
- @param autotest_dir: the place to find autotests.
+ @param build: the build on which we're running this suite.
@return a FileSystemGetter instance that looks under |autotest_dir|.
"""
- # currently hard-coded places to look for tests.
- subpaths = ['server/site_tests', 'client/site_tests']
- directories = [os.path.join(autotest_dir, p) for p in subpaths]
- return control_file_getter.FileSystemGetter(directories)
+ return control_file_getter.DevServerGetter(
+ build, dev_server.DevServer.create())
@staticmethod
- def create_from_name(name, autotest_dir, afe=None, tko=None, pool=None):
+ def create_from_name(name, build, afe=None, tko=None, pool=None):
"""
Create a Suite using a predicate based on the SUITE control file var.
@@ -236,7 +236,7 @@
|afe|. Results will be pulled from |tko| upon completion
@param name: a value of the SUITE control file variable to search for.
- @param autotest_dir: the place to find autotests.
+ @param build: the build on which we're running this suite.
@param afe: an instance of AFE as defined in server/frontend.py.
@param tko: an instance of TKO as defined in server/frontend.py.
@param pool: Specify the pool of machines to use for scheduling
@@ -244,10 +244,10 @@
@return a Suite instance.
"""
return Suite(lambda t: hasattr(t, 'suite') and t.suite == name,
- name, autotest_dir, afe, tko, pool)
+ name, build, afe, tko, pool)
- def __init__(self, predicate, tag, autotest_dir, afe=None, tko=None,
+ def __init__(self, predicate, tag, build, afe=None, tko=None,
pool=None):
"""
Constructor
@@ -256,7 +256,7 @@
ControlData representation of a control file that should be in
this Suite.
@param tag: a string with which to tag jobs run in this suite.
- @param autotest_dir: the place to find autotests.
+ @param build: the build on which we're running this suite.
@param afe: an instance of AFE as defined in server/frontend.py.
@param tko: an instance of TKO as defined in server/frontend.py.
@param pool: Specify the pool of machines to use for scheduling
@@ -264,6 +264,7 @@
"""
self._predicate = predicate
self._tag = tag
+ self._build = build
self._afe = afe or frontend_wrappers.RetryingAFE(timeout_min=30,
delay_sec=10,
debug=False)
@@ -273,7 +274,7 @@
self._pool = pool
self._jobs = []
- self._cf_getter = Suite.create_fs_getter(autotest_dir)
+ self._cf_getter = Suite.create_cf_getter(self._build)
self._tests = Suite.find_and_parse_tests(self._cf_getter,
self._predicate,
@@ -302,43 +303,41 @@
return filter(lambda t: t.experimental, self.tests)
- def _create_job(self, test, image_name):
+ def _create_job(self, test):
"""
Thin wrapper around frontend.AFE.create_job().
@param test: ControlData object for a test to run.
- @param image_name: the name of an image against which to test.
@return frontend.Job object for the job just scheduled.
"""
job_deps = []
if self._pool:
meta_hosts = 'pool:%s' % self._pool
- cros_label = VERSION_PREFIX+image_name
+ cros_label = VERSION_PREFIX + self._build
job_deps.append(cros_label)
else:
# No pool specified use any machines with the following label.
- meta_hosts = VERSION_PREFIX+image_name
+ meta_hosts = VERSION_PREFIX + self._build
return self._afe.create_job(
control_file=test.text,
- name='/'.join([image_name, self._tag, test.name]),
+ name='/'.join([self._build, self._tag, test.name]),
control_type=test.test_type.capitalize(),
meta_hosts=[meta_hosts],
dependencies=job_deps)
- def run_and_wait(self, image_name, record, add_experimental=True):
+ def run_and_wait(self, record, add_experimental=True):
"""
Synchronously run tests in |self.tests|.
- Schedules tests against a device running image |image_name|, and
+ Schedules tests against a device running image |self._build|, and
then polls for status, using |record| to print status when each
completes.
Tests returned by self.stable_tests() will always be run, while tests
in self.unstable_tests() will only be run if |add_experimental| is true.
- @param image_name: the name of an image against which to test.
@param record: callable that records job status.
prototype:
record(status, subdir, name, reason)
@@ -346,7 +345,7 @@
"""
try:
record('INFO', None, 'Start %s' % self._tag)
- self.schedule(image_name, add_experimental)
+ self.schedule(add_experimental)
try:
for result in self.wait_for_results():
# |result| will be a tuple of a maximum of 4 entries and a
@@ -368,25 +367,24 @@
'Exception while scheduling suite')
- def schedule(self, image_name, add_experimental=True):
+ def schedule(self, add_experimental=True):
"""
Schedule jobs using |self._afe|.
frontend.Job objects representing each scheduled job will be put in
|self._jobs|.
- @param image_name: the name of an image against which to test.
@param add_experimental: schedule experimental tests as well, or not.
"""
for test in self.stable_tests():
logging.debug('Scheduling %s', test.name)
- self._jobs.append(self._create_job(test, image_name))
+ self._jobs.append(self._create_job(test))
if add_experimental:
# TODO(cmasone): ensure I can log results from these differently.
for test in self.unstable_tests():
logging.debug('Scheduling %s', test.name)
- self._jobs.append(self._create_job(test, image_name))
+ self._jobs.append(self._create_job(test))
def _status_is_relevant(self, status):
diff --git a/server/cros/dynamic_suite_unittest.py b/server/cros/dynamic_suite_unittest.py
index 2f8e88b..bf8f0f5 100755
--- a/server/cros/dynamic_suite_unittest.py
+++ b/server/cros/dynamic_suite_unittest.py
@@ -29,13 +29,13 @@
"""Unit tests for dynamic_suite.Reimager.
@var _URL: fake image url
- @var _NAME: fake image name
+ @var _BUILD: fake build
@var _NUM: fake number of machines to run on
@var _BOARD: fake board to reimage
"""
_URL = 'http://nothing/%s'
- _NAME = 'name'
+ _BUILD = 'build'
_NUM = 4
_BOARD = 'board'
_CONFIG = global_config.global_config
@@ -119,8 +119,8 @@
# The semantics of |results_platform_map| and |test_results| are
# drawn from frontend.AFE.poll_all_jobs()
job.results_platform_map = {'netbook': {'Aborted' : [H3],
- 'Completed' : [H1, H4, H5],
- 'Failed': [H2]
+ 'Completed' : [H1, H4, H5],
+ 'Failed': [H2]
}
}
# Gin up fake results for H2 and H3 failure cases.
@@ -157,13 +157,14 @@
'image_url_pattern',
self._URL)
self.afe.create_job(
- control_file=mox.And(mox.StrContains(self._NAME),
- mox.StrContains(self._URL % self._NAME)),
- name=mox.StrContains(self._NAME),
+ control_file=mox.And(mox.StrContains(self._BUILD),
+ mox.StrContains(self._URL % self._BUILD)),
+ name=mox.StrContains(self._BUILD),
control_type='Server',
- meta_hosts=[self._BOARD] * self._NUM)
+ meta_hosts=['board:'+self._BOARD] * self._NUM,
+ dependencies=[])
self.mox.ReplayAll()
- self.reimager._schedule_reimage_job(self._NAME, self._NUM, self._BOARD)
+ self.reimager._schedule_reimage_job(self._BUILD, self._NUM, self._BOARD)
def expect_attempt(self, success):
@@ -174,10 +175,10 @@
"""
canary = FakeJob()
self.mox.StubOutWithMock(self.reimager, '_ensure_version_label')
- self.reimager._ensure_version_label(mox.StrContains(self._NAME))
+ self.reimager._ensure_version_label(mox.StrContains(self._BUILD))
self.mox.StubOutWithMock(self.reimager, '_schedule_reimage_job')
- self.reimager._schedule_reimage_job(self._NAME,
+ self.reimager._schedule_reimage_job(self._BUILD,
self._NUM,
self._BOARD).AndReturn(canary)
if success is not None:
@@ -199,7 +200,7 @@
rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
rjob.record('END GOOD', mox.IgnoreArg(), mox.IgnoreArg())
self.mox.ReplayAll()
- self.reimager.attempt(self._NAME, self._BOARD, rjob.record)
+ self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
def testFailedReimage(self):
@@ -210,7 +211,7 @@
rjob.record('START', mox.IgnoreArg(), mox.IgnoreArg())
rjob.record('END FAIL', mox.IgnoreArg(), mox.IgnoreArg())
self.mox.ReplayAll()
- self.reimager.attempt(self._NAME, self._BOARD, rjob.record)
+ self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
def testReimageThatNeverHappened(self):
@@ -222,18 +223,18 @@
rjob.record('FAIL', mox.IgnoreArg(), canary.name, mox.IgnoreArg())
rjob.record('END FAIL', mox.IgnoreArg(), mox.IgnoreArg())
self.mox.ReplayAll()
- self.reimager.attempt(self._NAME, self._BOARD, rjob.record)
+ self.reimager.attempt(self._BUILD, self._BOARD, rjob.record)
class SuiteTest(mox.MoxTestBase):
"""Unit tests for dynamic_suite.Suite.
- @var _NAME: fake image name
+ @var _BUILD: fake build
@var _TAG: fake suite tag
"""
- _NAME = 'name'
- _TAG = 'suite tag'
+ _BUILD = 'build'
+ _TAG = 'suite_tag'
def setUp(self):
@@ -337,10 +338,11 @@
continue
self.afe.create_job(
control_file=test.text,
- name=mox.And(mox.StrContains(self._NAME),
+ name=mox.And(mox.StrContains(self._BUILD),
mox.StrContains(test.name)),
control_type=mox.IgnoreArg(),
- meta_hosts=[dynamic_suite.VERSION_PREFIX+self._NAME])
+ meta_hosts=[dynamic_suite.VERSION_PREFIX + self._BUILD],
+ dependencies=[])
def testScheduleTests(self):
@@ -349,9 +351,9 @@
self.expect_job_scheduling(add_experimental=True)
self.mox.ReplayAll()
- suite = dynamic_suite.Suite.create_from_name(self._TAG, self.tmpdir,
+ suite = dynamic_suite.Suite.create_from_name(self._TAG, self._BUILD,
self.afe, self.tko)
- suite.schedule(self._NAME)
+ suite.schedule()
def testScheduleStableTests(self):
@@ -360,9 +362,9 @@
self.expect_job_scheduling(add_experimental=False)
self.mox.ReplayAll()
- suite = dynamic_suite.Suite.create_from_name(self._TAG, self.tmpdir,
+ suite = dynamic_suite.Suite.create_from_name(self._TAG, self._BUILD,
self.afe, self.tko)
- suite.schedule(self._NAME, add_experimental=False)
+ suite.schedule(add_experimental=False)
def expect_result_gathering(self, job):
@@ -379,8 +381,8 @@
@return Suite object, after mocking out behavior needed to create it.
"""
- self.mox.StubOutWithMock(dynamic_suite.Suite, 'create_fs_getter')
- dynamic_suite.Suite.create_fs_getter(self.tmpdir).AndReturn(self.getter)
+ self.mox.StubOutWithMock(dynamic_suite.Suite, 'create_cf_getter')
+ dynamic_suite.Suite.create_cf_getter(self.tmpdir).AndReturn(self.getter)
self.expect_control_file_parsing()
self.mox.ReplayAll()
suite = dynamic_suite.Suite.create_from_name(self._TAG, self.tmpdir,
@@ -463,12 +465,12 @@
recorder.record('END %s' % status, None, test_name)
self.mox.StubOutWithMock(suite, 'schedule')
- suite.schedule(self._NAME, True)
+ suite.schedule(True)
self.mox.StubOutWithMock(suite, 'wait_for_results')
suite.wait_for_results().AndReturn(results)
self.mox.ReplayAll()
- suite.run_and_wait(self._NAME, recorder.record, True)
+ suite.run_and_wait(recorder.record, True)
def testRunAndWaitFailure(self):
@@ -481,12 +483,12 @@
mox.StrContains('waiting'))
self.mox.StubOutWithMock(suite, 'schedule')
- suite.schedule(self._NAME, True)
+ suite.schedule(True)
self.mox.StubOutWithMock(suite, 'wait_for_results')
suite.wait_for_results().AndRaise(Exception())
self.mox.ReplayAll()
- suite.run_and_wait(self._NAME, recorder.record, True)
+ suite.run_and_wait(recorder.record, True)
def testRunAndWaitScheduleFailure(self):
@@ -499,10 +501,10 @@
mox.StrContains('scheduling'))
self.mox.StubOutWithMock(suite, 'schedule')
- suite.schedule(self._NAME, True).AndRaise(Exception())
+ suite.schedule(True).AndRaise(Exception())
self.mox.ReplayAll()
- suite.run_and_wait(self._NAME, recorder.record, True)
+ suite.run_and_wait(recorder.record, True)
if __name__ == '__main__':