diff --git a/README.md b/README.md index a050d1e..0811560 100644 --- a/README.md +++ b/README.md @@ -1,137 +1,64 @@ -# Table of contents -- [Test definition](#test-definition) - * [Unit test](#unit-test) - * [Test proprieties](#test-proprieties) - + [Critical test](#critical-test) - + [Long run test](#long-run-test) - + [Test template](#test-template) - * [Test suit](#test-suit) - * [Test setup](#test-setup) - * [Setup test directory](#setup-test-directory) +## 0. Setup test directory -# Test definition -## Unit test -A unit test can be defined to evaluate a procedure (block of code). +The provided directory tree below illustrates an example of how to use the +framework: -## Test proprieties -For each test, the following parameters can be set: - -### Critical test -- True - The test process should be stopped if the test fails -- False - The test process can continue even if the test fails - -### Long run test -- True - The test duration is considered long (long-time run) -- False - The test is considered fast (short-time run - -### Test template -A test definition must follow the outlined nomenclature. - -```C -BAO_TEST(#suite_name, #test_name, #criticality, #long_run) -{ - // test code here -} +```c + MUT + ├── ci + ├── src + ├── tests + │ ├── configs + │ │ ├── test_cfg1.dts + │ │ ├── test_cfg2.dts + │ ├── src + │ │ ├── test_src1.c + │ │ ├── test_src2.c + │ ├── bao-tests (git repository) + │ ├── bao-nix (git repository) ``` -## Test suit -The term "test suite" refers to a group of tests. Grouping the tests enables a more organized selection of tests, making running numerous tests easier. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Suit nameTest nameCritical testLong test
Suit_1
Test_1T/FT/F
Test_2T/FT/F
Test_3T/FT/F
Suit_2
Test_1T/FT/F
Test_2T/FT/F
Test_3T/FT/F
- -## Test setup -In order to use Bao Test Framework you have go throught the following steps: - -1. Add the following lines to your linkerscript - ```c - .testframework : { - __testframework_start = . ; - *(.testframework.PRIO* .testframework.prio*) - *(.testframework.*) - __testframework_end = . ; - } - ``` - -2. Add the following lines to your makefile (use the variable TEST_DIR to indicate the directory where all the file containing tests are. The variable REPO_DIR should point to the base directory of this repository. Additionally, the variables names C_SRC, INC_DIRS, and CFLAGS may have to be modified according to your makefile.) - - ```c - TESTS_DIR:=$(SRC_DIR)/tests - REPO_DIR:=$(SRC_DIR)/../bao-tests - include bao-tests/src/bao-test.mk - C_SRC += $(BAO_TEST_SRCS) - INC_DIRS += $(BAO_TEST_INC_DIR) - CFLAGS += $(BAO_TEST_FLAGS) - ``` -(Note: If you are using the C library without the Python tool, make sure you add also delete the test build directory in your clean rule) - -3. Add to your source the entry point (bao_test_entry()) to our framework, where you feel it is correct. -4. Create a .c file (or multiple) in the directory specified early, include bao_test.h and create tests according to the [test definition example](#Test template). -5. Build your system normally adding the following variable to your make command: +## 1. Get Bao-nix repository +```sh +git clone https://github.com/bao-project/bao-nix.git +``` - ```c - BAO_TEST=1 -> mandatory to use the framework, otherwise no test will be executed - SUITES="suite1 suite2" -> to specify which suites need to be executed - TESTS="test1 test2" -> to specify which tests are to bexecuted - ``` +## 2. Get Bao-tests repository +```sh +git clone https://github.com/bao-project/bao-tests.git +``` -## Setup test directory -The test platform directory provides a template of a VMM, a VM, and a Guest. These templates are available in the BAO_Test/ directory. A test relating to a MUT should be placed in that module's directory. Tests must be grouped into suits (directory name equals to the suit name) inside the module directory. +## 3. How to configure +Configuration of the test framework is performed through a .dts file. This file allows you to define the test recipe, select the target platform, and specify +the tests to be run in a given setup. The configuration file follows this +template: +```dts +/dts-v1/; +/ { + platform = "test-target-platform"; + + test_config { + recipe_test { + nix_file = "test-recipe.nix"; + suites = "list-of-suites"; + tests = "list-of-tests"; + log_level = "verbose-level"; + }; + }; +}; +``` +## 4. How to use -```c -BAO_Test/ -├─ VMM_template -├─ VM_template -├─ VM_Test/ -│ ├─ Suit_1/ -│ │ ├─ Test_1 -│ │ ├─ Test_n -│ ├─ Suit_n/ -│ │ ├─ Test_1 -│ │ ├─ Test_n -├─ VMM_Test/ -├─ Guest_Test/ +After setting up the directory, run the following commands: +```sh +cd /path-to-MUT-dir/tests/bao-tests/framework/ +python3 test_framework.py ``` +Alternatively, you can launch the framework with the following arguments: +```sh +python3 test_framework.py \ + -dts_path /path/to/config.dts \ # config.dts file + -bao_test_src_path /path/to/bao-tests/src \ # bao-tests repo (/src) + -tests_src_path /path/to/tests/src # tests to be executed +``` diff --git a/codegen.py b/framework/codegen.py similarity index 59% rename from codegen.py rename to framework/codegen.py index 3ff1abb..e0be596 100644 --- a/codegen.py +++ b/framework/codegen.py @@ -1,9 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 # Copyright (c) Bao Project and Contributors. All rights reserved -import sys -import argparse -import shutil -import os """ This script is used to generate Bao Project tests code. @@ -11,8 +7,18 @@ corresponding test functions. """ +import sys +import argparse +import shutil +import os def parse_args(): + """ + Function to parse command-line arguments for generating tests code. + + Returns: + args (argparse.Namespace): Parsed command-line arguments. + """ parser = argparse.ArgumentParser(description='Script to parse tests \ sourcesand generate tests code') @@ -29,20 +35,38 @@ def parse_args(): def get_srcs_list(base_dir): + """ + Function to retrieve a list of C source files from the specified directory. + + Args: + base_dir (str): Base directory to search for source files. + + Returns: + list: List of C source file paths. + """ c_srcs = [] - for root, dirs, files in os.walk(base_dir): + for root, _, files in os.walk(base_dir): for file in files: if file.endswith(".c"): c_srcs.append(os.path.join(root, file)) return c_srcs def generate_code(base_dir): + """ + Function to generate code based on specified C source files. + + Args: + base_dir (str): Base directory containing C source files. + + Returns: + str: Generated code. + """ c_files = get_srcs_list(base_dir) tests_list = {} code = "" for file in c_files: - with open(file, "r") as f: - file_code = f.readlines() + with open(file, "r", encoding="utf8") as c_file: + file_code = c_file.readlines() for line in file_code: if "BAO_TEST" in line: @@ -60,11 +84,11 @@ def generate_code(base_dir): else: tests_list[suite_name] = [test_name] - for suite in tests_list.keys(): - for test in tests_list[suite]: - code += "\t#if defined " + test + " || " + suite + "\n" - code += "\tentry_test_" + suite + "_" + test + "();" + "\n" - code += "\t#endif" + "\n" + "\n" + for suite, tests in tests_list.items(): + for test in tests: + code += f"\t#if defined {test} || {suite}\n" + code += f"\tentry_test_{suite}_{test}();\n" + code += "\t#endif\n\n" return code[:-2] @@ -75,29 +99,29 @@ def generate_code(base_dir): tests_code = generate_code(tool_args.base_dir) # Copy template to output directory - template_file = "./template.c" - if not os.path.isfile(template_file): + TEMPLATE_FILE = "../src/template.c" + if not os.path.isfile(TEMPLATE_FILE): print("Template file missing!") sys.exit() - shutil.copy(template_file, tool_args.out_code) + shutil.copy(TEMPLATE_FILE, tool_args.out_code) # Read template - with open(tool_args.out_code, "r") as f: - code = f.readlines() + with open(tool_args.out_code, "r", encoding="utf8") as code_file: + read_code = code_file.readlines() # Get codegen.py writable sections code_sec_begin, code_sec_end = -1, -1 - for index, line in enumerate(code): - if "// codegen.py section begin" in line: + for index, code_line in enumerate(read_code): + if "// codegen.py section begin" in code_line: code_sec_begin = index - if "// codegen.py section end" in line: + if "// codegen.py section end" in code_line: code_sec_end = index # Write generated code to output file - out_code = ''.join(code[0:code_sec_begin+1]) - out_code += tests_code - out_code += ''.join(code[code_sec_end:]) - with open(tool_args.out_code, "w") as f: - f.writelines(out_code) + OUT_CODE = ''.join(read_code[0:code_sec_begin+1]) + OUT_CODE += tests_code + OUT_CODE += ''.join(read_code[code_sec_end:]) + with open(tool_args.out_code, "w", encoding="utf8") as out_file: + out_file.writelines(OUT_CODE) print("Successfully generated bao tests code") diff --git a/framework/test_framework.py b/framework/test_framework.py index 22a8576..d6d0476 100644 --- a/framework/test_framework.py +++ b/framework/test_framework.py @@ -5,10 +5,171 @@ Test framework main file """ import argparse +import os +import sys +import subprocess +import psutil import constants as cons +from pydevicetree import Devicetree -PARSER = argparse.ArgumentParser(description="Bao Testing Framework") +test_config = { + 'nix_file': '', + 'suites': '', + 'tests': '', + 'tests_configs': { + 'log_level': '' + } +} + +def parse_args(): + """ + Parse python script arguments. + """ + parser = argparse.ArgumentParser(description="Bao Testing Framework") + + parser.add_argument("-dts_path", "--dts_path", + help="Path to .dts configuration file", + default="../../configs/config.dts") + + parser.add_argument("-bao_test_src_path", "--bao_test_src_path", + help="Path to bao-test /src dir", + default="../src") + + parser.add_argument("-tests_src_path", "--tests_src_path", + help="Path to bao-test /src dir", + default="../../src") + + input_args = parser.parse_args() + return input_args + +def parse_dts_file(file_path): + """ + Parse a DTS (Device Tree Source) file and extract relevant information. + + Args: + file_path (str): The path to the DTS configuration file. + """ + tree = Devicetree.parseFile(file_path) + test_config['platform'] = \ + tree.children[0].properties[0].values[0] + test_config['nix_file'] = \ + tree.children[0].children[0].children[0].properties[0].values[0] + test_config['suites'] = \ + tree.children[0].children[0].children[0].properties[1].values[0] + test_config['tests'] = \ + tree.children[0].children[0].children[0].properties[2].values[0] + test_config['tests_configs']['log_level'] = \ + tree.children[0].children[0].children[0].properties[2].values[0] + +def run_command_in_terminal(command): + """ + Run a command in a new Terminal window. + + Args: + command (str): The command to execute. + """ + # pylint: disable=R1732 + terminal_process = subprocess.Popen( + ['/bin/bash', '-c', command], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE + ) + # pylint: enable=R1732 + + return terminal_process + +def terminate_children_processes(parent_process): + """ + Terminate all child processes of the given parent process. + + Args: + parent_process: The parent process whose children will be terminated. + """ + parent = psutil.Process(parent_process.pid) + children = parent.children(recursive=True) + for child in children: + child.terminate() + child.wait() + +def deploy_test(platform): + """ + Deploy a test on a specific platform. + + Args: + platform (str): The platform to deploy the test on. + """ + if platform in ["qemu-aarch64-virt", "qemu-riscv64-virt"]: + arch = platform.split("-")[1] + run_cmd = "../platform/qemu/run.sh " + arch + process = run_command_in_terminal(run_cmd) + # Connection to platform will run here + # which will stall the platform untill the results + # are sent from the platform to the framework + terminate_children_processes(process) if __name__ == '__main__': print(cons.BLUE_TEXT + "Framework init..." + cons.RESET_COLOR) + print(cons.BLUE_TEXT + + "Framework init..." + + cons.RESET_COLOR) + + args = parse_args() + + print(cons.BLUE_TEXT + + "Reading config.dts..." + + cons.RESET_COLOR) + + dts_path = args.dts_path + print("config.dts file: " + dts_path) + + if args.dts_path is None: + print(cons.RED_TEXT + + "Error: Please provide the --dts_path argument." + + cons.RESET_COLOR) + else: + dts_path = args.dts_path + + parse_dts_file(dts_path) + print(cons.GREEN_TEXT + "config.dts successfully read!" + cons.RESET_COLOR) + + print(cons.BLUE_TEXT + + "Creating tests source file..." + + cons.RESET_COLOR) + + bao_test_src = args.bao_test_src_path + tests_src = args.tests_src_path + RUN_CMD = "python3 codegen.py -dir " + tests_src + " " + RUN_CMD += "-o " + bao_test_src + "/testf_entry.c" + os.system(RUN_CMD) + + print(cons.BLUE_TEXT + "Running nix build..." + cons.RESET_COLOR) + BUILD_CMD = 'nix-build ../../' + test_config['nix_file'] + list_suites = test_config['suites'].split() + list_tests = test_config['tests'].split() + BUILD_CMD += " --argstr platform " + test_config['platform'] + if len(list_suites): + BUILD_CMD += " --argstr list_suites \"" + for suit in list_suites: + BUILD_CMD += suit + " " + BUILD_CMD = BUILD_CMD[:-1] + "\"" + if len(list_tests): + BUILD_CMD += " --argstr list_tests \"" + for suit in list_tests: + BUILD_CMD += suit + " " + BUILD_CMD = BUILD_CMD[:-1] + "\"" + + print(BUILD_CMD) + res = os.system(BUILD_CMD) + if res==0: + print(cons.GREEN_TEXT + + "nix build successfully completed..." + + cons.RESET_COLOR) + + else: + print(cons.RED_TEXT + + "nix build failed..." + + cons.RESET_COLOR) + sys.exit(-1) + + print(cons.BLUE_TEXT + "Launching QEMU..." + cons.RESET_COLOR) + deploy_test(test_config['platform']) diff --git a/template.c b/src/template.c similarity index 52% rename from template.c rename to src/template.c index 6e0da79..046f631 100644 --- a/template.c +++ b/src/template.c @@ -1,8 +1,8 @@ /* -* Copyright (c) Bao Project and Contributors. All rights reserved -* -* SPDX-License-Identifier: Apache-2.0 -*/ + * Copyright (c) Bao Project and Contributors. All rights reserved + * + * SPDX-License-Identifier: Apache-2.0 + */ #include "testf.h" #include @@ -11,7 +11,6 @@ unsigned int testframework_tests; unsigned int testframework_fails; - void testf_entry(void) { // codegen.py section begin @@ -19,10 +18,10 @@ void testf_entry(void) // codegen.py section end if (testframework_tests > 0) { - LOG_TESTS(); - } else { - INFO_TAG(); - printf("No tests were executed!\n"); - } - return; + LOG_TESTS(); + } else { + INFO_TAG(); + printf("No tests were executed!\n"); + } + return; }