summaryrefslogtreecommitdiff
path: root/rtemsspec/runtests.py
diff options
context:
space:
mode:
Diffstat (limited to 'rtemsspec/runtests.py')
-rw-r--r--rtemsspec/runtests.py115
1 files changed, 115 insertions, 0 deletions
diff --git a/rtemsspec/runtests.py b/rtemsspec/runtests.py
new file mode 100644
index 00000000..a0de436d
--- /dev/null
+++ b/rtemsspec/runtests.py
@@ -0,0 +1,115 @@
+# SPDX-License-Identifier: BSD-2-Clause
+""" This module provides a build step to run the RTEMS Tester. """
+
+# Copyright (C) 2022 embedded brains GmbH (http://www.embedded-brains.de)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import datetime
+import json
+import os
+import logging
+import time
+from typing import Dict, List
+
+from rtemsspec.directorystate import DirectoryState
+from rtemsspec.items import Item
+from rtemsspec.packagebuild import BuildItem, PackageBuildDirector
+from rtemsspec.testrunner import Executable, Report, TestRunner
+
+
+def _now_utc() -> str:
+ return datetime.datetime.utcnow().isoformat()
+
+
+class TestLog(DirectoryState):
+ """ Maintains a test log. """
+
+ def __init__(self, director: PackageBuildDirector, item: Item):
+ super().__init__(director, item)
+ self.reports: List[Report] = []
+
+ def discard(self) -> None:
+ try:
+ with open(self.file, "r", encoding="utf-8") as src:
+ self.reports = json.load(src)["reports"]
+ logging.info("%s: loaded test log: %s", self.uid, self.file)
+ except FileNotFoundError:
+ self.reports = []
+ super().discard()
+
+ def get_reports_by_hash(self) -> Dict[str, Report]:
+ """ Gets the reports by executable hash. """
+ reports_by_hash: Dict[str, Report] = {}
+ for report in self.reports:
+ digest = report["executable-sha512"]
+ assert digest not in reports_by_hash
+ assert isinstance(digest, str)
+ reports_by_hash[digest] = report
+ return reports_by_hash
+
+
+class RunTests(BuildItem):
+ """ Runs the tests. """
+
+ def run(self) -> None:
+ start_time = _now_utc()
+ begin = time.monotonic()
+ log = self.output("log")
+ assert isinstance(log, TestLog)
+ previous_reports_by_hash = log.get_reports_by_hash()
+
+ # Use previous report if the executable hash did not change
+ source = self.input("source")
+ assert isinstance(source, DirectoryState)
+ reports: List[Report] = []
+ executables: List[Executable] = []
+ for path, digest in source.files_and_hashes():
+ if not path.endswith(".exe") or path.endswith(".norun.exe"):
+ continue
+ assert digest
+ report = previous_reports_by_hash.get(digest, None)
+ if report is None:
+ logging.debug("%s: run: %s", self.uid, path)
+ executables.append(Executable(path, digest, 1800))
+ else:
+ logging.debug("%s: use previous report for: %s", self.uid,
+ path)
+ report["executable"] = path
+ reports.append(report)
+
+ # Run the tests with changed executables
+ if executables:
+ runner = self.input("runner")
+ assert isinstance(runner, TestRunner)
+ reports.extend(runner.run_tests(executables))
+
+ # Save the reports
+ os.makedirs(os.path.dirname(log.file), exist_ok=True)
+ with open(log.file, "w", encoding="utf-8") as dst:
+ data = {
+ "duration": time.monotonic() - begin,
+ "end-time": _now_utc(),
+ "reports": sorted(reports, key=lambda x: x["executable"]),
+ "start-time": start_time
+ }
+ json.dump(data, dst, sort_keys=True, indent=2)