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

Unified Diff: expect_tests/type_definitions.py

Issue 554213004: Refactored types to simplify pickling. (Closed) Base URL: https://chromium.googlesource.com/infra/testing/expect_tests@shebang
Patch Set: Created 6 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 | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: expect_tests/type_definitions.py
diff --git a/expect_tests/type_definitions.py b/expect_tests/type_definitions.py
index cd5c89ca2ae2ef08be506cfe86ce197777a221a6..9a3f99fd201184ce83b68709a2730dea1bbc6c10 100644
--- a/expect_tests/type_definitions.py
+++ b/expect_tests/type_definitions.py
@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import copy
import inspect
import os
import re
@@ -148,17 +149,82 @@ class FuncCall(object):
return 'FuncCall(%r, *%r, **%r)' % (self.func, self.args, self.kwargs)
-_Test = namedtuple(
- 'Test', 'name func_call expect_dir expect_base ext covers breakpoints')
+class TestInfo(object):
+ TEST_COVERS_MATCH = re.compile('.*/test/([^/]*)_test\.py$')
dnj 2014/09/16 23:13:26 I know this is kind of outside of the immediate sc
-TestInfo = namedtuple(
- 'TestInfo', 'name expect_dir expect_base ext')
+ def __init__(self, name, expect_dir=None, expect_base=None,
+ ext='json', covers=None, breakpoints=None):
+ self._name = name
+ self._expect_dir = expect_dir
+ self._expect_base = expect_base
+ self._ext = ext
+ self._covers = covers
+ self._breakpoints = breakpoints
+ @property
+ def name(self):
+ return self._name
-class Test(_Test):
- TEST_COVERS_MATCH = re.compile('.*/test/([^/]*)_test\.py$')
+ @property
+ def expect_dir(self):
+ return self._expect_dir
+
+ @property
+ def expect_base(self):
+ return self._expect_base
+
+ @property
+ def ext(self):
+ return self._ext
+
+ @property
+ def covers(self):
+ return self._covers
+
+ @property
+ def breakpoints(self):
+ return self._breakpoints
+
+ @staticmethod
+ def covers_obj(obj):
+ test_file = inspect.getabsfile(obj)
+ covers = [test_file]
+ match = Test.TEST_COVERS_MATCH.match(test_file)
dnj 2014/09/16 23:13:26 Make this a "classmethod" and use "cls.TEST_COVERS
+ if match:
+ covers.append(os.path.join(
+ os.path.dirname(os.path.dirname(test_file)),
+ match.group(1) + '.py'
+ ))
+ return covers
+
+ @staticmethod
+ def expect_dir_obj(obj):
+ test_file = inspect.getabsfile(obj)
+ return os.path.splitext(test_file)[0] + '.expected'
+
+ def coverage_includes(self):
+ if self.covers is not None:
+ return self.covers
+ return self.covers_obj(self.func_call.func)
- def __new__(cls, name, func_call, expect_dir=None, expect_base=None,
+ def expect_path(self, ext=None):
+ expect_dir = self.expect_dir
+ if expect_dir is None:
+ expect_dir = self.expect_dir_obj(self.func_call.func)
+ name = self.expect_base or self.name
+ name = ''.join('_' if c in '<>:"\\/|?*\0' else c for c in name)
+ return os.path.join(expect_dir, name + ('.%s' % (ext or self.ext)))
+
+ def restrict(self, tests):
+ assert tests[0] is self
+ return self
+
+ def get_info(self):
+ return self
+
+
+class Test(TestInfo):
+ def __init__(self, name, func_call, expect_dir=None, expect_base=None,
ext='json', covers=None, breakpoints=None, break_funcs=()):
"""Create a new test.
@@ -184,7 +250,10 @@ class Test(_Test):
@param break_funcs: A list of functions for which to set breakpoints.
"""
breakpoints = breakpoints or []
+ self._func_call = func_call
+
if not breakpoints or break_funcs:
+ breakpoints = copy.copy(breakpoints)
for f in (break_funcs or (func_call.func,)):
if hasattr(f, 'im_func'):
f = f.im_func
@@ -194,76 +263,89 @@ class Test(_Test):
if expect_dir:
expect_dir = expect_dir.rstrip('/')
- return super(Test, cls).__new__(cls, name, func_call, expect_dir,
- expect_base, ext, covers, breakpoints)
+ super(Test, self).__init__(name, expect_dir, expect_base,
+ ext, covers, breakpoints)
-
- @staticmethod
- def covers_obj(obj):
- test_file = inspect.getabsfile(obj)
- covers = [test_file]
- match = Test.TEST_COVERS_MATCH.match(test_file)
- if match:
- covers.append(os.path.join(
- os.path.dirname(os.path.dirname(test_file)),
- match.group(1) + '.py'
- ))
- return covers
-
- @staticmethod
- def expect_dir_obj(obj):
- test_file = inspect.getabsfile(obj)
- return os.path.splitext(test_file)[0] + '.expected'
-
- def coverage_includes(self):
- if self.covers is not None:
- return self.covers
- return self.covers_obj(self.func_call.func)
-
- def expect_path(self, ext=None):
- expect_dir = self.expect_dir
- if expect_dir is None:
- expect_dir = self.expect_dir_obj(self.func_call.func)
- name = self.expect_base or self.name
- name = ''.join('_' if c in '<>:"\\/|?*\0' else c for c in name)
- return os.path.join(expect_dir, name + ('.%s' % (ext or self.ext)))
+ @property
+ def func_call(self):
+ return self._func_call
def run(self, context=None):
return self.func_call(context=context)
def process(self, func=lambda test: test.run()):
- """Applies |func| to the test, and yields (self, func(self)).
+ """Applies |func| to the test, and yields (self.get_info(), func(self)).
For duck-typing compatibility with MultiTest.
Bind(name='context') if used by your test function, is bound to None.
- Used interally by expect_tests, you're not expected to call this yourself.
+ Used internally by expect_tests, you're not expected to call this yourself.
"""
- yield self, func(self.bind(context=None))
+ yield self.get_info(), func(self.bind(context=None))
def bind(self, *args, **kwargs):
- return self._replace(func_call=self.func_call.bind(*args, **kwargs))
-
- def restrict(self, tests):
- assert tests[0] is self
- return self
+ return Test(self._name,
+ self._func_call.bind(*args, **kwargs),
+ expect_dir=self.expect_dir,
+ expect_base=self.expect_base,
+ ext=self._ext,
+ covers=self._covers,
+ breakpoints=self._breakpoints)
def get_info(self):
- """Strips test instance of hard-to-pickle stuff
+ """Strips test instance of information required for running test.
Returns a TestInfo instance.
"""
- return TestInfo(self.name, self.expect_dir, self.expect_base, self.ext)
+ return TestInfo(self.name,
+ expect_dir=self._expect_dir,
+ expect_base=self._expect_base,
+ ext=self._ext,
+ covers=self._covers,
+ breakpoints=self._breakpoints)
-_MultiTest = namedtuple(
- 'MultiTest', 'name make_ctx_call destroy_ctx_call tests atomic')
+class MultiTestInfo(object):
+ def __init__(self, name, tests, atomic):
+ self._name = name
+ self._tests = tests
+ self._atomic = atomic
-MultiTestInfo = namedtuple('MultiTestInfo', 'name tests atomic')
+ @property
+ def name(self):
+ return self._name
+ @property
+ def tests(self):
+ return self._tests
-class MultiTest(_MultiTest):
+ @property
+ def atomic(self):
+ return self._atomic
+
+ @staticmethod
+ def expect_path(_ext=None):
+ return None
+
+ def restrict(self, tests):
+ """A helper method to re-cast the MultiTest with fewer subtests.
+
+ All fields will be identical except for tests. If this MultiTest is atomic,
+ then this method returns |self|.
+
+ Used interally by expect_tests, you're not expected to call this yourself.
+ """
+ if self.atomic:
+ return self
+ assert all(t in self.tests for t in tests)
+ return MultiTestInfo(self._name, tests, self._atomic)
+
+ def get_info(self):
+ return self
+
+
+class MultiTest(MultiTestInfo):
"""A wrapper around one or more Test instances.
Allows the entire group to have common pre- and post- actions and an optional
@@ -285,18 +367,18 @@ class MultiTest(_MultiTest):
either all at once, or not at all (i.e., subtests may not be filtered).
"""
- def restrict(self, tests):
- """A helper method to re-cast the MultiTest with fewer subtests.
+ def __init__(self, name, make_ctx_call, destroy_ctx_call, tests, atomic):
+ self._make_ctx_call = make_ctx_call
+ self._destroy_ctx_call = destroy_ctx_call
+ super(MultiTest, self).__init__(name, tests, atomic)
- All fields will be identical except for tests. If this MultiTest is atomic,
- then this method returns |self|.
+ @property
+ def make_ctx_call(self):
+ return self._make_ctx_call
- Used interally by expect_tests, you're not expected to call this yourself.
- """
- if self.atomic:
- return self
- assert all(t in self.tests for t in tests)
- return self._replace(tests=tests)
+ @property
+ def destroy_ctx_call(self):
+ return self._destroy_ctx_call
def process(self, func=lambda test: test.run()):
"""Applies |func| to each sub-test, with properly bound context.
@@ -316,13 +398,23 @@ class MultiTest(_MultiTest):
ctx_object = self.make_ctx_call()
try:
for test in self.tests:
- yield test, func(test.bind(context=ctx_object))
+ yield test.get_info(), func(test.bind(context=ctx_object))
finally:
self.destroy_ctx_call.bind(context=ctx_object)()
- @staticmethod
- def expect_path(_ext=None):
- return None
+ def restrict(self, tests):
+ """A helper method to re-cast the MultiTest with fewer subtests.
+
+ All fields will be identical except for tests. If this MultiTest is atomic,
+ then this method returns |self|.
+
+ Used internally by expect_tests, you're not expected to call this yourself.
+ """
+ if self.atomic:
+ return self
+ assert all(t in self.tests for t in tests)
+ return MultiTest(self._name, self._make_ctx_call, self._destroy_ctx_call,
+ tests, self._atomic)
def get_info(self):
"""Strips MultiTest instance of hard-to-pickle stuff
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698