Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(207)

Unified Diff: third_party/recipe_engine/recipe_test_api.py

Issue 1241323004: Cross-repo recipe package system. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Roll to latest recipes-py Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/recipe_engine/recipe_api.py ('k') | third_party/recipe_engine/simulation_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/recipe_engine/recipe_test_api.py
diff --git a/third_party/recipe_engine/recipe_test_api.py b/third_party/recipe_engine/recipe_test_api.py
deleted file mode 100644
index 2ea3791725a276ddffca3035baf47a652db7bc92..0000000000000000000000000000000000000000
--- a/third_party/recipe_engine/recipe_test_api.py
+++ /dev/null
@@ -1,462 +0,0 @@
-# Copyright 2013-2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import collections
-
-from .util import ModuleInjectionSite, static_call, static_wraps
-
-def combineify(name, dest, a, b):
- """
- Combines dictionary members in two objects into a third one using addition.
-
- Args:
- name - the name of the member
- dest - the destination object
- a - the first source object
- b - the second source object
- """
- dest_dict = getattr(dest, name)
- dest_dict.update(getattr(a, name))
- for k, v in getattr(b, name).iteritems():
- if k in dest_dict:
- dest_dict[k] += v
- else:
- dest_dict[k] = v
-
-
-class BaseTestData(object):
- def __init__(self, enabled=True):
- super(BaseTestData, self).__init__()
- self._enabled = enabled
-
- @property
- def enabled(self):
- return self._enabled
-
-
-class PlaceholderTestData(BaseTestData):
- def __init__(self, data=None):
- super(PlaceholderTestData, self).__init__()
- self.data = data
-
- def __repr__(self):
- return "PlaceholderTestData(%r)" % (self.data,)
-
-
-class StepTestData(BaseTestData):
- """
- Mutable container for per-step test data.
-
- This data is consumed while running the recipe (during
- annotated_run.run_steps).
- """
- def __init__(self):
- super(StepTestData, self).__init__()
- # { (module, placeholder) -> [data] }
- self.placeholder_data = collections.defaultdict(list)
- self.override = False
- self._stdout = None
- self._stderr = None
- self._retcode = None
-
- def __add__(self, other):
- assert isinstance(other, StepTestData)
-
- if other.override:
- return other
-
- ret = StepTestData()
-
- combineify('placeholder_data', ret, self, other)
-
- # pylint: disable=W0212
- ret._stdout = other._stdout or self._stdout
- ret._stderr = other._stderr or self._stderr
- ret._retcode = self._retcode
- if other._retcode is not None:
- assert ret._retcode is None
- ret._retcode = other._retcode
-
- return ret
-
- def unwrap_placeholder(self):
- # {(module, placeholder): [data]} => data.
- assert len(self.placeholder_data) == 1
- data_list = self.placeholder_data.items()[0][1]
- assert len(data_list) == 1
- return data_list[0]
-
- def pop_placeholder(self, name_pieces):
- l = self.placeholder_data[name_pieces]
- if l:
- return l.pop(0)
- else:
- return PlaceholderTestData()
-
- @property
- def retcode(self): # pylint: disable=E0202
- return self._retcode or 0
-
- @retcode.setter
- def retcode(self, value): # pylint: disable=E0202
- self._retcode = value
-
- @property
- def stdout(self):
- return self._stdout or PlaceholderTestData(None)
-
- @stdout.setter
- def stdout(self, value):
- assert isinstance(value, PlaceholderTestData)
- self._stdout = value
-
- @property
- def stderr(self):
- return self._stderr or PlaceholderTestData(None)
-
- @stderr.setter
- def stderr(self, value):
- assert isinstance(value, PlaceholderTestData)
- self._stderr = value
-
- @property
- def stdin(self): # pylint: disable=R0201
- return PlaceholderTestData(None)
-
- def __repr__(self):
- return "StepTestData(%r)" % ({
- 'placeholder_data': dict(self.placeholder_data.iteritems()),
- 'stdout': self._stdout,
- 'stderr': self._stderr,
- 'retcode': self._retcode,
- 'override': self.override,
- },)
-
-
-class ModuleTestData(BaseTestData, dict):
- """
- Mutable container for test data for a specific module.
-
- This test data is consumed at module load time (i.e. when create_recipe_api
- runs).
- """
- def __add__(self, other):
- assert isinstance(other, ModuleTestData)
- ret = ModuleTestData()
- ret.update(self)
- ret.update(other)
- return ret
-
- def __repr__(self):
- return "ModuleTestData(%r)" % super(ModuleTestData, self).__repr__()
-
-
-class TestData(BaseTestData):
- def __init__(self, name=None):
- super(TestData, self).__init__()
- self.name = name
- self.properties = {} # key -> val
- self.mod_data = collections.defaultdict(ModuleTestData)
- self.step_data = collections.defaultdict(StepTestData)
- self.expected_exception = None
-
- def __add__(self, other):
- assert isinstance(other, TestData)
- ret = TestData(self.name or other.name)
-
- ret.properties.update(self.properties)
- ret.properties.update(other.properties)
-
- combineify('mod_data', ret, self, other)
- combineify('step_data', ret, self, other)
- ret.expected_exception = self.expected_exception
- if other.expected_exception:
- ret.expected_exception = other.expected_exception
-
- return ret
-
- def empty(self):
- return not self.step_data
-
- def pop_step_test_data(self, step_name, step_test_data_fn):
- step_test_data = step_test_data_fn()
- if step_name in self.step_data:
- step_test_data += self.step_data.pop(step_name)
- return step_test_data
-
- def get_module_test_data(self, module_name):
- return self.mod_data.get(module_name, ModuleTestData())
-
- def expect_exception(self, exception):
- assert not self.expected_exception
- self.expected_exception = exception
-
- def is_unexpected_exception(self, exception):
- name = exception.__class__.__name__
- return not (self.enabled and name == self.expected_exception)
-
- def __repr__(self):
- return "TestData(%r)" % ({
- 'name': self.name,
- 'properties': self.properties,
- 'mod_data': dict(self.mod_data.iteritems()),
- 'step_data': dict(self.step_data.iteritems()),
- 'expected_exception': self.expected_exception,
- },)
-
-
-class DisabledTestData(BaseTestData):
- def __init__(self):
- super(DisabledTestData, self).__init__(False)
-
- def __getattr__(self, name):
- return self
-
- def pop_placeholder(self, _name_pieces):
- return self
-
- def pop_step_test_data(self, _step_name, _step_test_data_fn):
- return self
-
- def get_module_test_data(self, _module_name):
- return self
-
- def is_unexpected_exception(self, exception): #pylint: disable=R0201
- return False
-
-def mod_test_data(func):
- @static_wraps(func)
- def inner(self, *args, **kwargs):
- assert isinstance(self, RecipeTestApi)
- mod_name = self._module.NAME # pylint: disable=W0212
- ret = TestData(None)
- data = static_call(self, func, *args, **kwargs)
- ret.mod_data[mod_name][inner.__name__] = data
- return ret
- return inner
-
-
-def placeholder_step_data(func):
- """Decorates RecipeTestApi member functions to allow those functions to
- return just the placeholder data, instead of the normally required
- StepTestData() object.
-
- The wrapped function may return either:
- * <placeholder data>, <retcode or None>
- * StepTestData containing exactly one PlaceholderTestData and possible a
- retcode. This is useful for returning the result of another method which
- is wrapped with placeholder_step_data.
-
- In either case, the wrapper function will return a StepTestData object with
- the retcode and placeholder datum inserted with a name of:
- (<Test module name>, <wrapped function name>)
-
- Say you had a 'foo_module' with the following RecipeTestApi:
- class FooTestApi(RecipeTestApi):
- @placeholder_step_data
- @staticmethod
- def cool_method(data, retcode=None):
- return ("Test data (%s)" % data), retcode
-
- @placeholder_step_data
- def other_method(self, retcode=None):
- return self.cool_method('hammer time', retcode)
-
- Code calling cool_method('hello') would get a StepTestData:
- StepTestData(
- placeholder_data = {
- ('foo_module', 'cool_method'): [
- PlaceholderTestData('Test data (hello)')
- ]
- },
- retcode = None
- )
-
- Code calling other_method(50) would get a StepTestData:
- StepTestData(
- placeholder_data = {
- ('foo_module', 'other_method'): [
- PlaceholderTestData('Test data (hammer time)')
- ]
- },
- retcode = 50
- )
- """
- @static_wraps(func)
- def inner(self, *args, **kwargs):
- assert isinstance(self, RecipeTestApi)
- mod_name = self._module.NAME # pylint: disable=W0212
- data = static_call(self, func, *args, **kwargs)
- if isinstance(data, StepTestData):
- all_data = [i
- for l in data.placeholder_data.values()
- for i in l]
- assert len(all_data) == 1, (
- 'placeholder_step_data is only expecting a single placeholder datum. '
- 'Got: %r' % data
- )
- placeholder_data, retcode = all_data[0], data.retcode
- else:
- placeholder_data, retcode = data
- placeholder_data = PlaceholderTestData(placeholder_data)
-
- ret = StepTestData()
- ret.placeholder_data[(mod_name, inner.__name__)].append(placeholder_data)
- ret.retcode = retcode
- return ret
- return inner
-
-
-class RecipeTestApi(object):
- """Provides testing interface for GenTest method.
-
- There are two primary components to the test api:
- * Test data creation methods (test and step_data)
- * test_api's from all the modules in DEPS.
-
- Every test in GenTests(api) takes the form:
- yield <instance of TestData>
-
- There are 4 basic pieces to TestData:
- name - The name of the test.
- properties - Simple key-value dictionary which is used as the combined
- build_properties and factory_properties for this test.
- mod_data - Module-specific testing data (see the platform module for a
- good example). This is testing data which is only used once at
- the start of the execution of the recipe. Modules should
- provide methods to get their specific test information. See
- the platform module's test_api for a good example of this.
- step_data - Step-specific data. There are two major components to this.
- retcode - The return code of the step
- placeholder_data - A mapping from placeholder name to the a list of
- PlaceholderTestData objects, one for each instance
- of that kind of Placeholder in the step.
- stdout, stderr - PlaceholderTestData objects for stdout and stderr.
-
- TestData objects are concatenatable, so it's convenient to phrase test cases
- as a series of added TestData objects. For example:
- DEPS = ['properties', 'platform', 'json']
- def GenTests(api):
- yield (
- api.test('try_win64') +
- api.properties.tryserver(power_level=9001) +
- api.platform('win', 64) +
- api.step_data(
- 'some_step',
- api.json.output("bobface"),
- api.json.output({'key': 'value'})
- )
- )
-
- This example would run a single test (named 'try_win64') with the standard
- tryserver properties (plus an extra property 'power_level' whose value was
- over 9000). The test would run as if it were being run on a 64-bit windows
- installation, and the step named 'some_step' would have its first json output
- placeholder be mocked to return '"bobface"', and its second json output
- placeholder be mocked to return '{"key": "value"}'.
-
- The properties.tryserver() call is documented in the 'properties' module's
- test_api.
- The platform() call is documented in the 'platform' module's test_api.
- The json.output() call is documented in the 'json' module's test_api.
- """
- def __init__(self, module=None):
- """Note: Injected dependencies are NOT available in __init__()."""
- # If we're the 'root' api, inject directly into 'self'.
- # Otherwise inject into 'self.m'
- self.m = self if module is None else ModuleInjectionSite()
- self._module = module
-
- @staticmethod
- def test(name):
- """Returns a new empty TestData with the name filled in.
-
- Use in GenTests:
- def GenTests(api):
- yield api.test('basic')
- """
- return TestData(name)
-
- @staticmethod
- def empty_test_data():
- """Returns a TestData with no information.
-
- This is the identity of the + operator for combining TestData.
- """
- return TestData(None)
-
- @staticmethod
- def _step_data(name, *data, **kwargs):
- """Returns a new TestData with the mock data filled in for a single step.
-
- Used by step_data and override_step_data.
-
- Args:
- name - The name of the step we're providing data for
- data - Zero or more StepTestData objects. These may fill in placeholder
- data for zero or more modules, as well as possibly setting the
- retcode for this step.
- retcode=(int or None) - Override the retcode for this step, even if it
- was set by |data|. This must be set as a keyword arg.
- stdout - StepTestData object with placeholder data for a step's stdout.
- stderr - StepTestData object with placeholder data for a step's stderr.
- override=(bool) - This step data completely replaces any previously
- generated step data, instead of adding on to it.
-
- Use in GenTests:
- # Hypothetically, suppose that your recipe has default test data for two
- # steps 'init' and 'sync' (probably via recipe_api.inject_test_data()).
- # For this example, lets say that the default test data looks like:
- # api.step_data('init', api.json.output({'some': ["cool", "json"]}))
- # AND
- # api.step_data('sync', api.json.output({'src': {'rev': 100}}))
- # Then, your GenTests code may augment or replace this data like:
-
- def GenTests(api):
- yield (
- api.test('more') +
- api.step_data( # Adds step data for a step with no default test data
- 'mystep',
- api.json.output({'legend': ['...', 'DARY!']})
- ) +
- api.step_data( # Adds retcode to default step_data for this step
- 'init',
- retcode=1
- ) +
- api.override_step_data( # Removes json output and overrides retcode
- 'sync',
- retcode=100
- )
- )
- """
- assert all(isinstance(d, StepTestData) for d in data)
- ret = TestData(None)
- if data:
- ret.step_data[name] = reduce(sum, data)
- if 'retcode' in kwargs:
- ret.step_data[name].retcode = kwargs['retcode']
- if 'override' in kwargs:
- ret.step_data[name].override = kwargs['override']
- for key in ('stdout', 'stderr'):
- if key in kwargs:
- stdio_test_data = kwargs[key]
- assert isinstance(stdio_test_data, StepTestData)
- setattr(ret.step_data[name], key, stdio_test_data.unwrap_placeholder())
- return ret
-
- def step_data(self, name, *data, **kwargs):
- """See _step_data()"""
- return self._step_data(name, *data, **kwargs)
- step_data.__doc__ = _step_data.__doc__
-
- def override_step_data(self, name, *data, **kwargs):
- """See _step_data()"""
- kwargs['override'] = True
- return self._step_data(name, *data, **kwargs)
- override_step_data.__doc__ = _step_data.__doc__
-
- def expect_exception(self, exc_type): #pylint: disable=R0201
- ret = TestData(None)
- ret.expect_exception(exc_type)
- return ret
« no previous file with comments | « third_party/recipe_engine/recipe_api.py ('k') | third_party/recipe_engine/simulation_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698