1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #19546 from MrKevinWeiss/pr/reruntests

Enable compile_and_test_for_board to skip if nothing changed
This commit is contained in:
Teufelchen 2024-02-01 16:22:34 +00:00 committed by GitHub
commit ed44a43464
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 68 additions and 3 deletions

View File

@ -44,6 +44,7 @@ usage: compile_and_test_for_board.py [-h] [--applications APPLICATIONS]
[--test-targets TEST_TARGETS]
[--test-available-targets TEST_AVAILABLE_TARGETS]
[--report-xml] [--jobs JOBS]
[--only-if-changed]
riot_directory board [result_directory]
positional arguments:
@ -84,6 +85,9 @@ optional arguments:
(default: False)
--jobs JOBS, -j JOBS Parallel building (0 means not limit, like '--jobs')
(default: None)
--only-if-changed, -c
Only test if the application has changed since last
test (default: False)
```
""" # noqa
@ -225,6 +229,7 @@ def is_in_directory(path, directory):
return path.startswith(directory)
# pylint: disable=too-many-instance-attributes
class RIOTApplication:
"""RIOT Application representation.
@ -245,6 +250,7 @@ class RIOTApplication:
)
FLASH_TARGETS = ("flash-only",)
TEST_TARGETS = ("test",)
TEST_INPUT_HASH = ("test-input-hash-changed",)
TEST_AVAILABLE_TARGETS = ("test/available",)
# pylint: disable=too-many-arguments
@ -252,6 +258,7 @@ class RIOTApplication:
self.board = board
self.riotdir = riotdir
self.appdir = appdir
self.hashdir = os.path.abspath(os.path.join(resultdir, "hashes", appdir))
self.resultdir = os.path.join(resultdir, appdir)
if junit:
if not junit_xml:
@ -368,8 +375,11 @@ class RIOTApplication:
incremental=False,
jobs=False,
with_test_only=False,
only_if_changed=False,
):
# pylint:disable=too-many-arguments
# pylint:disable=too-many-nested-blocks
# pylint:disable=too-many-branches
"""Compile and execute test if available.
Checks for board supported/enough memory, compiles.
@ -422,8 +432,28 @@ class RIOTApplication:
if clean_after:
self.clean_intermediates()
# pylint: disable=too-many-nested-blocks
if runtest:
if has_test:
if only_if_changed:
try:
out = self.make(
self.TEST_INPUT_HASH,
env={"RIOT_TEST_HASH_DIR": self.hashdir},
)
hash_match = "hashes match" in out.lower()
self.logger.info("Hashes match: %r", hash_match)
if hash_match:
self._skip(
"bins_unchanged",
f"{self.appdir} matched test input hashes.",
)
if clean_after:
self.clean()
self.logger.info("Success")
return
except subprocess.CalledProcessError as err:
self._make_handle_error(self.TEST_INPUT_HASH[0], err)
setuptasks = collections.OrderedDict([("flash", self.FLASH_TARGETS)])
self.make_with_outfile(
"test", self.TEST_TARGETS, save_output=True, setuptasks=setuptasks
@ -732,7 +762,6 @@ PARSER.add_argument(
default=False,
help="Output results to report.xml in the " "result_directory",
)
PARSER.add_argument(
"--jobs",
"-j",
@ -740,6 +769,14 @@ PARSER.add_argument(
default=None,
help="Parallel building (0 means not limit, like '--jobs')",
)
# Arg that allows hashes of tests to be saved locally in a hashes folder
# and used to skip tests if new hashes match.
PARSER.add_argument(
"--only-if-changed",
"-c",
action="store_true",
help="Only test if the application has changed since last test",
)
def main(args):
@ -798,6 +835,7 @@ def main(args):
incremental=args.incremental,
jobs=args.jobs,
with_test_only=args.with_test_only,
only_if_changed=args.only_if_changed,
)
for app in applications
]

View File

@ -85,6 +85,9 @@ test-with-config/check-config:
done; \
${COLOR_ECHO} -n "${COLOR_RESET}"
RIOT_TEST_HASH_DIR ?= $(BINDIR)
# this target only makes sense if an ELFFILE is actually created, thus guard by
# RIOTNOLINK="".
ifeq (,$(RIOTNOLINK))
@ -92,10 +95,33 @@ ifeq (,$(RIOTNOLINK))
$(error HASHFILE is empty for $(BOARD))
endif
test-input-hash: $(TESTS) $(TESTS_WITH_CONFIG) $(TESTS_AS_ROOT) $(HASHFILE) $(TEST_EXTRA_FILES)
sha1sum $^ > $(BINDIR)/test-input-hash.sha1
sha1sum $^ > $(RIOT_TEST_HASH_DIR)/test-input-hash.sha1
else
# .SECONDARY creates the bin folder, we depend on it to avoid writing to it
# prior to it being created when concurrent building is used
test-input-hash: .SECONDARY
$(file >$(BINDIR)/test-input-hash.sha1,no binary generated due to RIOTNOLINK=1)
$(file >$(RIOT_TEST_HASH_DIR)/test-input-hash.sha1,no binary generated due to RIOTNOLINK=1)
endif
# Helper function to compare two strings
define compare_strings
$(and $(filter $1,$2),$(filter $2,$1))
endef
# Target to test only if the input hash has changed
.PHONY: test-input-hash-changed
test-input-hash-changed:
@if [ ! -f "$(RIOT_TEST_HASH_DIR)/test-input-hash.sha1" ]; then \
echo "Old hash file doesn't exist. Generating hash and running tests..."; \
mkdir -p $(RIOT_TEST_HASH_DIR); \
$(MAKE) test-input-hash; \
else \
OLD_HASH=$$(cat $(RIOT_TEST_HASH_DIR)/test-input-hash.sha1); \
$(MAKE) test-input-hash; \
NEW_HASH=$$(cat $(RIOT_TEST_HASH_DIR)/test-input-hash.sha1); \
if [ "$${OLD_HASH}" != "$${NEW_HASH}" ]; then \
echo "Hashes do not match."; \
else \
echo "Hashes match."; \
fi; \
fi

View File

@ -46,6 +46,7 @@ export RIOTMAKE # Location of all supplemental Makefiles (such as t
export RIOTKCONFIG # Location of all supplemental Kconfig files
export BINDIRBASE # This is the folder where the application should be built in. For each BOARD a different subfolder is used.
export BINDIR # This is the folder where the application should be built in.
export RIOT_TEST_HASH_DIR # The dir to generate the test-input-hash.sha1 file for checking if a test has changed, uses BINDIR by default.
export CARGO_TARGET_DIR # This is the folder where Rust parts of the application should be built in.
export BUILD_DIR # This is the base folder to store common build files and artifacts, e.g. test results.
export APPDIR # The base folder containing the application