def __init__(self, artifact_sets, context=TestContext(), cache_dir=ARTIFACTS_LOCAL_ROOT): """Initialize context, cache dir, and artifact_sets.""" self._context = context # Stores the artifact sets array. self._sets = artifact_sets # ArtifactSet stack pointer. self._set_index = 0 if not os.path.exists(cache_dir): os.mkdir(cache_dir) self._cache_dir = cache_dir self._failure_aggregator = ExceptionAggregator(add_newline=True)
class TestMatrix: """Computes the cartesian product of artifacts.""" __test__ = False def __init__(self, artifact_sets, context=TestContext(), cache_dir=ARTIFACTS_LOCAL_ROOT): """Initialize context, cache dir, and artifact_sets.""" self._context = context # Stores the artifact sets array. self._sets = artifact_sets # ArtifactSet stack pointer. self._set_index = 0 if not os.path.exists(cache_dir): os.mkdir(cache_dir) self._cache_dir = cache_dir self._failure_aggregator = ExceptionAggregator(add_newline=True) @property def sets(self): """Return the artifact sets.""" return self._sets def download_artifacts(self): """Download all configured artifacts.""" for artifact_set in self._sets: for artifact in artifact_set.artifacts: artifact.download(self._cache_dir) def _backtrack(self, test_fn, cartesian_product): if len(self._sets) < self._set_index: return # Validate solution: tuple element count is equal to # set stack size. if len(self._sets) == len(cartesian_product): try: self._run_test_fn(cartesian_product, test_fn) except Exception as _err: # pylint: disable=W0703 self._failure_aggregator.add_row("".join( traceback.format_exception(*sys.exc_info()))) return current_set = self._sets[self._set_index] for _artifact in current_set.artifacts: # Prepare for recursive call. # Push the current Artifact in the solution. cartesian_product.append(_artifact) # Go 1 level up the ArtifactSet stack. self._set_index += 1 self._backtrack(test_fn, cartesian_product) # Pop the previous artifact from solution. cartesian_product.pop() # Walk down 1 level. self._set_index -= 1 def _run_test_fn(self, artifacts, test_fn): """Patch context and call test_fn.""" for artifact in artifacts: self._context.set_any(artifact.type.value, artifact) test_fn(self._context) def run_test(self, test_fn): """Run a test function. Iterates over the cartesian product of artifact sets and calls `test_fn` for each element. """ self.download_artifacts() # Reset artifact stack pointer. self._set_index = 0 # Recursive backtracking will generate the cartesian product of # the artifact sets. self._backtrack(test_fn, []) if self._failure_aggregator.has_any(): raise self._failure_aggregator