diff --git a/llvm/utils/lit/lit/LitConfig.py b/llvm/utils/lit/lit/LitConfig.py --- a/llvm/utils/lit/lit/LitConfig.py +++ b/llvm/utils/lit/lit/LitConfig.py @@ -67,6 +67,21 @@ self.parallelism_groups = parallelism_groups self.echo_all_commands = echo_all_commands + self._setup_callbacks = [] + self._teardown_callbacks = [] + + def __getstate__(self): + # Don't pickle callbacks. + state = dict(self.__dict__) + del state['_setup_callbacks'] + del state['_teardown_callbacks'] + return state + + def __setstate__(self, state): + self.__dict__.update(state) + self._setup_callbacks = [] + self._teardown_callbacks = [] + @property def maxIndividualTestTime(self): """ @@ -159,6 +174,42 @@ self.bashPath = '' return dir + + def setup_callback(self, callback): + ''' + Adds the callback to the list of setup callbacks that will be run + before running the test suite. + + Can be used as a decorator in lit configuration files like this: + + @lit_config.setup_callback + def setup(): + ... + ''' + self._setup_callbacks.append(callback) + return callback + + def teardown_callback(self, callback): + ''' + Adds the callback to the list of teardown callbacks that will be run + after the test suite completes. + + Can be used as a decorator in lit configuration files like this: + + @lit_config.teardown_callback + def teardown(): + ... + ''' + self._teardown_callbacks.append(callback) + return callback + + def run_setup_callbacks(self): + for callback in self._setup_callbacks: + callback() + + def run_teardown_callbacks(self): + for callback in self._teardown_callbacks: + callback() def _write_message(self, kind, message): # Get the file/line where this message was generated. diff --git a/llvm/utils/lit/lit/run.py b/llvm/utils/lit/lit/run.py --- a/llvm/utils/lit/lit/run.py +++ b/llvm/utils/lit/lit/run.py @@ -58,12 +58,19 @@ timeout = self.timeout or one_week deadline = time.time() + timeout - self._execute(deadline) + try: + self.lit_config.run_setup_callbacks() + self._execute(deadline) + finally: + self.lit_config.run_teardown_callbacks() # Mark any tests that weren't run as UNRESOLVED. for test in self.tests: if test.result is None: test.setResult(lit.Test.Result(lit.Test.UNRESOLVED, '', 0.0)) + + def _execute(self, deadline): + raise NotImplementedError("Should be implemented in a subclass") # TODO(yln): as the comment says.. this is racing with the main thread waiting # for results diff --git a/llvm/utils/lit/tests/Inputs/setup-teardown/lit.cfg b/llvm/utils/lit/tests/Inputs/setup-teardown/lit.cfg new file mode 100644 --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/setup-teardown/lit.cfg @@ -0,0 +1,14 @@ +import lit.formats +config.name = 'setup-teardown' +config.suffixes = ['.txt'] +config.test_format = lit.formats.ShTest() +config.test_source_root = None +config.test_exec_root = None + +@lit_config.setup_callback +def setup(): + print("Running setup code...") + +@lit_config.teardown_callback +def teardown(): + print("Running teardown code...") diff --git a/llvm/utils/lit/tests/Inputs/setup-teardown/test1.txt b/llvm/utils/lit/tests/Inputs/setup-teardown/test1.txt new file mode 100644 --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/setup-teardown/test1.txt @@ -0,0 +1 @@ +# RUN: true diff --git a/llvm/utils/lit/tests/Inputs/setup-teardown/test2.txt b/llvm/utils/lit/tests/Inputs/setup-teardown/test2.txt new file mode 100644 --- /dev/null +++ b/llvm/utils/lit/tests/Inputs/setup-teardown/test2.txt @@ -0,0 +1 @@ +# RUN: false diff --git a/llvm/utils/lit/tests/setup-teardown.py b/llvm/utils/lit/tests/setup-teardown.py new file mode 100644 --- /dev/null +++ b/llvm/utils/lit/tests/setup-teardown.py @@ -0,0 +1,9 @@ +# RUN: not %{lit} -j2 %{inputs}/setup-teardown | FileCheck %s + +# CHECK: -- Testing: 2 tests, 2 workers -- +# CHECK: Running setup code... +# CHECK-DAG: PASS: setup-teardown :: test1.txt +# CHECK-DAG: FAIL: setup-teardown :: test2.txt +# CHECK: Running teardown code... +# CHECK: Expected Passes : 1 +# CHECK: Unexpected Failures: 1