diff --git a/.vscode/settings.template.json b/.vscode/settings.template.json index 108294e5c0..0253a5cc74 100644 --- a/.vscode/settings.template.json +++ b/.vscode/settings.template.json @@ -74,6 +74,7 @@ "editor.formatOnSave": true, "cmake.ignoreKitEnv": true, "cmake.configureOnOpen": true, + "cmake.format.allowOptionalArgumentIndentation": true, "cmake.buildDirectory": "${workspaceFolder}/_build_cmake_tools", "cmake.mergedCompileCommands": "${workspaceFolder}/compile_commands.json", "cmake.generator": "Ninja", diff --git a/CMakeLists.txt b/CMakeLists.txt index 67b7aa144d..e5aecf7fd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,9 @@ set(BOOTLOADER_DIR ${ROOT_DIR}/app/bootloader) # spikes set(SPIKES_DIR ${ROOT_DIR}/spikes) +# functional tests +set(TESTS_FUNCTIONAL_DIR ${ROOT_DIR}/tests/functional) + # # MARK: - LekaOS Project # @@ -136,6 +139,9 @@ add_subdirectory(${LIBS_DIR}) # Add spikes add_subdirectory(${SPIKES_DIR}) +# Add functional tests +add_subdirectory(${TESTS_FUNCTIONAL_DIR}) + # Add bootloader add_subdirectory(${BOOTLOADER_DIR}) diff --git a/spikes/CMakeLists.txt b/spikes/CMakeLists.txt index e8690ad216..d626c11ee0 100644 --- a/spikes/CMakeLists.txt +++ b/spikes/CMakeLists.txt @@ -6,7 +6,6 @@ add_subdirectory(${SPIKES_DIR}/lk_audio) add_subdirectory(${SPIKES_DIR}/lk_behavior_kit) add_subdirectory(${SPIKES_DIR}/lk_ble) add_subdirectory(${SPIKES_DIR}/lk_bluetooth) -add_subdirectory(${SPIKES_DIR}/lk_boost_ut) add_subdirectory(${SPIKES_DIR}/lk_cg_animations) add_subdirectory(${SPIKES_DIR}/lk_color_kit) add_subdirectory(${SPIKES_DIR}/lk_command_kit) @@ -46,7 +45,6 @@ add_custom_target(spikes_leka) add_dependencies(spikes_leka spike_lk_ble spike_lk_bluetooth - spike_lk_boost_ut spike_lk_cg_animations spike_lk_color_kit spike_lk_command_kit diff --git a/spikes/lk_boost_ut/CMakeLists.txt b/spikes/lk_boost_ut/CMakeLists.txt deleted file mode 100644 index 9d6df8a578..0000000000 --- a/spikes/lk_boost_ut/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -# Leka - LekaOS -# Copyright 2022 APF France handicap -# SPDX-License-Identifier: Apache-2.0 - -add_mbed_executable(spike_lk_boost_ut) - -target_include_directories(spike_lk_boost_ut - PRIVATE - . -) - -target_sources(spike_lk_boost_ut - PRIVATE - main.cpp - tests/test_array.cpp - tests/test_expect.cpp - tests/test_minimal.cpp - tests/test_mutable.cpp - tests/test_parametrized.cpp - tests/test_should.cpp - tests/test_skip.cpp - tests/test_spec.cpp -) - -target_link_libraries(spike_lk_boost_ut -) - -target_link_custom_leka_targets(spike_lk_boost_ut) diff --git a/tests/functional/CMakeLists.txt b/tests/functional/CMakeLists.txt new file mode 100644 index 0000000000..1fc5291047 --- /dev/null +++ b/tests/functional/CMakeLists.txt @@ -0,0 +1,43 @@ +# Leka - LekaOS +# Copyright 2022 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +set(TESTS_FUNCTIONAL_INCLUDE_DIR ${TESTS_FUNCTIONAL_DIR}/include) +set(TESTS_FUNCTIONAL_SOURCE_DIR ${TESTS_FUNCTIONAL_DIR}/source) +set(TESTS_FUNCTIONAL_TESTS_DIR ${TESTS_FUNCTIONAL_DIR}/tests) + +function(register_functional_test) + set(options "") + set(oneValueArgs TARGET) + set(multiValueArgs INCLUDE_DIRECTORIES SOURCES LINK_LIBRARIES) + + cmake_parse_arguments(REGISTER_FT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + add_mbed_executable(${REGISTER_FT_TARGET}) + + target_include_directories(${REGISTER_FT_TARGET} + PRIVATE + ${TESTS_FUNCTIONAL_INCLUDE_DIR} + ${REGISTER_FT_INCLUDE_DIRECTORIES} + ) + + target_sources(${REGISTER_FT_TARGET} + PRIVATE + ${TESTS_FUNCTIONAL_SOURCE_DIR}/test_main.cpp + ${REGISTER_FT_SOURCES} + ) + + target_link_libraries(${REGISTER_FT_TARGET} + ${REGISTER_FT_LINK_LIBRARIES} + ) + + target_compile_definitions(${REGISTER_FT_TARGET} + PUBLIC + TARGET="${REGISTER_FT_TARGET}" + ) +endfunction() + +add_subdirectory(${TESTS_FUNCTIONAL_TESTS_DIR}/boost_ut) + +add_subdirectory(${TESTS_FUNCTIONAL_TESTS_DIR}/io_expander) +add_subdirectory(${TESTS_FUNCTIONAL_TESTS_DIR}/qdac) diff --git a/spikes/lk_boost_ut/tests/config.h b/tests/functional/include/tests/config.h similarity index 100% rename from spikes/lk_boost_ut/tests/config.h rename to tests/functional/include/tests/config.h diff --git a/spikes/lk_boost_ut/utils.h b/tests/functional/include/tests/utils.h similarity index 83% rename from spikes/lk_boost_ut/utils.h rename to tests/functional/include/tests/utils.h index 5c42047618..6af8efd52e 100644 --- a/spikes/lk_boost_ut/utils.h +++ b/tests/functional/include/tests/utils.h @@ -15,9 +15,7 @@ using namespace std::chrono; inline auto start = rtos::Kernel::Clock::now(); inline auto stop = rtos::Kernel::Clock::now(); -inline auto delta = static_cast((stop - start).count()); - -// namespace time +inline auto delta = [] { return static_cast((stop - start).count()); }; } // namespace utils::time @@ -34,8 +32,7 @@ inline auto delta = static_cast((stop - start).count()); // NOLINTNEXTLINE #define utils_end() \ do { \ - utils::time::stop = rtos::Kernel::Clock::now(); \ - utils::time::delta = static_cast((utils::time::stop - utils::time::start).count()); \ + utils::time::stop = rtos::Kernel::Clock::now(); \ log_ll("\n", 1); \ - log_info("End of tests (%i ms total)", utils::time::delta); \ + log_info("End of tests (%i ms total)", utils::time::delta()); \ } while (0) diff --git a/spikes/lk_boost_ut/main.cpp b/tests/functional/source/test_main.cpp similarity index 66% rename from spikes/lk_boost_ut/main.cpp rename to tests/functional/source/test_main.cpp index b82d828713..01476ec967 100644 --- a/spikes/lk_boost_ut/main.cpp +++ b/tests/functional/source/test_main.cpp @@ -5,9 +5,9 @@ #include #include -#include "./tests/config.h" -#include "./utils.h" #include "boost/ut.hpp" +#include "tests/config.h" +#include "tests/utils.h" using namespace leka; using namespace std::chrono; @@ -15,9 +15,9 @@ namespace ut = boost::ut; auto main() -> int { - ut::cfg = {.filter = "*", .colors = {.none = "", .pass = "", .fail = ""}, .dry_run = false}; + ut::cfg = {.filter = "*", .dry_run = false}; - utils_start("boost::ut example spike"); + utils_start(TARGET); [[maybe_unused]] const auto result = ut::cfg<>.run({.report_errors = true}); diff --git a/tests/functional/tests/boost_ut/CMakeLists.txt b/tests/functional/tests/boost_ut/CMakeLists.txt new file mode 100644 index 0000000000..08ac679f4b --- /dev/null +++ b/tests/functional/tests/boost_ut/CMakeLists.txt @@ -0,0 +1,22 @@ +# Leka - LekaOS +# Copyright 2022 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +register_functional_test( + TARGET + functional_ut_boost_ut + + INCLUDE_DIRECTORIES + + SOURCES + test_array.cpp + test_expect.cpp + test_minimal.cpp + test_mutable.cpp + test_parametrized.cpp + test_should.cpp + test_skip.cpp + test_spec.cpp + + LINK_LIBRARIES +) diff --git a/spikes/lk_boost_ut/tests/test_array.cpp b/tests/functional/tests/boost_ut/test_array.cpp similarity index 85% rename from spikes/lk_boost_ut/tests/test_array.cpp rename to tests/functional/tests/boost_ut/test_array.cpp index 7bf8435407..b0a4a50e70 100644 --- a/spikes/lk_boost_ut/tests/test_array.cpp +++ b/tests/functional/tests/boost_ut/test_array.cpp @@ -2,8 +2,7 @@ // Copyright 2022 APF France handicap // SPDX-License-Identifier: Apache-2.0 -#include "./tests/config.h" -#include "boost/ut.hpp" +#include "tests/config.h" using namespace boost::ut; diff --git a/spikes/lk_boost_ut/tests/test_expect.cpp b/tests/functional/tests/boost_ut/test_expect.cpp similarity index 98% rename from spikes/lk_boost_ut/tests/test_expect.cpp rename to tests/functional/tests/boost_ut/test_expect.cpp index 2d3447d0e6..3cda1b5983 100644 --- a/spikes/lk_boost_ut/tests/test_expect.cpp +++ b/tests/functional/tests/boost_ut/test_expect.cpp @@ -4,8 +4,7 @@ #include -#include "./tests/config.h" -#include "boost/ut.hpp" +#include "tests/config.h" using namespace boost::ut; diff --git a/spikes/lk_boost_ut/tests/test_minimal.cpp b/tests/functional/tests/boost_ut/test_minimal.cpp similarity index 78% rename from spikes/lk_boost_ut/tests/test_minimal.cpp rename to tests/functional/tests/boost_ut/test_minimal.cpp index 32c9222c5c..4a374f9fac 100644 --- a/spikes/lk_boost_ut/tests/test_minimal.cpp +++ b/tests/functional/tests/boost_ut/test_minimal.cpp @@ -2,8 +2,7 @@ // Copyright 2022 APF France handicap // SPDX-License-Identifier: Apache-2.0 -#include "./tests/config.h" -#include "boost/ut.hpp" +#include "tests/config.h" using namespace ut; diff --git a/spikes/lk_boost_ut/tests/test_mutable.cpp b/tests/functional/tests/boost_ut/test_mutable.cpp similarity index 90% rename from spikes/lk_boost_ut/tests/test_mutable.cpp rename to tests/functional/tests/boost_ut/test_mutable.cpp index 657f2f6a52..58648ffc8c 100644 --- a/spikes/lk_boost_ut/tests/test_mutable.cpp +++ b/tests/functional/tests/boost_ut/test_mutable.cpp @@ -2,8 +2,7 @@ // Copyright 2022 APF France handicap // SPDX-License-Identifier: Apache-2.0 -#include "./tests/config.h" -#include "boost/ut.hpp" +#include "tests/config.h" using namespace boost::ut; diff --git a/spikes/lk_boost_ut/tests/test_parametrized.cpp b/tests/functional/tests/boost_ut/test_parametrized.cpp similarity index 96% rename from spikes/lk_boost_ut/tests/test_parametrized.cpp rename to tests/functional/tests/boost_ut/test_parametrized.cpp index 787ee10e46..ad130fb844 100644 --- a/spikes/lk_boost_ut/tests/test_parametrized.cpp +++ b/tests/functional/tests/boost_ut/test_parametrized.cpp @@ -4,8 +4,7 @@ #include -#include "./tests/config.h" -#include "boost/ut.hpp" +#include "tests/config.h" using namespace boost::ut; diff --git a/spikes/lk_boost_ut/tests/test_should.cpp b/tests/functional/tests/boost_ut/test_should.cpp similarity index 89% rename from spikes/lk_boost_ut/tests/test_should.cpp rename to tests/functional/tests/boost_ut/test_should.cpp index 476f457fa0..ab57fed8a2 100644 --- a/spikes/lk_boost_ut/tests/test_should.cpp +++ b/tests/functional/tests/boost_ut/test_should.cpp @@ -2,8 +2,7 @@ // Copyright 2022 APF France handicap // SPDX-License-Identifier: Apache-2.0 -#include "./tests/config.h" -#include "boost/ut.hpp" +#include "tests/config.h" using namespace boost::ut; diff --git a/spikes/lk_boost_ut/tests/test_skip.cpp b/tests/functional/tests/boost_ut/test_skip.cpp similarity index 90% rename from spikes/lk_boost_ut/tests/test_skip.cpp rename to tests/functional/tests/boost_ut/test_skip.cpp index ac79d7fb35..7d775dfefa 100644 --- a/spikes/lk_boost_ut/tests/test_skip.cpp +++ b/tests/functional/tests/boost_ut/test_skip.cpp @@ -2,8 +2,7 @@ // Copyright 2022 APF France handicap // SPDX-License-Identifier: Apache-2.0 -#include "./tests/config.h" -#include "boost/ut.hpp" +#include "tests/config.h" using namespace boost::ut; diff --git a/spikes/lk_boost_ut/tests/test_spec.cpp b/tests/functional/tests/boost_ut/test_spec.cpp similarity index 92% rename from spikes/lk_boost_ut/tests/test_spec.cpp rename to tests/functional/tests/boost_ut/test_spec.cpp index 9ee4ba01f8..dfe69a3811 100644 --- a/spikes/lk_boost_ut/tests/test_spec.cpp +++ b/tests/functional/tests/boost_ut/test_spec.cpp @@ -2,8 +2,7 @@ // Copyright 2022 APF France handicap // SPDX-License-Identifier: Apache-2.0 -#include "./tests/config.h" -#include "boost/ut.hpp" +#include "tests/config.h" using namespace boost::ut; diff --git a/tests/functional/tests/io_expander/CMakeLists.txt b/tests/functional/tests/io_expander/CMakeLists.txt new file mode 100644 index 0000000000..499dd9130c --- /dev/null +++ b/tests/functional/tests/io_expander/CMakeLists.txt @@ -0,0 +1,17 @@ +# Leka - LekaOS +# Copyright 2022 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +register_functional_test( + TARGET + functional_ut_io_expander + + INCLUDE_DIRECTORIES + + SOURCES + suite_io_expander.cpp + + LINK_LIBRARIES + CoreI2C + CoreIOExpander +) diff --git a/tests/functional/tests/io_expander/suite_io_expander.cpp b/tests/functional/tests/io_expander/suite_io_expander.cpp new file mode 100644 index 0000000000..4552b811ea --- /dev/null +++ b/tests/functional/tests/io_expander/suite_io_expander.cpp @@ -0,0 +1,21 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "CoreI2C.h" +#include "CoreIOExpander.h" +#include "DigitalOut.h" +#include "tests/config.h" + +using namespace leka; +using namespace std::chrono; +using namespace boost::ut; + +suite suite_io_expander = [] { + const uint8_t i2c_address {0x4E}; + auto corei2c = CoreI2C {PinName::SENSOR_PROXIMITY_MUX_I2C_SDA, PinName::SENSOR_PROXIMITY_MUX_I2C_SCL}; + auto io_expander_reset = mbed::DigitalOut {PinName::SENSOR_PROXIMITY_MUX_RESET, 0}; + auto io_expander = CoreIOExpanderMCP23017 {corei2c, io_expander_reset}; + + "Initialization"_test = [&] { expect(neq(&io_expander, nullptr)); }; +}; diff --git a/tests/functional/tests/qdac/CMakeLists.txt b/tests/functional/tests/qdac/CMakeLists.txt new file mode 100644 index 0000000000..1cac30ffef --- /dev/null +++ b/tests/functional/tests/qdac/CMakeLists.txt @@ -0,0 +1,17 @@ +# Leka - LekaOS +# Copyright 2022 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +register_functional_test( + TARGET + functional_ut_qdac + + INCLUDE_DIRECTORIES + + SOURCES + suite_qdac.cpp + + LINK_LIBRARIES + CoreI2C + CoreQDAC +) diff --git a/tests/functional/tests/qdac/suite_qdac.cpp b/tests/functional/tests/qdac/suite_qdac.cpp new file mode 100644 index 0000000000..7270586efe --- /dev/null +++ b/tests/functional/tests/qdac/suite_qdac.cpp @@ -0,0 +1,34 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "CoreI2C.h" +#include "CoreQDAC.h" +#include "tests/config.h" + +using namespace leka; +using namespace std::chrono; +using namespace boost::ut; + +suite suite_qdac = [] { + auto corei2c = CoreI2C {PinName::SENSOR_PROXIMITY_MUX_I2C_SDA, PinName::SENSOR_PROXIMITY_MUX_I2C_SCL}; + auto dac = CoreQDACMCP4728 {corei2c, 0xC2}; + auto data = uint16_t {}; + auto channel = uint8_t {}; + + "read channel A"_test = [&] { + data = 0x0ABC; + channel = 0x00; + dac.write(channel, data); + auto ret = dac.read(channel); + expect(data == ret) << "Failed to read Channel A"; + }; + + "read channel B"_test = [&] { + data = 0x0DEF; + channel = 0x01; + dac.write(channel, data); + auto ret = dac.read(channel); + expect(data == ret) << "Failed to read Channel B"; + }; +}; diff --git a/tools/run_functional_tests.py b/tools/run_functional_tests.py new file mode 100755 index 0000000000..c781043e65 --- /dev/null +++ b/tools/run_functional_tests.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python3 + + +import datetime +import time +from colorama import Fore, Style +import os +import glob +import re +import sys +import argparse + +from time import sleep + +import serial +import serial.tools.list_ports + + +# +# MARK: - argparse +# + +TESTS_FUNCTIONAL_ROOT_DIRECTORY = "_build/LEKA_V1_2_DEV/tests/functional/tests/" +TESTS_BIN_EXTENSION = ".bin" + + +def valid_file(parser, arg): + path = os.path.join(TESTS_FUNCTIONAL_ROOT_DIRECTORY, arg) + if not os.path.exists(path): + parser.error("⚠️ The file %s does not exist!" % arg) + else: + base, ext = os.path.splitext(arg) + if not ext.endswith(TESTS_BIN_EXTENSION): + parser.error( + "⚠️ The filename %s must have a \'.bin\' extension !" % arg) + return path + + +parser = argparse.ArgumentParser(description='Run functional tests') + +parser.add_argument('-p', '--port', metavar='PORT', default='/dev/tty.usbmodem*', + help='serial port path used for the robot') +parser.add_argument('--response-timeout', metavar='RESPONSE_TIMEOUT', default=5.0, + help='response timeout') +parser.add_argument('--flash-erase', action='store_true', + help='disable flash erase') + +group = parser.add_mutually_exclusive_group(required=True) + +group.add_argument('-b', '--bin-files', metavar='BIN_FILES', nargs='+', type=lambda s: valid_file(parser, s), default=list(), + help='list binary executables') + +group.add_argument('--all', action='store_true', + help='select all binary executable') + + +args = parser.parse_args() + +# +# MARK: - Serial +# + +PORTS = glob.glob(args.port) +SERIAL_PORT = PORTS[0] if (len(PORTS) != 0) else args.port + +RESPONSE_TIMEOUT = float(args.response_timeout) # in seconds +SERIAL_TIMEOUT = 0.1 # in seconds + +MAX_GET_LINE_RETRIES = RESPONSE_TIMEOUT / SERIAL_TIMEOUT + +try: + com = serial.Serial(SERIAL_PORT, 115200, timeout=SERIAL_TIMEOUT) +except serial.serialutil.SerialException as error: + print(f"{error}") + parser.print_help() + sys.exit(1) + +print(f"Connected to {com.name}") + + +def read_output_serial(): + return com.readline().decode("utf-8") + + +def wait_for_response(): + data = '' + no_response_counter = 0 + + while (no_response_counter <= MAX_GET_LINE_RETRIES): + sleep(.005) + data = read_output_serial() + if (data): + return data + no_response_counter += 1 + + return None + + +# +# MARK: - Functions +# + +TESTS_FUNCTIONAL_BIN_FILES = list() + + +def list_bin_files(): + set = list() + for root, dirs, files in os.walk(TESTS_FUNCTIONAL_ROOT_DIRECTORY): + for filename in files: + if filename.endswith(TESTS_BIN_EXTENSION): + set.append( + os.path.join(root, filename)) + + return set + + +TESTS_FUNCTIONAL_BIN_FILES = list_bin_files() if args.all else args.bin_files + +FLASH_ERASE_FLAG = args.flash_erase + + +def warningprint(*args, **kwargs): + print(Fore.YELLOW + "\n⚠️ Warning : " + + " ".join(map(str, args))+Style.RESET_ALL, **kwargs) + + +# +# MARK: - Class Test +# + +class Test: + + def __init__(self, path): + self.path = path + + def generate_result_file(self): + def define_path(source_path): + base, ext = os.path.splitext(source_path) + timestamp = time.time() + date = str(datetime.datetime.fromtimestamp( + timestamp)) + date = date.replace(':', '.') + date = date.replace(' ', '_') + new_extension = ".txt" + target_path = base + "_" + date + new_extension + return target_path + + def create_file(path): + try: + file = open(path, "w") + except OSError as e: + print("Could not create or open file: " + path) + print("Error: " + e) + sys.exit(1) + file.close() + + self.result_filepath = define_path(self.path) + create_file(self.result_filepath) + + def edit_result_file(self, data): + result_filepath = self.result_filepath + try: + with open(result_filepath, "a") as file: + file.write(data.strip() + '\n') + except FileNotFoundError as e: + print("The file: " + result_filepath + "doesn\'t exist") + print("Error: " + e) + sys.exit(1) + + def print_result_file(self): + result_filepath = self.result_filepath + try: + with open(result_filepath, "r") as file: + data = file.read() + if (data): + print(data) + else: + warningprint("No data !") + except FileNotFoundError as e: + print("The file: " + result_filepath + "doesn\'t exist") + print("Error: " + e) + sys.exit(1) + + def flash(self): + print(f"Flashing {self.path}...") + CMD_FLASH = (f"openocd -f interface/stlink.cfg " + f"-c 'transport select hla_swd' " + f"-f target/stm32f7x.cfg " + f"-c 'program {self.path} 0x08000000' " + f"-c exit " + f">/dev/null 2>&1 ") + flash = os.system(CMD_FLASH) + + sleep(1) + + CMD_RESET = ("openocd -f interface/stlink.cfg " + "-c 'transport select hla_swd' " + "-f target/stm32f7x.cfg " + "-c init -c 'reset run' " + "-c exit " + f">/dev/null 2>&1 ") + reset = os.system(CMD_RESET) + return flash or reset + + def run(self): + self.generate_result_file() + ret = self.flash() + + if ret: + warningprint("Error flashing !") + return ret + else: + while True: + data = wait_for_response() + if data is not None: + if data.strip() != ".": + self.edit_result_file(data) + else: + return ret + + def check_status(self): + + def all_tests_passed(file): + ploop = (".*All tests passed.+\(\d+ asserts in \d+ tests\)") + pattern = re.compile(ploop) + ret = False + for line in file: + match = pattern.search(line) + if match is not None: + ret = True + break + return ret + + def failure_lines(file): + ploop = (".*\.cpp:[0-9].+:.*FAILED") + pattern = re.compile(ploop) + for line in file: + match = pattern.search(line) + if match is not None: + yield line.strip() + + ret = 0 + result_filepath = self.result_filepath + + try: + with open(result_filepath, "r") as file: + self.failures = list() + for line in failure_lines(file): + self.failures.append(line) + file.seek(0, 0) + if len(self.failures) or not all_tests_passed(file): + print("Failures : " + str(self.failures)) + print("All test passed : "+str(all_tests_passed(file))) + ret = 1 + + except FileNotFoundError as e: + print("The file: " + result_filepath + "doesn\'t exist") + print("Error: " + e) + sys.exit(1) + + return ret + + +def print_summary(): + + if not RUN_TESTS: + warningprint("No available set !") + else: + FAILS = list() + print("\n") + print("Results files :") + for test in RUN_TESTS: + print(test.result_filepath) + fail = test.check_status() + if fail: + FAILS.append(test) + + print("\n") + print("{:<100} {:<7}".format('BIN_PATH', 'STATUS')) + for test in RUN_TESTS: + path = test.path + status = "❌" if test in FAILS else "✅" + print("{:<100} {:^7}".format(path, status)) + + print("\n") + for test in FAILS: + print(Fore.YELLOW + test.result_filepath + Style.RESET_ALL) + test.print_result_file() + + if (FAILS): + print(Fore.RED + " ❌ %d in %d suites have failed..." % (len(FAILS), len(RUN_TESTS)) + + Style.RESET_ALL) + else: + print(Fore.GREEN + " ✅ All the %d suites have passed !" % len(RUN_TESTS) + + Style.RESET_ALL) + + ret = len(FAILS) + return ret + + +# +# MARK: - Main script +# + +RUN_TESTS = list() + + +def reboot_device(): + com.send_break() + REBOOT_SLEEP_DELAY = 3 + sleep(REBOOT_SLEEP_DELAY) + + +def flash_erase(): + ret = os.system("st-flash --debug erase") + return ret + + +def main(): + ret = 0 + + if not TESTS_FUNCTIONAL_BIN_FILES: + warningprint("No exec !") + sys.exit(1) + + if FLASH_ERASE_FLAG: + flash_erase() + + print("Rebooting device...") + reboot_device() + + while True: + if wait_for_response() is not None: + break + + print("Running tests...") + for filepath in TESTS_FUNCTIONAL_BIN_FILES: + test = Test(filepath) + error = test.run() + if not error: + RUN_TESTS.append(test) + + fails = print_summary() + if fails: + ret = 1 + + return ret + + +if __name__ == '__main__': + sys.exit(main())