diff --git a/test/functional/example_test.py b/test/functional/example_test.py --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -30,6 +30,7 @@ connect_nodes, wait_until, ) +from test_framework.decorators import * # NodeConnCB is a class containing callbacks to be executed when a P2P # message is received from the node-under-test. Subclass NodeConnCB and @@ -228,5 +229,13 @@ assert_equal(block, 1) +@tags('fast') +@nodes([], ["-logips"], []) +@test_case("NamedTest") +@clean_chain() +def NamedTest(self): + print("hello world") + + if __name__ == '__main__': ExampleTest().main() diff --git a/test/functional/test_framework/decorators.py b/test/functional/test_framework/decorators.py --- a/test/functional/test_framework/decorators.py +++ b/test/functional/test_framework/decorators.py @@ -7,9 +7,89 @@ examine """ +from test_framework.test_framework import BitcoinTestFramework +import inspect + + +def _build_test_class(obj): + """ + Basic function to either build a TestCase class, or to pass an existing one through. + This is called before any other decorators to ensure we're working with + the proper type of object. + """ + if inspect.isfunction(obj): + # Build our test object + class TestCase(BitcoinTestFramework): + is_test = True + + def run_test(self): + """ + Wrapper to call our test function + """ + obj(self) + + def set_test_params(self): + """ + Do nothing, so we can wrap this other places. + """ + pass + return TestCase + elif inspect.isclass(obj) and issubclass(obj, BitcoinTestFramework): + return obj + else: + assert(False, "Not a function") + + +def _set_param(obj, f): + # wrap set_test_params + cls = _build_test_class(obj) + old_setter = cls.set_test_params + + def wrapped_setter(self): + old_setter(self) + f(self) + cls.set_test_params = wrapped_setter + return cls + + +def _setter_decorator(setter): + def decorator(obj): + return _set_param(obj, setter) + return decorator + def tags(*tags): - def decorater(cls): + def decorater(obj): + cls = _build_test_class(obj) + assert cls, "No class?" cls.test_tags = tags return cls return decorater + + +def clean_chain(clean=True): + def setter(self): + self.setup_clean_chain = clean + return _setter_decorator(setter) + + +def nodes(*node_args): + def setter(self): + self.num_nodes = len(node_args) + self.extra_args = node_args + return _setter_decorator(setter) + + +def mocktime(base_time): + def setter(self): + self.mocktime = base_time + return _setter_decorator(setter) + + +def test_case(name): + def decorator(obj): + cls = _build_test_class(obj) + cls.is_test = True + cls.test_name = name + return cls + return decorator diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -145,14 +145,18 @@ test_instance = test_case() # For compatible with old test printer. # TODO: Update test result printing + if hasattr(test_case, 'test_name'): + name = test_case.test_name + else: + name = test_case.__name__ legacy_name = " ".join( - [self.test_file, test_case.__name__] + param_set) + [self.test_file, name] + param_set) # Use the old name if there's only one test in the file. if len(test_cases) == 1: legacy_name = " ".join([self.test_file] + param_set) update_queue.put(TestStarted(self.test_file, legacy_name)) test_result = self.run_test( - test_instance, obj.__name__, param_set, legacy_name, base_flags, run_tags) + test_instance, name, param_set, legacy_name, base_flags, run_tags) update_queue.put(test_result) def run_test(self, test_instance, test_name, param_set, legacy_name, base_flags, run_tags):