diff options
author | Alexis Lothoré <alexis.lothore@bootlin.com> | 2023-06-09 08:48:01 +0200 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-06-17 11:50:57 +0100 |
commit | 58206ea8d94e30a61dbd732d652cd2cdeb9c8e4f (patch) | |
tree | 6923f614e1b982442182165a3c833cab2005706b /meta | |
parent | cdac245fc288a5ecc8163325f4b1468d4bc643db (diff) | |
download | poky-58206ea8d94e30a61dbd732d652cd2cdeb9c8e4f.tar.gz |
testimage: implement test artifacts retriever for failing tests
Add a basic artifacts retrievers in testimage class which:
- triggers when at least one runtime test fails but tests execution
encountered no major issue
- reads a list of paths to retrieve from TESTIMAGE_FAILED_QA_ARTIFACTS
- checks for artifacts presence on target
- retrieve those files over scp thanks to existing ssh class
- store those files in an "artifacts" directory in "tmp/log/oeqa/<image>"
This implementation assumes that the SSH or Qemu target has run and
finished gracefully. If tests do not finish because of an exception,
artifacts will not be retrieved
Bring partial solution to [YOCTO #14901]
(From OE-Core rev: 36ef582b8c1c99e6af1ce79ea79f5b059d2a1aad)
Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta')
-rw-r--r-- | meta/classes-recipe/testimage.bbclass | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/meta/classes-recipe/testimage.bbclass b/meta/classes-recipe/testimage.bbclass index b48cd96575..765184c180 100644 --- a/meta/classes-recipe/testimage.bbclass +++ b/meta/classes-recipe/testimage.bbclass | |||
@@ -18,6 +18,15 @@ inherit image-artifact-names | |||
18 | 18 | ||
19 | TESTIMAGE_AUTO ??= "0" | 19 | TESTIMAGE_AUTO ??= "0" |
20 | 20 | ||
21 | # When any test fails, TESTIMAGE_FAILED_QA ARTIFACTS will be parsed and for | ||
22 | # each entry in it, if artifact pointed by path description exists on target, | ||
23 | # it will be retrieved onto host | ||
24 | |||
25 | TESTIMAGE_FAILED_QA_ARTIFACTS ??= "\ | ||
26 | ${localstatedir}/log \ | ||
27 | ${sysconfdir}/version \ | ||
28 | ${sysconfdir}/os-release" | ||
29 | |||
21 | # You can set (or append to) TEST_SUITES in local.conf to select the tests | 30 | # You can set (or append to) TEST_SUITES in local.conf to select the tests |
22 | # which you want to run for your target. | 31 | # which you want to run for your target. |
23 | # The test names are the module names in meta/lib/oeqa/runtime/cases. | 32 | # The test names are the module names in meta/lib/oeqa/runtime/cases. |
@@ -192,6 +201,39 @@ def get_testimage_boot_patterns(d): | |||
192 | boot_patterns[flag] = flagval.encode().decode('unicode-escape') | 201 | boot_patterns[flag] = flagval.encode().decode('unicode-escape') |
193 | return boot_patterns | 202 | return boot_patterns |
194 | 203 | ||
204 | def get_artifacts_list(target, raw_list): | ||
205 | result = [] | ||
206 | # Passed list may contains patterns in paths, expand them directly on target | ||
207 | for raw_path in raw_list.split(): | ||
208 | cmd = f"for p in {raw_path}; do if [ -e $p ]; then echo $p; fi; done" | ||
209 | try: | ||
210 | status, output = target.run(cmd) | ||
211 | if status != 0 or not output: | ||
212 | raise Exception() | ||
213 | result += output.split() | ||
214 | except: | ||
215 | bb.warn(f"No file/directory matching path {raw_path}") | ||
216 | |||
217 | return result | ||
218 | |||
219 | def retrieve_test_artifacts(target, artifacts_list, target_dir): | ||
220 | import shutil | ||
221 | |||
222 | local_artifacts_dir = os.path.join(target_dir, "artifacts") | ||
223 | if os.path.isdir(local_artifacts_dir): | ||
224 | shutil.rmtree(local_artifacts_dir) | ||
225 | |||
226 | os.makedirs(local_artifacts_dir) | ||
227 | for artifact_path in artifacts_list: | ||
228 | if not os.path.isabs(artifact_path): | ||
229 | bb.warn(f"{artifact_path} is not an absolute path") | ||
230 | continue | ||
231 | try: | ||
232 | dest_dir = os.path.join(local_artifacts_dir, os.path.dirname(artifact_path[1:])) | ||
233 | os.makedirs(dest_dir, exist_ok=True) | ||
234 | target.copyFrom(artifact_path, dest_dir) | ||
235 | except: | ||
236 | bb.warn(f"Can not retrieve {artifact_path} from test target") | ||
195 | 237 | ||
196 | def testimage_main(d): | 238 | def testimage_main(d): |
197 | import os | 239 | import os |
@@ -383,6 +425,12 @@ def testimage_main(d): | |||
383 | pass | 425 | pass |
384 | results = tc.runTests() | 426 | results = tc.runTests() |
385 | complete = True | 427 | complete = True |
428 | if results.hasAnyFailingTest(): | ||
429 | artifacts_list = get_artifacts_list(tc.target, d.getVar("TESTIMAGE_FAILED_QA_ARTIFACTS")) | ||
430 | if not artifacts_list: | ||
431 | bb.warn("Could not load artifacts list, skip artifacts retrieval") | ||
432 | else: | ||
433 | retrieve_test_artifacts(tc.target, artifacts_list, get_testimage_json_result_dir(d)) | ||
386 | except (KeyboardInterrupt, BlockingIOError) as err: | 434 | except (KeyboardInterrupt, BlockingIOError) as err: |
387 | if isinstance(err, KeyboardInterrupt): | 435 | if isinstance(err, KeyboardInterrupt): |
388 | bb.error('testimage interrupted, shutting down...') | 436 | bb.error('testimage interrupted, shutting down...') |