[autotest] Pipe suite_name through client devserver code.
To reduce the overhead involved with fetching control files
from the devserver we generate a mapping of suite->control
files at build time. This cl will ask the control file getter
to fetch the control files for a given suite instead of all
the control files associated with an image.
Also removing SUITE = None from some networking test control
files, and patched unittests.
BUG=chromium:252398
TEST=Ran a suite with many control files. Made sure the suite->
control file mapping falls back to the old code when a bad suite
name is specified. Compared the test objects created in the old
version vs those created with this change.
Ran unittests.
CQ-DEPEND=CL:I6b590c8c40a863e6744875a26ac228ffd4dd8794
Change-Id: I4274c78764ffaaabef1ed48cdb40f523e307938d
Reviewed-on: https://gerrit.chromium.org/gerrit/63182
Tested-by: Prashanth Balasubramanian <beeps@chromium.org>
Reviewed-by: Dan Shi <dshi@chromium.org>
Commit-Queue: Prashanth Balasubramanian <beeps@chromium.org>
diff --git a/client/common_lib/cros/dev_server.py b/client/common_lib/cros/dev_server.py
index 7608539..806c6c9 100644
--- a/client/common_lib/cros/dev_server.py
+++ b/client/common_lib/cros/dev_server.py
@@ -478,16 +478,19 @@
@remote_devserver_call()
- def list_control_files(self, build):
+ def list_control_files(self, build, suite_name=''):
"""Ask the devserver to list all control files for |build|.
@param build: The build (e.g. x86-mario-release/R18-1586.0.0-a1-b1514)
whose control files the caller wants listed.
+ @param suite_name: The name of the suite for which we require control
+ files.
@return None on failure, or a list of control file paths
(e.g. server/site_tests/autoupdate/control)
@raise DevServerException upon any return code that's not HTTP OK.
"""
- call = self.build_call('controlfiles', build=build)
+ call = self.build_call('controlfiles', build=build,
+ suite_name=suite_name)
response = urllib2.urlopen(call)
return [line.rstrip() for line in response]
diff --git a/client/site_tests/network_NavigateToUrl/control b/client/site_tests/network_NavigateToUrl/control
index 69abbc3..36548b1 100644
--- a/client/site_tests/network_NavigateToUrl/control
+++ b/client/site_tests/network_NavigateToUrl/control
@@ -8,7 +8,6 @@
CRITERIA = """
This test will fail if it does not successfully navigate to the indicated url.
"""
-SUITE = "None"
TIME = "SHORT"
TEST_CATEGORY = "Functional"
TEST_CLASS = "network"
diff --git a/client/site_tests/network_WiFiDownloads/control b/client/site_tests/network_WiFiDownloads/control
index da6570b..8bb67d2 100644
--- a/client/site_tests/network_WiFiDownloads/control
+++ b/client/site_tests/network_WiFiDownloads/control
@@ -9,7 +9,6 @@
This test will fail if it cannot connect to a wireless network and
download files.
"""
-SUITE = "None"
TIME = "SHORT"
TEST_CATEGORY = "Functional"
TEST_CLASS = "network"
diff --git a/client/site_tests/network_WiFiSimpleConnection/control b/client/site_tests/network_WiFiSimpleConnection/control
index a50cd82..28ec6ed 100644
--- a/client/site_tests/network_WiFiSimpleConnection/control
+++ b/client/site_tests/network_WiFiSimpleConnection/control
@@ -9,7 +9,6 @@
This test will fail if it cannot connect to a wireless network and navigate
to www.msn.com.
"""
-SUITE = "None"
TIME = "SHORT"
TEST_CATEGORY = "Functional"
TEST_CLASS = "network"
diff --git a/server/cros/dynamic_suite/control_file_getter.py b/server/cros/dynamic_suite/control_file_getter.py
index 89e1108..7309838 100644
--- a/server/cros/dynamic_suite/control_file_getter.py
+++ b/server/cros/dynamic_suite/control_file_getter.py
@@ -3,7 +3,7 @@
# found in the LICENSE file.
import common
-import os, re
+import logging, os, re
from autotest_lib.client.common_lib import error, utils
from autotest_lib.client.common_lib.cros import dev_server
@@ -20,10 +20,11 @@
pass
- def get_control_file_list(self):
+ def get_control_file_list(self, suite_name=''):
"""
Gather a list of paths to control files.
+ @param suite_name: The name of a suite we would like control files for.
@return A list of file paths.
@throws NoControlFileList if there is an error while listing.
"""
@@ -63,17 +64,18 @@
self._files = []
- def get_control_file_list(self):
+ def get_control_file_list(self, suite_name=''):
"""
Gather a list of paths to control files.
Gets a list of control files; populates |self._files| with that list
and then returns the paths to all useful and wanted files in the list.
+ @param suite_name: The name of a suite we would like control files for.
@return A list of file paths.
@throws NoControlFileList if there is an error while listing.
"""
- files = self._get_control_file_list()
+ files = self._get_control_file_list(suite_name=suite_name)
for cf_filter in self.CONTROL_FILE_FILTERS:
files = filter(lambda path: not path.endswith(cf_filter), files)
self._files = files
@@ -128,7 +130,7 @@
return '__init__.py' not in name and '.svn' not in name
- def _get_control_file_list(self):
+ def _get_control_file_list(self, suite_name=''):
"""
Gather a list of paths to control files under |self._paths|.
@@ -136,9 +138,16 @@
|self._CONTROL_PATTERN|. Populates |self._files| with that list
and then returns the paths to all useful files in the list.
+ @param suite_name: The name of a suite we would like control files for.
@return A list of files that match |self._CONTROL_PATTERN|.
@throws NoControlFileList if we find no files.
"""
+ if suite_name:
+ logging.warning('Getting control files for a specific suite has '
+ 'not been implemented for FileSystemGetter. '
+ 'Getting all control files instead.')
+
+
regexp = re.compile(self._CONTROL_PATTERN)
directories = self._paths
while len(directories) > 0:
@@ -201,19 +210,23 @@
return DevServerGetter(build, ds)
- def _get_control_file_list(self):
+ def _get_control_file_list(self, suite_name=''):
"""
Gather a list of paths to control files from |self._dev_server|.
Get a listing of all the control files for |self._build| on
|self._dev_server|. Populates |self._files| with that list
- and then returns paths (under the autotest dir) to them.
+ and then returns paths (under the autotest dir) to them. If suite_name
+ is specified, this method populates |self._files| with the control
+ files from just the specified suite.
+ @param suite_name: The name of a suite we would like control files for.
@return A list of control file paths.
@throws NoControlFileList if there is an error while listing.
"""
try:
- return self._dev_server.list_control_files(self._build)
+ return self._dev_server.list_control_files(self._build,
+ suite_name=suite_name)
except dev_server.DevServerException as e:
raise error.NoControlFileList(e)
diff --git a/server/cros/dynamic_suite/control_file_getter_unittest.py b/server/cros/dynamic_suite/control_file_getter_unittest.py
index cc0684e..ad1049d 100644
--- a/server/cros/dynamic_suite/control_file_getter_unittest.py
+++ b/server/cros/dynamic_suite/control_file_getter_unittest.py
@@ -37,7 +37,9 @@
def testListControlFiles(self):
"""Should successfully list control files from the dev server."""
- self.dev_server.list_control_files(self._BUILD).AndReturn(self._FILES)
+ self.dev_server.list_control_files(
+ self._BUILD,
+ suite_name='').AndReturn(self._FILES)
self.mox.ReplayAll()
self.assertEquals(self.getter.get_control_file_list(), self._FILES)
self.assertEquals(self.getter._files, self._FILES)
@@ -45,7 +47,9 @@
def testListControlFilesFail(self):
"""Should fail to list control files from the dev server."""
- self.dev_server.list_control_files(self._BUILD).AndRaise(self._403)
+ self.dev_server.list_control_files(
+ self._BUILD,
+ suite_name='').AndRaise(self._403)
self.mox.ReplayAll()
self.assertRaises(error.NoControlFileList,
self.getter.get_control_file_list)
@@ -94,7 +98,9 @@
path = "file/%s/control" % name
files = self._FILES + [path]
- self.dev_server.list_control_files(self._BUILD).AndReturn(files)
+ self.dev_server.list_control_files(
+ self._BUILD,
+ suite_name='').AndReturn(files)
self.dev_server.get_control_file(self._BUILD,
path).AndReturn(self._CONTENTS)
self.mox.ReplayAll()
@@ -110,7 +116,9 @@
path = "file/" + name
files = self._FILES + [path]
- self.dev_server.list_control_files(self._BUILD).AndReturn(files)
+ self.dev_server.list_control_files(
+ self._BUILD,
+ suite_name='').AndReturn(files)
self.dev_server.get_control_file(self._BUILD,
path).AndReturn(self._CONTENTS)
self.mox.ReplayAll()
@@ -122,7 +130,9 @@
"""Should fail to get a control file from the dev server by name."""
name = 'one'
- self.dev_server.list_control_files(self._BUILD).AndReturn(self._FILES)
+ self.dev_server.list_control_files(
+ self._BUILD,
+ suite_name='').AndReturn(self._FILES)
self.mox.ReplayAll()
self.assertRaises(error.ControlFileNotFound,
self.getter.get_control_file_contents_by_name,
diff --git a/server/cros/dynamic_suite/suite.py b/server/cros/dynamic_suite/suite.py
index d5b288f..d2ca1fe 100644
--- a/server/cros/dynamic_suite/suite.py
+++ b/server/cros/dynamic_suite/suite.py
@@ -277,6 +277,7 @@
self._jobs = []
self._tests = Suite.find_and_parse_tests(self._cf_getter,
self._predicate,
+ self._tag,
add_experimental=True)
self._max_runtime_mins = max_runtime_mins
self._version_prefix = version_prefix
@@ -530,18 +531,28 @@
@staticmethod
- def find_and_parse_tests(cf_getter, predicate, add_experimental=False):
+ def find_and_parse_tests(cf_getter, predicate, suite_name='',
+ add_experimental=False):
"""
Function to scan through all tests and find eligible tests.
Looks at control files returned by _cf_getter.get_control_file_list()
- for tests that pass self._predicate().
+ for tests that pass self._predicate(). When this method is called
+ with a file system ControlFileGetter, it performs a full parse of the
+ root directory associated with the getter. This is the case when it's
+ invoked from suite_preprocessor. When it's invoked with a devserver
+ getter it looks up the suite_name in a suite to control file map
+ generated at build time, and parses the relevant control files alone.
+ This lookup happens on the devserver, so as far as this method is
+ concerned, both cases are equivalent.
@param cf_getter: a control_file_getter.ControlFileGetter used to list
and fetch the content of control files
@param predicate: a function that should return True when run over a
ControlData representation of a control file that should be in
this Suite.
+ @param suite_name: If specified, this method will attempt to restrain
+ the search space to just this suite's control files.
@param add_experimental: add tests with experimental attribute set.
@return list of ControlData objects that should be run, with control
@@ -549,7 +560,8 @@
on the TIME setting in control file, slowest test comes first.
"""
tests = {}
- files = cf_getter.get_control_file_list()
+ files = cf_getter.get_control_file_list(suite_name=suite_name)
+
matcher = re.compile(r'[^/]+/(deps|profilers)/.+')
parsed_count = 0
for file in filter(lambda f: not matcher.match(f), files):
diff --git a/server/cros/dynamic_suite/suite_unittest.py b/server/cros/dynamic_suite/suite_unittest.py
index 5714d9d..6f0d223 100644
--- a/server/cros/dynamic_suite/suite_unittest.py
+++ b/server/cros/dynamic_suite/suite_unittest.py
@@ -73,15 +73,19 @@
shutil.rmtree(self.tmpdir, ignore_errors=True)
- def expect_control_file_parsing(self):
- """Expect an attempt to parse the 'control files' in |self.files|."""
+ def expect_control_file_parsing(self, suite_name=_TAG):
+ """Expect an attempt to parse the 'control files' in |self.files|.
+
+ @param suite_name: The suite name to parse control files for.
+ """
all_files = self.files.keys() + self.files_to_filter.keys()
self._set_control_file_parsing_expectations(False, all_files,
- self.files)
+ self.files, suite_name)
def _set_control_file_parsing_expectations(self, already_stubbed,
- file_list, files_to_parse):
+ file_list, files_to_parse,
+ suite_name):
"""Expect an attempt to parse the 'control files' in |files|.
@param already_stubbed: parse_control_string already stubbed out.
@@ -92,7 +96,8 @@
if not already_stubbed:
self.mox.StubOutWithMock(control_data, 'parse_control_string')
- self.getter.get_control_file_list().AndReturn(file_list)
+ self.getter.get_control_file_list(
+ suite_name=suite_name).AndReturn(file_list)
for file, data in files_to_parse.iteritems():
self.getter.get_control_file_contents(
file).InAnyOrder().AndReturn(data.string)
@@ -106,7 +111,7 @@
self.mox.ReplayAll()
predicate = lambda d: d.text == self.files['two'].string
- tests = Suite.find_and_parse_tests(self.getter, predicate)
+ tests = Suite.find_and_parse_tests(self.getter, predicate, self._TAG)
self.assertEquals(len(tests), 1)
self.assertEquals(tests[0], self.files['two'])
@@ -119,6 +124,7 @@
predicate = lambda d: d.suite == self._TAG
tests = Suite.find_and_parse_tests(self.getter,
predicate,
+ self._TAG,
add_experimental=True)
self.assertEquals(len(tests), 5)
self.assertTrue(self.files['one'] in tests)
@@ -131,7 +137,7 @@
def testAdHocSuiteCreation(self):
"""Should be able to schedule an ad-hoc suite by specifying
a single test name."""
- self.expect_control_file_parsing()
+ self.expect_control_file_parsing(suite_name='ad_hoc_suite')
self.mox.ReplayAll()
predicate = Suite.test_name_equals_predicate('name-data_five')
suite = Suite.create_from_predicates([predicate], self._BUILD,
@@ -193,6 +199,7 @@
Suite.find_and_parse_tests(
mox.IgnoreArg(),
mox.IgnoreArg(),
+ mox.IgnoreArg(),
add_experimental=True).AndReturn(self.files.values())
@@ -390,6 +397,7 @@
# Get all tests.
tests = Suite.find_and_parse_tests(self.getter,
lambda d: True,
+ self._TAG,
add_experimental=True)
self.assertEquals(len(tests), 6)
times = [control_data.ControlData.get_test_time_index(test.time)
diff --git a/site_utils/suite_preprocessor.py b/site_utils/suite_preprocessor.py
index c5c0cdd..d2a2131 100755
--- a/site_utils/suite_preprocessor.py
+++ b/site_utils/suite_preprocessor.py
@@ -51,7 +51,8 @@
"""
fs_getter = Suite.create_fs_getter(autotest_dir)
predicate = lambda t: hasattr(t, 'suite')
- return Suite.find_and_parse_tests(fs_getter, predicate, True)
+ return Suite.find_and_parse_tests(fs_getter, predicate,
+ add_experimental=True)
def calculate_dependencies(autotest_dir):