Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/firecracker
Path: blob/main/tests/framework/matrix.py
1956 views
1
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
# SPDX-License-Identifier: Apache-2.0
3
"""Generate multiple microvm configurations and run tests.
4
5
Implements a component that calls a specific function in the context
6
of the cartesian product of all artifact sets.
7
"""
8
9
import os
10
import sys
11
import traceback
12
from framework.artifacts import ARTIFACTS_LOCAL_ROOT
13
from framework.utils import ExceptionAggregator
14
15
16
class TestContext:
17
"""Define a context for running test fns by TestMatrix."""
18
19
__test__ = False
20
21
def __init__(self):
22
"""Initialize the test context."""
23
self._context = {}
24
25
def set_any(self, key, value):
26
"""Set any key value in the context."""
27
self._context[key] = value
28
29
@property
30
def kernel(self):
31
"""Return the kernel artifact."""
32
return self._context.get('kernel', None)
33
34
@kernel.setter
35
def kernel(self, kernel):
36
"""Setter for kernel artifact."""
37
self._context['kernel'] = kernel
38
39
@property
40
def disk(self):
41
"""Return the disk artifact."""
42
return self._context.get('disk', None)
43
44
@disk.setter
45
def disk(self, disk):
46
"""Setter for disk artifact."""
47
self._context['disk'] = disk
48
49
@property
50
def microvm(self):
51
"""Return the microvm artifact."""
52
return self._context.get('microvm', None)
53
54
@microvm.setter
55
def microvm(self, microvm):
56
"""Setter for kernel artifact."""
57
self._context['microvm'] = microvm
58
59
@property
60
def snapshot(self):
61
"""Return the snapshot artifact."""
62
return self._context.get('snapshot', None)
63
64
@snapshot.setter
65
def snapshot(self, snapshot):
66
"""Setter for snapshot artifact."""
67
self._context['snapshot'] = snapshot
68
69
@property
70
def custom(self):
71
"""Return the custom context."""
72
return self._context.get('custom', None)
73
74
@custom.setter
75
def custom(self, custom):
76
"""Setter for custom context."""
77
self._context['custom'] = custom
78
79
80
class TestMatrix:
81
"""Computes the cartesian product of artifacts."""
82
83
__test__ = False
84
85
def __init__(self,
86
artifact_sets,
87
context=TestContext(),
88
cache_dir=ARTIFACTS_LOCAL_ROOT):
89
"""Initialize context, cache dir, and artifact_sets."""
90
self._context = context
91
# Stores the artifact sets array.
92
self._sets = artifact_sets
93
# ArtifactSet stack pointer.
94
self._set_index = 0
95
if not os.path.exists(cache_dir):
96
os.mkdir(cache_dir)
97
self._cache_dir = cache_dir
98
self._failure_aggregator = ExceptionAggregator(add_newline=True)
99
100
@property
101
def sets(self):
102
"""Return the artifact sets."""
103
return self._sets
104
105
def download_artifacts(self):
106
"""Download all configured artifacts."""
107
for artifact_set in self._sets:
108
for artifact in artifact_set.artifacts:
109
artifact.download(self._cache_dir)
110
111
def _backtrack(self, test_fn, cartesian_product):
112
if len(self._sets) < self._set_index:
113
return
114
115
# Validate solution: tuple element count is equal to
116
# set stack size.
117
if len(self._sets) == len(cartesian_product):
118
try:
119
self._run_test_fn(cartesian_product, test_fn)
120
except Exception as _err: # pylint: disable=W0703
121
self._failure_aggregator.add_row(
122
"".join(traceback.format_exception(*sys.exc_info()))
123
)
124
return
125
126
current_set = self._sets[self._set_index]
127
for _artifact in current_set.artifacts:
128
# Prepare for recursive call.
129
# Push the current Artifact in the solution.
130
cartesian_product.append(_artifact)
131
# Go 1 level up the ArtifactSet stack.
132
self._set_index += 1
133
134
self._backtrack(test_fn, cartesian_product)
135
136
# Pop the previous artifact from solution.
137
cartesian_product.pop()
138
# Walk down 1 level.
139
self._set_index -= 1
140
141
def _run_test_fn(self, artifacts, test_fn):
142
"""Patch context and call test_fn."""
143
for artifact in artifacts:
144
self._context.set_any(artifact.type.value, artifact)
145
test_fn(self._context)
146
147
def run_test(self, test_fn):
148
"""Run a test function.
149
150
Iterates over the cartesian product of artifact sets and
151
calls `test_fn` for each element.
152
"""
153
self.download_artifacts()
154
# Reset artifact stack pointer.
155
self._set_index = 0
156
157
# Recursive backtracking will generate the cartesian product of
158
# the artifact sets.
159
self._backtrack(test_fn, [])
160
161
if self._failure_aggregator.has_any():
162
raise self._failure_aggregator
163
164