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

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: Fixed breakpoints computation 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 | « expect_tests/handle_list.py ('k') | 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..6ac2fdd622416160ff39675158e43bb600271186 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,10 @@ 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 Test(object):
+ TEST_COVERS_MATCH = re.compile('.*/test/([^/]+)_test\.py$')
-TestInfo = namedtuple(
- 'TestInfo', 'name expect_dir expect_base ext')
-
-
-class Test(_Test):
- TEST_COVERS_MATCH = re.compile('.*/test/([^/]*)_test\.py$')
-
- def __new__(cls, name, func_call, expect_dir=None, expect_base=None,
+ def __init__(self, name, func_call, expect_dir=None, expect_base=None,
ext='json', covers=None, breakpoints=None, break_funcs=()):
"""Create a new test.
@@ -183,87 +177,125 @@ class Test(_Test):
mode. See |break_funcs| for an easier way to set this.
@param break_funcs: A list of functions for which to set breakpoints.
"""
- breakpoints = breakpoints or []
- if not breakpoints or break_funcs:
- for f in (break_funcs or (func_call.func,)):
- if hasattr(f, 'im_func'):
- f = f.im_func
- breakpoints.append((f.func_code.co_filename,
- f.func_code.co_firstlineno,
- f.func_code.co_name))
-
+ self._func_call = func_call
+ self._name = name
if expect_dir:
expect_dir = expect_dir.rstrip('/')
- return super(Test, cls).__new__(cls, name, func_call, expect_dir,
- expect_base, ext, covers, breakpoints)
+ self._expect_dir = expect_dir
+ self._expect_base = expect_base
+ self._ext = ext
+ self._covers = covers
+ self._breakpoints = copy.copy(breakpoints) if breakpoints else []
+
+ if self._func_call:
+ if not self._breakpoints or break_funcs:
+ for f in (break_funcs or (func_call.func,)):
+ if hasattr(f, 'im_func'):
+ f = f.im_func
+ self._breakpoints.append((f.func_code.co_filename,
+ f.func_code.co_firstlineno,
+ f.func_code.co_name))
+ @property
+ def name(self):
+ return self._name
- @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
+ @property
+ def func_call(self):
+ return self._func_call
- @staticmethod
- def expect_dir_obj(obj):
- test_file = inspect.getabsfile(obj)
- return os.path.splitext(test_file)[0] + '.expected'
+ @property
+ def expect_dir(self):
+ return self._expect_dir
- def coverage_includes(self):
- if self.covers is not None:
- return self.covers
- return self.covers_obj(self.func_call.func)
+ @property
+ def expect_base(self):
+ return self._expect_base
- 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 ext(self):
+ return self._ext
+
+ @property
+ def covers(self):
+ return self._covers
+
+ @property
+ def breakpoints(self):
+ return self._breakpoints
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))
+ 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 restrict(self, tests):
assert tests[0] is self
return self
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 Test(self.name,
+ None,
+ expect_dir=self._expect_dir,
+ expect_base=self._expect_base,
+ ext=self._ext,
+ covers=self._covers,
+ breakpoints=self._breakpoints)
+ def coverage_includes(self):
+ if self._covers is not None:
+ return self._covers
+ return self.covers_obj(self._func_call.func)
+
+ @classmethod
+ def covers_obj(cls, obj):
+ test_file = inspect.getabsfile(obj)
+ covers = [test_file]
+ match = cls.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
-_MultiTest = namedtuple(
- 'MultiTest', 'name make_ctx_call destroy_ctx_call tests atomic')
+ 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,)))
-MultiTestInfo = namedtuple('MultiTestInfo', 'name tests atomic')
+ @staticmethod
+ def expect_dir_obj(obj):
+ test_file = inspect.getabsfile(obj)
+ return os.path.splitext(test_file)[0] + '.expected'
-class MultiTest(_MultiTest):
+class MultiTest(object):
"""A wrapper around one or more Test instances.
Allows the entire group to have common pre- and post- actions and an optional
@@ -285,18 +317,32 @@ 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._name = name
+ self._make_ctx_call = make_ctx_call
+ self._destroy_ctx_call = destroy_ctx_call
+ self._tests = tests
+ self._atomic = atomic
- All fields will be identical except for tests. If this MultiTest is atomic,
- then this method returns |self|.
+ @property
+ def name(self):
+ return self._name
- 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 make_ctx_call(self):
+ return self._make_ctx_call
+
+ @property
+ def destroy_ctx_call(self):
+ return self._destroy_ctx_call
+
+ @property
+ def tests(self):
+ return self._tests
+
+ @property
+ def atomic(self):
+ return self._atomic
def process(self, func=lambda test: test.run()):
"""Applies |func| to each sub-test, with properly bound context.
@@ -316,13 +362,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
@@ -330,12 +386,14 @@ class MultiTest(_MultiTest):
Returns a MultiTestInfo instance.
"""
all_tests = [test.get_info() for test in self.tests]
- test = MultiTestInfo(name=self.name,
- tests=all_tests,
- atomic=self.atomic
- )
+ test = MultiTest(self._name, None, None, all_tests, self._atomic)
return test
+ @staticmethod
+ def expect_path(_ext=None):
+ return None
+
+
class Handler(object):
"""Handler object.
« no previous file with comments | « expect_tests/handle_list.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698