diff --git a/.gitignore b/.gitignore index 663e8294..4ca12179 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,7 @@ host_usb_mixer_control/xmos_mixer **/.vscode/** **.egg-info *tests/logs/* +midi_tx_cmds.txt +midi_rx_cmds.txt +trace.txt +tests/xua_unit_tests/src.runners diff --git a/Jenkinsfile b/Jenkinsfile index 3479f03c..5ab20179 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,4 +1,4 @@ -@Library('xmos_jenkins_shared_library@v0.24.0') _ +@Library('xmos_jenkins_shared_library@v0.27.0') _ getApproval() @@ -7,6 +7,7 @@ pipeline { environment { REPO = 'lib_xua' VIEW = getViewName(REPO) + TOOLS_VERSION = "15.2.1" // For unit tests } options { skipDefaultCheckout() @@ -35,7 +36,8 @@ pipeline { dir("${REPO}/tests"){ viewEnv(){ withVenv{ - runPytest('--numprocesses=4') + sh "xmake -C test_midi -j" // Xdist does not like building so do here + runPytest('--numprocesses=auto -vvv') } } } @@ -43,15 +45,14 @@ pipeline { } stage('Unity tests') { steps { - dir("${REPO}") { - dir('tests') { - dir('xua_unit_tests') { - withVenv { - runWaf('.', "configure clean build --target=xcore200") - viewEnv() { - runPython("TARGET=XCORE200 pytest -s --junitxml=pytest_unity.xml") - junit "pytest_unity.xml" - } + dir("${REPO}/tests/xua_unit_tests") { + withTools("${env.TOOLS_VERSION}") { + withVenv { + withEnv(["XMOS_CMAKE_PATH=${WORKSPACE}/xcommon_cmake"]) { + sh "cmake -G 'Unix Makefiles' -B build" + sh 'xmake -C build -j' + runPython("pytest -s --junitxml=pytest_unity.xml") + junit "pytest_unity.xml" } } } diff --git a/lib_xua/src/midi/midiinparse.h b/lib_xua/src/midi/midiinparse.h index 257820fd..4b55f872 100644 --- a/lib_xua/src/midi/midiinparse.h +++ b/lib_xua/src/midi/midiinparse.h @@ -1,4 +1,4 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2024 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef MIDIINPARSE_XH #define MIDIINPARSE_XH @@ -19,8 +19,11 @@ struct midi_in_parse_state { unsigned codeIndexNumber; }; -void dump_midi_in_parse_state(struct midi_in_parse_state &s); + +#ifdef __XC__ void reset_midi_state(struct midi_in_parse_state &mips); +void dump_midi_in_parse_state(struct midi_in_parse_state &s); {unsigned int , unsigned int} midi_in_parse(struct midi_in_parse_state &mips, unsigned cable_number, unsigned char b); +#endif #endif diff --git a/lib_xua/src/midi/queue.h b/lib_xua/src/midi/queue.h index af67e6e5..3e51b328 100644 --- a/lib_xua/src/midi/queue.h +++ b/lib_xua/src/midi/queue.h @@ -1,4 +1,4 @@ -// Copyright 2013-2021 XMOS LIMITED. +// Copyright 2013-2024 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef QUEUE_H_ #define QUEUE_H_ @@ -14,6 +14,8 @@ typedef struct queue_t { unsigned mask; } queue_t; +#ifdef __XC__ + inline int is_power_of_2(unsigned x) { return x != 0 && (x & (x - 1)) == 0; } @@ -64,4 +66,6 @@ inline unsigned queue_space(const queue_t &q) { return q.size - queue_items(q); } +#endif // __XC__ + #endif /* QUEUE_H_ */ diff --git a/tests/conftest.py b/tests/conftest.py index 3117626c..f0845cc9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,11 @@ -# Copyright 2022-2023 XMOS LIMITED. +# Copyright 2022-2024 XMOS LIMITED. # This Software is subject to the terms of the XMOS Public Licence: Version 1. import pytest import time - +import Pyxsim +from pathlib import Path +from midi_test_helpers import MIDI_TEST_CONFIGS +import subprocess @pytest.fixture() def test_file(request): @@ -39,3 +42,16 @@ def pytest_addoption(parser): @pytest.fixture def options(request): yield request.config.option + +# We use the same binary multiple times so just build once for all MIDI tests +@pytest.fixture(scope="session") +def build_midi(): + cmd = "xmake -C test_midi -j" + # result = subprocess.run(cmd, capture_output=True, text=True, shell=True) + # return_code = result.returncode + return_code = 0 + + assert return_code == 0, f"{result.stderr}\n{result.stdout}" + + return str(Path(__file__).parent / f"test_midi/bin/") + diff --git a/tests/midi_test_helpers.py b/tests/midi_test_helpers.py new file mode 100644 index 00000000..ab612d15 --- /dev/null +++ b/tests/midi_test_helpers.py @@ -0,0 +1,81 @@ +# Copyright 2024 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. + +import contextlib +import os +import shutil +import tempfile + +MIDI_TEST_CONFIGS = ["xs2", "xs3"] +MIDI_RATE = 31250 + +@contextlib.contextmanager +def cd(newdir, cleanup=lambda: True): + prevdir = os.getcwd() + os.chdir(os.path.expanduser(newdir)) + try: + yield + finally: + os.chdir(prevdir) + cleanup() + +@contextlib.contextmanager +def tempdir(): + dirpath = tempfile.mkdtemp(dir=os.getcwd()) + def cleanup(): + shutil.rmtree(dirpath) + with cd(dirpath, cleanup): + yield dirpath + +class midi_expect_tx: + def __init(self): + pass + + def expect(self, commands): + expected = "" + for command in commands: + while len(command) < 3: + command.append(0) + expected += "uart_tx_checker: " + " ".join([f"0x{byte:02x}" for byte in command]) + "\n" + + return expected + "\n" + +class midi_expect_rx: + def __init(self): + pass + + def expect(self, commands): + expected = "" + for command in commands: + while len(command) < 3: + command.append(0) + expected += "dut_midi_rx: " + " ".join([f"{byte}" for byte in command]) + "\n" + + return expected + "\n" + +midi_tx_file = "midi_tx_cmds.txt" +midi_rx_file = "midi_rx_cmds.txt" + +def create_midi_tx_file(commands=None): + with open(midi_tx_file, "wt") as mt: + if commands is None: + return + for command in commands: + while len(command) < 3: + command.append(0) + text = " ".join([str(byte) for byte in command]) + "\n" + mt.write(text) + +def create_midi_rx_file(num_commands=0): + with open(midi_rx_file, "wt") as mr: + text = f"{num_commands}\n" + mr.write(text) + + + +# Test/dev only +if __name__ == "__main__": + with tempdir() as td: + print(td) + create_midi_tx_file() + input("PRESS ENTER TO CONTINUE") diff --git a/tests/test_midi/Makefile b/tests/test_midi/Makefile new file mode 100644 index 00000000..66ef5e05 --- /dev/null +++ b/tests/test_midi/Makefile @@ -0,0 +1,37 @@ +# The TARGET variable determines what target system the application is +# compiled for. It either refers to an XN file in the source directories +# or a valid argument for the --target option when compiling. + +ifeq ($(CONFIG), xs2) + TARGET = XCORE-200-EXPLORER +else + TARGET = XCORE-AI-EXPLORER #for xs3 and also loopback test +endif + +# The APP_NAME variable determines the name of the final .xe file. It should +# not include the .xe postfix. If left blank the name will default to +# the project name + +APP_NAME = + +# The flags passed to xcc when building the application +# You can also set the following to override flags for a particular language: +# +# XCC_XC_FLAGS, XCC_C_FLAGS, XCC_ASM_FLAGS, XCC_CPP_FLAGS +# +# If the variable XCC_MAP_FLAGS is set it overrides the flags passed to +# xcc for the final link (mapping) stage. +XCC_FLAGS_xs2 = $(EXTRA_BUILD_FLAGS) -O2 -g +XCC_FLAGS_xs3 = $(EXTRA_BUILD_FLAGS) -O2 -g +XCC_FLAGS_LOOPBACK = $(EXTRA_BUILD_FLAGS) -O2 -g -DMIDI_LOOPBACK=1 + +# The USED_MODULES variable lists other module used by the application. +USED_MODULES = lib_xua + + +#============================================================================= +# The following part of the Makefile includes the common build infrastructure +# for compiling XMOS applications. You should not need to edit below here. + +XMOS_MAKE_PATH ?= ../.. +include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common \ No newline at end of file diff --git a/tests/test_midi/src/app_midi_simple.xc b/tests/test_midi/src/app_midi_simple.xc new file mode 100644 index 00000000..ac4bc7df --- /dev/null +++ b/tests/test_midi/src/app_midi_simple.xc @@ -0,0 +1,186 @@ +// Copyright 2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/* A very simple *example* of a USB audio application (and as such is un-verified for production) + * + * It uses the main blocks from the lib_xua + * + * - 2 channels out I2S only + * - No DFU + * - I2S only + * + */ + +#include +#include +#include +#include +#include +#include + +#include "xua.h" +#include "xud_device.h" +#include "midiinparse.h" +#include "midioutparse.h" + +on tile[MIDI_TILE] : port p_midi_tx = XS1_PORT_4C; +#if(MIDI_RX_PORT_WIDTH == 4) +on tile[MIDI_TILE] : buffered in port:4 p_midi_rx = XS1_PORT_1F; +#elif(MIDI_RX_PORT_WIDTH == 1) +on tile[MIDI_TILE] : buffered in port:1 p_midi_rx = XS1_PORT_1F; +#endif +#define CLKBLK_MIDI XS1_CLKBLK_2 +on tile[MIDI_TILE] : clock clk_midi = CLKBLK_MIDI; + +#define MAX_TEST_COMMANDS 100 +#define TEST_COMMAND_FILE_TX "midi_tx_cmds.txt" +#define TEST_COMMAND_FILE_RX "midi_rx_cmds.txt" + +#define DEBUG 0 // Prints for debugging. Turn off for actual test + +#if DEBUG +#define dprintf(...) printf(__VA_ARGS__) +#else +#define dprintf(...) +#endif + +/* See hwsupport.xc */ +void board_setup(); + +#define CABLE_NUM 0 + + +unsigned midi_in_parse_helper(unsigned midi[3]){ + struct midi_in_parse_state m_state; + reset_midi_state(m_state); + + unsigned valid = 0; + unsigned packed = 0; + + for(int i = 0; i < 3; i++){ + dprintf("Packing byte %d 0x%x\n", i, midi[i]); + {valid, packed} = midi_in_parse(m_state, CABLE_NUM, midi[i]); + if(valid){ + return packed; + } + } + return 0; +} + +{unsigned, unsigned} read_config_file(uint8_t commands[MAX_TEST_COMMANDS][3]) +{ + unsigned tx_line_count = 0; + + FILE * movable fptr_tx = fopen(TEST_COMMAND_FILE_TX,"rt"); + if (fptr_tx == NULL) { + dprintf("WARNING: TX command file %s not found or unable to open.\n", TEST_COMMAND_FILE_TX); + } else { + unsigned a,b,c; + while (fscanf(fptr_tx, "%u %u %u\n", &a, &b, &c) == 3) { + commands[tx_line_count][0] = a; + commands[tx_line_count][1] = b; + commands[tx_line_count][2] = c; + //printf("Line %u params: 0x%x 0x%x 0x%x\n", tx_line_count, commands[tx_line_count][0], commands[tx_line_count][1], commands[tx_line_count][2]); + tx_line_count++; + if(tx_line_count > MAX_TEST_COMMANDS){ + printf("ERROR: Too many lines in TX command file\n"); + tx_line_count = MAX_TEST_COMMANDS; + } + } + } + fclose(move(fptr_tx)); + + + unsigned rx_cmd_count = 0; + + FILE * movable fptr_rx = fopen(TEST_COMMAND_FILE_RX,"rt"); + if (fptr_rx == NULL) { + dprintf("WARNING: RX command file %s not found or unable to open.\n", TEST_COMMAND_FILE_RX); + } else { + if(fscanf(fptr_rx, "%u\n", &rx_cmd_count) != 1){ + printf("ERROR: Not enough or too many items in RX command file line\n"); + } + } + fclose(move(fptr_rx)); + + return {tx_line_count, rx_cmd_count}; +} + +void test(chanend c_midi){ + uint8_t commands[MAX_TEST_COMMANDS][3] = {{0}}; + unsigned num_to_tx = 0; + unsigned num_to_rx = 0; + {num_to_tx, num_to_rx} = read_config_file(commands); + dprintf("Sending %u MIDI command line(s) and receiving %u MIDI command(s)\n", num_to_tx, num_to_rx); + + // For MIDI Rx + int is_ack; + unsigned rx_packet; + + // Counters for Rx and Tx + unsigned tx_cmd_count = 0; + unsigned rx_cmd_count = 0; + + timer tmr; + + int t_tx; // Used for delay between Txs + int tx_end; // Used to wait for packet to have fully left + tmr :> t_tx; + tmr :> tx_end; + + const int max_tx_time = XS1_TIMER_HZ / 31250 * 3 * (8 + 1 + 1); // 30 bits at 31.25 kbps is 0.96ms + const int tx_interval = XS1_TIMER_HZ / 8000; // SoF rate on HS + tx_end += max_tx_time; // One whole packet + + while(tx_cmd_count < num_to_tx || rx_cmd_count < num_to_rx ){ + select{ + case midi_get_ack_or_data(c_midi, is_ack, rx_packet): + if(is_ack){ + dprintf("ACK from Tx\n"); + tx_cmd_count++; + } else { + unsigned midi_data[3] = {0}; + unsigned byte_count = 0; + {midi_data[0], midi_data[1], midi_data[2], byte_count} = midi_out_parse(byterev(rx_packet)); + // Note this needs to always print for capfd in pytest to pick it up + printf("dut_midi_rx: %u %u %u\n", midi_data[0], midi_data[1], midi_data[2]); + rx_cmd_count++; + midi_send_ack(c_midi); + } + break; + + case tx_cmd_count < num_to_tx => tmr when timerafter(t_tx) :> int _: + unsigned midi[] = {commands[tx_cmd_count][0], commands[tx_cmd_count][1], commands[tx_cmd_count][2]}; + unsigned tx_packet = midi_in_parse_helper(midi); + outuint(c_midi, byterev(tx_packet)); + dprintf("Sent packet to midi: %u %u %u\n", commands[tx_cmd_count][0], commands[tx_cmd_count][1], commands[tx_cmd_count][2]); + t_tx += tx_interval; + tx_end += max_tx_time; + break; + } + } + + dprintf("Tx and Rx count met - exiting after last tx complete.\n"); + tmr when timerafter(tx_end) :> int _; // wait until packet definitely departed + + exit(0); +} + + +int main(void) +{ + chan c_midi; + + par + { + on tile[0]: test(c_midi); + on tile[1]: usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0); + + // Setup HW so we can run this on the MC board + on tile[0]: board_setup(); + } + + return 0; +} + + diff --git a/tests/test_midi/src/hwsupport.xc b/tests/test_midi/src/hwsupport.xc new file mode 100644 index 00000000..02b36a6b --- /dev/null +++ b/tests/test_midi/src/hwsupport.xc @@ -0,0 +1,47 @@ +// Copyright 2017-2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include +#include "xua.h" + + +on tile[0]: out port p_ctrl = XS1_PORT_8D; /* p_ctrl: + * [0:3] - Unused + * [4] - EN_3v3_N (1v0 hardware only) + * [5] - EN_3v3A + * [6] - EXT_PLL_SEL (CS2100:0, SI: 1) + * [7] - MCLK_DIR (Out:0, In: 1) + */ + +#define USE_FRACTIONAL_N (0) + +#if (USE_FRACTIONAL_N) +#define EXT_PLL_SEL__MCLK_DIR (0x00) +#else +#define EXT_PLL_SEL__MCLK_DIR (0x80) +#endif + +/* Board setup for XU316 MC Audio (1v1) */ +void board_setup() +{ + /* "Drive high mode" - drive high for 1, non-driving for 0 */ + set_port_drive_high(p_ctrl); + + /* Drive control port to turn on 3V3 and mclk direction appropriately. + * Bits set to low will be high-z, pulled down */ + p_ctrl <: EXT_PLL_SEL__MCLK_DIR | 0x20; + + /* Wait for power supplies to be up and stable */ + delay_milliseconds(10); +} + +/* Configures the external audio hardware at startup. Note this runs on Tile[1] */ +void AudioHwInit() +{ +} + +/* Configures the external audio hardware for the required sample frequency */ +void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) +{ +} + diff --git a/tests/test_midi/src/xua_conf.h b/tests/test_midi/src/xua_conf.h new file mode 100644 index 00000000..b2042156 --- /dev/null +++ b/tests/test_midi/src/xua_conf.h @@ -0,0 +1,28 @@ +// Copyright 2017-2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#ifndef _XUA_CONF_H_ +#define _XUA_CONF_H_ + +#define NUM_USB_CHAN_OUT 2 /* Number of channels from host to device */ +#define NUM_USB_CHAN_IN 0 /* Number of channels from device to host */ +#define I2S_CHANS_DAC 2 /* Number of I2S channels out of xCORE */ +#define I2S_CHANS_ADC 0 /* Number of I2S channels in to xCORE */ +#define MCLK_441 (512 * 44100) /* 44.1kHz family master clock frequency */ +#define MCLK_48 (512 * 48000) /* 48kHz family master clock frequency */ +#define MIN_FREQ 48000 /* Minimum sample rate */ +#define MAX_FREQ 48000 /* Maximum sample rate */ + +#define EXCLUDE_USB_AUDIO_MAIN + +#define MIDI 1 +#define MIDI_TILE 1 +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "XUA Example" +#define PRODUCT_STR_A1 "XUA Example" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define XUA_DFU_EN 0 /* Disable DFU (for simplicity of example */ + +#endif diff --git a/tests/test_midi/src/xud_conf.h b/tests/test_midi/src/xud_conf.h new file mode 100644 index 00000000..374cbe34 --- /dev/null +++ b/tests/test_midi/src/xud_conf.h @@ -0,0 +1,8 @@ +// Copyright 2017-2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#include "xua_conf.h" + +/* TODO */ +#define XUD_UAC_NUM_USB_CHAN_OUT NUM_USB_CHAN_OUT +#define XUD_UAC_NUM_USB_CHAN_IN NUM_USB_CHAN_IN diff --git a/tests/test_midi_loopback.py b/tests/test_midi_loopback.py new file mode 100644 index 00000000..4a21a753 --- /dev/null +++ b/tests/test_midi_loopback.py @@ -0,0 +1,60 @@ +# Copyright 2014-2024 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. + +import pytest +import Pyxsim +from Pyxsim import testers +from pathlib import Path +from uart_rx_checker import UARTRxChecker +from midi_test_helpers import midi_expect_rx, create_midi_rx_file, create_midi_tx_file, tempdir, MIDI_RATE +from distutils.dir_util import copy_tree # we're using python 3.7 and dirs_exist_ok=True isn't available until 3.8 :( + +MAX_CYCLES = 15000000 + + +##### +# This test takes the built binary, copies it to a tmp dir and runs the midi loopback test which sends some commands +# the firmware receives them, prints and compares with the expected output +##### +def test_midi_loopback(capfd, build_midi): + # Need tempdir as we use the same config files and this causes issues when using xdist + with tempdir() as tmpdirname: + config = "LOOPBACK" + copy_tree(build_midi, tmpdirname) + xe = str(Path(tmpdirname) / f"{config}/test_midi_{config}.xe") + + midi_commands = [ + [0x90, 60, 81], #note on + [0xc0, 15], #instr select + [0xe0, 0, 96], #pitch bend + [0xff], #MIDI reset + [0x80, 60, 81], #note off + ] + create_midi_rx_file(len(midi_commands)) + create_midi_tx_file(midi_commands) + + expected = midi_expect_rx().expect(midi_commands) + tester = testers.ComparisonTester(expected, ordered = True) + + simthreads = [] + + simargs = ["--max-cycles", str(MAX_CYCLES)] + #This is just for local debug so we can capture the traces if needed. It slows xsim down so not needed + # simargs.extend(["--trace-to", "trace.txt", "--vcd-tracing", "-tile tile[1] -ports -o trace.vcd"]) + + Pyxsim.run_with_pyxsim( + xe, + simthreads=simthreads, + timeout=120, + simargs=simargs, + ) + capture = capfd.readouterr().out + result = tester.run(capture.split("\n")) + + # Print to console + # with capfd.disabled(): + # print("++++", capture, "++++") + # print("----", expected, "----") + + + assert result \ No newline at end of file diff --git a/tests/test_midi_rx.py b/tests/test_midi_rx.py new file mode 100644 index 00000000..1997dd73 --- /dev/null +++ b/tests/test_midi_rx.py @@ -0,0 +1,61 @@ +# Copyright 2014-2024 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. + +import pytest +import Pyxsim +from Pyxsim import testers +from pathlib import Path +from uart_rx_checker import UARTRxChecker +from midi_test_helpers import midi_expect_rx, create_midi_rx_file, create_midi_tx_file, tempdir, MIDI_TEST_CONFIGS, MIDI_RATE +from distutils.dir_util import copy_tree # we're using python 3.7 and dirs_exist_ok=True isn't available until 3.8 :( + +MAX_CYCLES = 15000000 + + +##### +# This test takes the built binary, copies it to a tmp dir and runs the midi Rx test which sends some commands +# to using the UARTRX checker and the firmware receives them +##### +@pytest.mark.parametrize("config", MIDI_TEST_CONFIGS) +def test_rx(capfd, config, build_midi): + # Need tempdir as we use the same config files and this causes issues when using xdist + with tempdir() as tmpdirname: + copy_tree(build_midi, tmpdirname) + xe = str(Path(tmpdirname) / f"{config}/test_midi_{config}.xe") + + midi_commands = [[0x90, 60, 81]] + create_midi_rx_file(len(midi_commands)) + create_midi_tx_file() + + expected = midi_expect_rx().expect(midi_commands) + tester = testers.ComparisonTester(expected, ordered = True) + + rx_port = "tile[1]:XS1_PORT_1F" + tx_port = "tile[1]:XS1_PORT_4C" # Needed so that UARTRxChecker (a transmitter) knows when to start + baud = MIDI_RATE + bpb = 8 + parity = 0 + stop = 1 + + midi_commands_flattened = [item for row in midi_commands for item in row] + # midi_commands_flattened.append(0x00) # send a null afterwards to give RXChecker to complete + + simthreads = [ + UARTRxChecker(tx_port, rx_port, parity, baud, stop, bpb, midi_commands_flattened, debug=False) + ] + + simargs = ["--max-cycles", str(MAX_CYCLES)] + #This is just for local debug so we can capture the traces if needed. It slows xsim down so not good for Jenkins + # simargs.extend(["--trace-to", "trace.txt", "--vcd-tracing", "-tile tile[1] -ports -o trace.vcd"]) + + # with capfd.disabled(): # use to see xsim and tester output + Pyxsim.run_with_pyxsim( + xe, + simthreads=simthreads, + timeout=120, + simargs=simargs, + ) + capture = capfd.readouterr().out + result = tester.run(capture.split("\n")) + + assert result, f"expected: {expected}\n capture: {capture}" \ No newline at end of file diff --git a/tests/test_midi_tx.py b/tests/test_midi_tx.py new file mode 100644 index 00000000..ade525d6 --- /dev/null +++ b/tests/test_midi_tx.py @@ -0,0 +1,59 @@ +# Copyright 2014-2024 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. + +import pytest +import Pyxsim +from Pyxsim import testers +from pathlib import Path +from uart_tx_checker import UARTTxChecker +from midi_test_helpers import midi_expect_tx, create_midi_tx_file, create_midi_rx_file, tempdir, MIDI_TEST_CONFIGS, MIDI_RATE +from distutils.dir_util import copy_tree # we're using python 3.7 and dirs_exist_ok=True isn't available until 3.8 :( + +MAX_CYCLES = 15000000 + +##### +# This test takes the built binary, copies it to a tmp dir and runs the midi Tx test which sends some commands +# to the firmware and then receives them using the UARTTX checker +##### +@pytest.mark.parametrize("config", MIDI_TEST_CONFIGS) +def test_tx(capfd, config, build_midi): + + # Need tempdir as we use the same config files and this causes issues when using xdist + with tempdir() as tmpdirname: + copy_tree(build_midi, tmpdirname) + xe = str(Path(tmpdirname) / f"{config}/test_midi_{config}.xe") + + midi_commands = [[0x90, 60, 81]] + create_midi_tx_file(midi_commands) + create_midi_rx_file() + + expected = midi_expect_tx().expect(midi_commands) + tester = testers.ComparisonTester(expected, ordered = True) + + tx_port = "tile[1]:XS1_PORT_4C" + baud = MIDI_RATE + bpb = 8 + parity = 0 + stop = 1 + length_of_test = sum(len(cmd) for cmd in midi_commands) + + simthreads = [ + UARTTxChecker(tx_port, parity, baud, length_of_test, stop, bpb, debug=False) + ] + + + simargs = ["--max-cycles", str(MAX_CYCLES)] + #This is just for local debug so we can capture the traces if needed. It slows xsim down so not needed + # simargs.extend(["--trace-to", "trace.txt", "--vcd-tracing", "-tile tile[1] -ports -o trace.vcd"]) + + # with capfd.disabled(): # use to see xsim and tester output + Pyxsim.run_with_pyxsim( + xe, + simthreads=simthreads, + timeout=120, + simargs=simargs, + ) + capture = capfd.readouterr().out + result = tester.run(capture.split("\n")) + + assert result, f"expected: {expected}\n capture: {capture}" \ No newline at end of file diff --git a/tests/uart_rx_checker.py b/tests/uart_rx_checker.py new file mode 100644 index 00000000..79f57f27 --- /dev/null +++ b/tests/uart_rx_checker.py @@ -0,0 +1,178 @@ +# Copyright 2022-2024 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import Pyxsim as px +from typing import Sequence +from functools import partial + +# We need to disable output buffering for this test to work on MacOS; this has +# no effect on Linux systems. Let's redefine print once to avoid putting the +# same argument everywhere. +print = partial(print, flush=True) + +Parity = dict( + UART_PARITY_EVEN=0, + UART_PARITY_ODD=1, + UART_PARITY_NONE=2, + UART_PARITY_BAD=3 +) + +# From tools 15.2.1 we need to add an extra factor to go from ps to fs +time_scaling_factor = 1000 + +class DriveHigh(px.SimThread): + def __init__(self, p): + self._p = p + + def run(self): + xsi = self.xsi + + xsi.drive_port_pins(self._p, 1); + + +class UARTRxChecker(px.SimThread): + def __init__(self, tx_port, rx_port, parity, baud, stop_bits, bpb, data=[0x7f, 0x00, 0x2f, 0xff], + intermittent=False, debug=False): + """ + Create a UARTRxChecker instance. + + :param rx_port: Receive port of the UART device under test. + :param parity: Parity of the UART connection. + :param baud: BAUD rate of the UART connection. + :param stop_bits: Number of stop_bits for each UART byte. + :param bpb: Number of data bits per "byte" of UART data. + :param data: A list of bytes to send (default: [0x7f, 0x00, 0x2f, 0xff]) + :param intermittent: Add a random delay between sent bytes. + """ + self._tx_port = tx_port + self._rx_port = rx_port + self._parity = parity + self._baud = baud + self._stop_bits = stop_bits + self._bits_per_byte = bpb + self._data = data + self._intermittent = intermittent + # Hex value of stop bits, as MSB 1st char, e.g. 0b11 : 0xC0 + + def send_byte(self, xsi, byte): + """ + Send a byte to the rx_port + + :param xsi: XMOS Simulator Instance. + :param byte: Byte to send + """ + # Send start bit + self.send_start(xsi) + + # Send data + self.send_data(xsi, byte) + + # Send parity + self.send_parity(xsi, byte) + + # Send stop bit(s) + self.send_stop(xsi) + + + def send_start(self, xsi): + """ + Send a start bit. + + :param xsi: XMOS Simulator Instance. + """ + xsi.drive_port_pins(self._rx_port, 0) + self.wait_baud_time(xsi) + + def send_data(self, xsi, byte): + """ + Write the data bits to the rx_port + + :param xsi: XMOS Simulator Instance. + :param byte: Data to send. + """ + # print(f"Checker sent 0x{byte:02x}") + for x in range(self._bits_per_byte): + # print(f" Sending bit {x}") + xsi.drive_port_pins(self._rx_port, (byte & (0x01 << x)) >= 1) + # print(f" (x): {((byte & (0x01 << x))>=1)}") + self.wait_baud_time(xsi) + + def send_parity(self, xsi, byte): + """ + Send the parity bit to the rx_port + + :param xsi: XMOS Simulator Instance. + :param byte: Data to send parity of. + """ + parity = (self._parity - 1) % 3 #parity enum in lib_uart (old XC) different from SDK + if parity < 2: + crc_sum = 0 + for x in range(self._bits_per_byte): + crc_sum += ((byte & (0x01 << x)) >= 1) + crc_sum += parity + # print "Parity for 0x%02x: %d" % (byte, crc_sum%2) + xsi.drive_port_pins(self._rx_port, crc_sum % 2) + self.wait_baud_time(xsi) + elif parity == Parity['UART_PARITY_BAD']: + # print "Sending bad parity bit" + self.send_bad_parity(xsi) + + def send_stop(self, xsi): + """ + Send the stop bit(s) to the rx_port + + :param xsi: XMOS Simulator Instance. + """ + for x in range(self._stop_bits): + xsi.drive_port_pins(self._rx_port, 1) + self.wait_baud_time(xsi) + + def send_bad_parity(self, xsi): + """ + Send a parity bit of 1 to simulate an incorrect parity state. + + :param xsi: XMOS Simulator Instance. + """ + # Always send a parity bit of 1 + xsi.drive_port_pins(self._rx_port, 0) + self.wait_baud_time(xsi) + + def get_bit_time(self): + """ + Returns the expected time between bits for the currently set BAUD rate. + + Returns float value in nanoseconds. + """ + # Return float value in ps + return (1.0 / self._baud) * 1e12 * time_scaling_factor + + def wait_baud_time(self, xsi): + """ + Wait for 1 bit time, as determined by the baud rate. + """ + self.wait_until(xsi.get_time() + self.get_bit_time()) + + def wait_half_baud_time(self, xsi): + """ + Wait for half a bit time, as determined by the baud rate. + """ + self.wait_until(xsi.get_time() + (self.get_bit_time() / 2)) + + def run(self): + xsi = self.xsi + # Drive the uart line high. + xsi.drive_port_pins(self._rx_port, 1) + + # Wait for the device to bring up it's tx port, indicating it is ready + self.wait((lambda _x: self.xsi.is_port_driving(self._tx_port))) + + # If we're doing an intermittent send, add a delay between each byte + # sent. Delay is in ns. 20,000ns = 20ms, 100,000ns = 100ms. Delays could + # be more variable, but it hurts test time substantially. + if self._intermittent: + for x in self._data: + k = randint(20000, 100000) + self.wait_until(xsi.get_time() + k) + self.send_byte(xsi, x) + else: + for x in self._data: + self.send_byte(xsi, x) diff --git a/tests/uart_tx_checker.py b/tests/uart_tx_checker.py new file mode 100644 index 00000000..e0476c76 --- /dev/null +++ b/tests/uart_tx_checker.py @@ -0,0 +1,246 @@ +# Copyright 2022-2024 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import Pyxsim as px +from typing import Sequence +from functools import partial + +# We need to disable output buffering for this test to work on MacOS; this has +# no effect on Linux systems. Let's redefine print once to avoid putting the +# same argument everywhere. +print = partial(print, flush=True) + +# From tools 15.2.1 we need to add an extra factor to go from ps to fs +time_scaling_factor = 1000 + +class UARTTxChecker(px.SimThread): + """ + This simulator thread will act as a UART device, and will check sent and + transations caused by the device, by looking at the tx pins. + """ + + def __init__(self, tx_port, parity, baud, length, stop_bits, bpb, debug=False): + """ + Create a UARTTxChecker instance. + + :param tx_port: Transmit port of the UART device under test. + :param parity: Parity of the UART connection. + :param baud: BAUD rate of the UART connection. + :param length: Length of transmission to check. + :param stop_bits: Number of stop_bits for each UART byte. + :param bpb: Number of data bits per "byte" of UART data. + """ + self._tx_port = tx_port + self._parity = parity + self._baud = baud + self._length = length + self._stop_bits = stop_bits + self._bits_per_byte = bpb + # Hex value of stop bits, as MSB 1st char, e.g. 0b11 : 0xC0 + self.debug = debug + + def get_port_val(self, xsi, port): + """ + Sample the state of a port + + :rtype: int + :param xsi: XMOS Simulator Instance. + :param port: Port to sample. + """ + is_driving = xsi.is_port_driving(port) + if not is_driving: + return 1 + else: + return xsi.sample_port_pins(port) + + def get_bit_time(self): + """ + Returns the expected time between bits for the currently set BAUD rate. + + Returns float value in nanoseconds. + :rtype: float + """ + # Return float value in ps + return (1.0/self._baud) * 1e12 * time_scaling_factor + + def wait_baud_time(self, xsi): + """ + Wait for 1 bit time, as determined by the baud rate. + """ + self.wait_until(xsi.get_time() + self.get_bit_time()) + return True + + def wait_half_baud_time(self, xsi): + """ + Wait for half a bit time, as determined by the baud rate. + """ + self.wait_until(xsi.get_time() + (self.get_bit_time() / 2)) + + def read_packet(self, xsi, parity, length=4): + """ + Read a given number of bytes of UART traffic sent by the device. + + Returns a list of bytes sent by the device. + + :rtype: list + :param xsi: XMOS Simulator Instance. + :param parity: The UART partiy setting. See Parity. + :param length: The number of bytes to read. Defaults to 4. + """ + packet = [] + start_time = 0 + got_start_bit = False + + initial_port_val = self.get_port_val(xsi, self._tx_port) + if self.debug: print("tx starts high: %s" % ("True" if initial_port_val else "False")) + + for x in range(length): + packet.append(chr(self.read_byte(xsi, parity))) + return packet + + def read_byte(self, xsi, parity): + """ + Read 1 byte of UART traffic sent by the device + + Returns an int, representing a byte read from the uart. Should be in the range 0 <= x < 2^bits_per_byte + + :rtype: int + :param xsi: XMOS Simulator Instance. + :param parity: The UART partiy setting. See Parity. + """ + byte = 0 + val = 0 + + # Recv start bit + initial_port_val = self.get_port_val(xsi, self._tx_port) + + if initial_port_val == 1: + self.wait_for_port_pins_change([self._tx_port]) + #else go for it as assume tx has just fallen with no interframe gap + + # The tx line should go low for 1 bit time + if self.get_val_timeout(xsi, self._tx_port) == 0: + if self.debug: print("Start bit recv'd") + else: + print("Start bit issue") + return False + + # recv the byte + crc_sum = 0 + for j in range(self._bits_per_byte): + val = self.get_val_timeout(xsi, self._tx_port) + byte += (val << j) + crc_sum += val + + if self.debug: print(f"Sampled {self._bits_per_byte} data bits: 0x{hex(byte)}") + + # Check the parity if needs be + self.check_parity(xsi, crc_sum, parity) + + # Get the stop bit + self.check_stopbit(xsi) + + # Print a new line to split bytes in output + if self.debug: print() + + return byte + + def check_parity(self, xsi, crc_sum, parity): + """ + Read the parity bit and check it against a crc sum. Print correctness. + + :param xsi: XMOS Simulator Instance. + :param crc_sum: The checksum to test parity against. + :param parity: The UART partiy setting. See Parity. + """ + if parity > 0: + parity_val = 0 if parity == 1 else 1 + read = self.get_val_timeout(xsi, self._tx_port) + if read == (crc_sum + parity_val) % 2: + print("Parity bit correct") + else: + print("Parity bit incorrect. Got %d, expected %d" % (read, (crc_sum + parity_val) % 2)) + else: + if self.debug: print("Parity bit correct") + + def check_stopbit(self, xsi): + """ + Read the stop bit(s) of a UART transmission and print correctness. + + :param xsi: XMOS Simulator Instance. + """ + stop_bits_correct = True + for i in range(self._stop_bits): + # The stop bits should stay high for this time + if self.get_val_timeout(xsi, self._tx_port) == 0: + stop_bits_correct = False + if self.debug: print("Stop bit correct: %s" % ("True" if stop_bits_correct else "False")) + + def get_val_timeout(self, xsi, port): + """ + Get a value from a given port of the device, with a timeout determined + by the BAUD rate. + + Returns whether the pin is high (True) or low (False) + + :rtype: bool + :param xsi: XMOS Simulator Instance. + :param port: The port to sample. + """ + # This intentionally has a 0.3% slop. It is per-byte and gives some + # wiggle-room if the clock doesn't divide into ns nicely. + timeout = self.get_bit_time() * 0.5 + short_timeout = self.get_bit_time() * 0.2485 + + # Allow for "rise" time + self.wait_until(xsi.get_time() + short_timeout) + + # Get val + K = self.wait_time_or_pin_change(xsi, timeout, port) + + # Allow for "fall" time + self.wait_until(xsi.get_time() + short_timeout) + return K + + def wait_time_or_pin_change(self, xsi, timeout, port): + """ + Waits for a given timeout, or until a port changes state. Which ever + occurs 1st. Prints an error if the former causes the function to break. + + Returns whether the pin is high (True) or low (False) + + :rtype: bool + :param xsi: XMOS Simulator Instance. + :param timeout: Time to wait. + :param port: Port to sample. + """ + start_time = xsi.get_time() + start_val = self.get_port_val(xsi, port) + transitioned_during_wait = False + + def _continue(_timeout, _start_time, _start_val): + if xsi.get_time() >= _start_time + _timeout: + return True + if self.get_port_val(xsi, port) != _start_val: + transitioned_during_wait = True + return True + return False + wait_fun = (lambda x: _continue(timeout, start_time, start_val)) + self.wait(wait_fun) + + # Start value should *not* have changed during timeout + if transitioned_during_wait: + print("FAIL :: Unexpected Transition.") + + return start_val + + def run(self): + # Wait for the xcore to bring the uart tx port up + self.wait((lambda x: self.xsi.is_port_driving(self._tx_port))) + self.wait((lambda x: self.get_port_val(self.xsi, self._tx_port) == 1)) + + K = self.read_packet(self.xsi, self._parity, self._length) + + # Print each member of K as a hex byte + # inline lambda function mapped over a list? awh yiss. + print("uart_tx_checker:", " ".join(map((lambda x: "0x%02x" % ord(x)), K))) + diff --git a/tests/xua_unit_tests/CMakeLists.txt b/tests/xua_unit_tests/CMakeLists.txt index 6bccf6d6..8316cf96 100644 --- a/tests/xua_unit_tests/CMakeLists.txt +++ b/tests/xua_unit_tests/CMakeLists.txt @@ -1,103 +1,96 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.21) +include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake) -set(XMOS_TOOLS_PATH $ENV{XMOS_TOOL_PATH}/bin) - -#********************** -# Setup XMOS toolchain -#********************** -if(NOT DEFINED ENV{XUA_PATH}) - message(FATAL_ERROR "XUA_PATH environment variable not defined") - # some more commands +# Auto-generate schedule and top level config files +if( NOT ${Python3_FOUND} ) + message(FATAL_ERROR "Python3 not found for running . ") endif() -include("$ENV{XUA_PATH}/cmake_utils/xmos_toolchain.cmake") -#********************** -# Project -#********************** -# Disable in-source build. -#if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") -# message(FATAL_ERROR "In-source build is not allowed! Please specify a build folder.\n\tex:cmake -B build") -#endif() +#copy conftest.py in the build directory since pytest_collect_file only collects tests from the directory tree where conftest.py is present +configure_file( conftest.py conftest.py COPYONLY ) + +## executable output directory +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) + +# Set unity runner generate script +set(GEN_RUNNER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/generate_unity_runner.py) + +# Create directory for runner files +set(RUNNERS_DIR ${CMAKE_CURRENT_LIST_DIR}/src.runners ) +file(MAKE_DIRECTORY ${RUNNERS_DIR} ) + +# Find unit test files +file(GLOB_RECURSE TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/test_*/*.c) + +# For every source file in xua_unit_tests/ +foreach(TESTFILE ${TEST_SOURCES}) + set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) + + # Get test name from C file stem + cmake_path(GET TESTFILE STEM TESTNAME) + project(${TESTNAME}) + message(STATUS "Processing unit test: ${TESTNAME}") + + # Create runner file directory + file(MAKE_DIRECTORY ${RUNNERS_DIR}/${TESTNAME}) + + ##################### + ## Create runner file + ##################### + set( RUNNER_FILE ${RUNNERS_DIR}/${TESTNAME}/${TESTNAME}_Runner.c ) + set( GEN_RUNNER_SCRIPT_BYPRODUCTS ${RUNNER_FILE}) + + unset(GEN_RUNNER_SCRIPT_ARGS) + list(APPEND GEN_RUNNER_SCRIPT_ARGS --project-root ${XMOS_SANDBOX_DIR}) + list(APPEND GEN_RUNNER_SCRIPT_ARGS --source-file ${TESTFILE}) + list(APPEND GEN_RUNNER_SCRIPT_ARGS --runner-file ${RUNNER_FILE}) + + ## Add command to generate runner file + add_custom_command( + OUTPUT ${RUNNER_FILE} + COMMAND python ${GEN_RUNNER_SCRIPT} ${GEN_RUNNER_SCRIPT_ARGS} + COMMENT "Generate XUA Unit Test Runner" ) + + ########################## + ## Do xcommon cmake build + ########################## + set(APP_HW_TARGET XK-EVK-XU316) + set(APP_DEPENDENT_MODULES "lib_xua" + "lib_unity(2.5.2)") + # set(APP_PCA_ENABLE ON) + set(APP_COMPILER_FLAGS ${EXTRA_BUILD_FLAGS} -fcomment-asm + -Wall + -O2 + -report + -g + -fxscope + -DUSB_TILE=tile[0] + -DUNITY_SUPPORT_64 + -DUNITY_INCLUDE_DOUBLE + -DXUD_CORE_CLOCK=600 + -DXUD_SERIES_SUPPORT=4 + ) + + # For HID tests only enable HID + if(${TESTFILE} MATCHES ".+hid.*") + list(APPEND APP_COMPILER_FLAGS "-DHID_CONTROLS=1") + endif() -## Define project -project(xua_unit_tests VERSION 0.1.0) + # Workaround for xcommon cmake pre-pending CMAKE_CURRENT_LIST_DIR + string(REPLACE ${CMAKE_CURRENT_LIST_DIR} "" UNIT_TEST_SOURCE_RELATIVE ${TESTFILE}) + string(REPLACE ${CMAKE_CURRENT_LIST_DIR} "" RUNNER_FILE_RELATIVE ${RUNNER_FILE}) -## Enable languages for project -enable_language(CXX XC C ASM) + set(APP_C_SRCS ${RUNNER_FILE_RELATIVE} + ${UNIT_TEST_SOURCE_RELATIVE} + ) -message(STATUS "CAME HERE") -add_custom_target("runners" ALL) -add_custom_command( - TARGET runners - COMMAND python generate_unity_runners.py - COMMENT "generate unity runners" -) -message(STATUS "CAME HERE 1") -file( GLOB APP_SOURCES src/test*.xc ) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) -foreach( testsourcefile ${APP_SOURCES} ) - get_filename_component(ITEM_NAME ${testsourcefile} NAME_WE) - message(STATUS "item_name " ${ITEM_NAME}) - add_executable(${ITEM_NAME}) - set(APP_COMPILER_FLAGS - "-O2" - "-g" - "-Wall" - "-report" - "-fxscope" - "-target=XCORE-AI-EXPLORER" - "${CMAKE_CURRENT_SOURCE_DIR}/config.xscope" - "-DHID_CONTROLS=1" - "-DUNITY_SUPPORT_64" - "-DUNITY_INCLUDE_DOUBLE" - ) - set_source_files_properties( - "runners/${ITEM_NAME}/${ITEM_NAME}_Runner.c" - PROPERTIES GENERATED TRUE - ) + get_filename_component(TEST_FILE_DIR ${TESTFILE} DIRECTORY) + set(APP_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/src + ${TEST_FILE_DIR} + ${XMOS_SANDBOX_DIR}/lib_xud/lib_xud/src/user/class) - set(APP_SRCS - ${testsourcefile} - "runners/${ITEM_NAME}/${ITEM_NAME}_Runner.c" - "${CMAKE_CURRENT_SOURCE_DIR}/../../../Unity/src/unity.c" - ) - set(APP_INCLUDES - "src" - "${CMAKE_CURRENT_SOURCE_DIR}/../../../Unity/src" - ) - set(APP_DEPENDENT_MODULES - "lib_xua(>=2.0.0)" - "lib_logging(>=3.0.0)" - "lib_xassert(>=4.0.0)" - "lib_xud(>=2.0.0)" - "lib_spdif(>=4.0.0)" - "lib_mic_array(>=4.0.0)" - ) + XMOS_REGISTER_APP() - include("$ENV{XUA_PATH}/cmake_utils/xua.cmake") - set_target_properties(${ITEM_NAME} PROPERTIES OUTPUT_NAME ${ITEM_NAME}.xe) - target_compile_options(${ITEM_NAME} PRIVATE ${APP_COMPILER_FLAGS}) - - target_include_directories(${ITEM_NAME} - PRIVATE ${APP_INCLUDES} - PRIVATE ${XUA_INCLUDES_ALL} - ) - - target_sources(${ITEM_NAME} - PRIVATE ${APP_SRCS} - PRIVATE ${XUA_SRCS_ALL} - ) - add_dependencies(${ITEM_NAME} runners) - target_link_options(${ITEM_NAME} PRIVATE ${APP_COMPILER_FLAGS}) - ## Set any additional flags only for C++ - set(CMAKE_CXX_FLAGS "-std=c++11") - -endforeach( testsourcefile ${APP_SOURCES} ) - -message(STATUS ${APP_SOURCES}) - -message(STATUS "CAME HERE 2") -## Register the application -#XMOS_REGISTER_APP() +endforeach() diff --git a/tests/xua_unit_tests/conftest.py b/tests/xua_unit_tests/conftest.py index 2128a9b7..bac3e416 100644 --- a/tests/xua_unit_tests/conftest.py +++ b/tests/xua_unit_tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 XMOS LIMITED. +# Copyright 2021-2024 XMOS LIMITED. # This Software is subject to the terms of the XMOS Public Licence: Version 1. from __future__ import print_function from builtins import str @@ -28,7 +28,7 @@ class UnityTestSource(pytest.File): # unit_tests/ <- Test root directory # |-- bin/ <- Compiled binaries of the test runners # |-- conftest.py <- This file - # |-- runners/ <- Auto-generated buildable source of test binaries + # |-- src.runners <- Auto-generated buildable source of test binaries # |-- src/ <- Unity test functions # `-- wscript <- Build system file used to generate/build runners xe_name = ((os.path.basename(self.name)).split("."))[0] + ".xe" @@ -51,9 +51,9 @@ class UnityTestExecutable(pytest.Item): print("run axe for executable ", self.name) test_output = subprocess.check_output(["axe", self.name], text=True) else: - print("run xrun for executable ", self.name) + print("run xsim for executable ", self.name) test_output = subprocess.check_output( - ["xrun", "--io", "--id", "0", self.name], + ["xsim", self.name], text=True, stderr=subprocess.STDOUT, ) diff --git a/tests/xua_unit_tests/generate_unity_runner.py b/tests/xua_unit_tests/generate_unity_runner.py new file mode 100755 index 00000000..7d8dfef1 --- /dev/null +++ b/tests/xua_unit_tests/generate_unity_runner.py @@ -0,0 +1,59 @@ +# Copyright 2024 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. + +import glob +import os.path +import subprocess +import sys +import argparse + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument("--project-root", nargs='?', help="Project root directory") + parser.add_argument("--source-file", nargs='?', help="source file.") + parser.add_argument("--runner-file", nargs='?', help="runner file.") + args = parser.parse_args() + return args + +def get_ruby(): + """ + Check ruby is avaliable and return the command to invoke it. + """ + interpreter_name = 'ruby' + try: + dev_null = open(os.devnull, 'w') + # Call the version command to check the interpreter can be run + subprocess.check_call([interpreter_name, '--version'], + stdout=dev_null, + close_fds=True) + except OSError as e: + print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + + return interpreter_name + +def get_unity_runner_generator(project_root_path): + """ + Check the Unity generate_test_runner script is avaliable, and return the + path to it. + """ + unity_runner_generator = os.path.join( + project_root_path, 'Unity', 'auto', 'generate_test_runner.rb') + if not os.path.exists(unity_runner_generator): + print("Unity repo not found in workspace", file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + return unity_runner_generator + +if __name__ == "__main__": + args = parse_arguments() + print(f"in python: root {args.project_root}, source {args.source_file}, runner {args.runner_file}") + + try: + subprocess.check_call([get_ruby(), + get_unity_runner_generator(args.project_root), + args.source_file, + args.runner_file]) + except OSError as e: + print("Ruby generator failed for {}\n\t{}".format(unity_test_path, e), + file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error diff --git a/tests/xua_unit_tests/generate_unity_runners.py b/tests/xua_unit_tests/generate_unity_runners.py deleted file mode 100644 index 7276d969..00000000 --- a/tests/xua_unit_tests/generate_unity_runners.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright 2021-2022 XMOS LIMITED. -# This Software is subject to the terms of the XMOS Public Licence: Version 1. -import glob -import os.path -import subprocess -import sys - -UNITY_TEST_DIR = "src" -UNITY_TEST_PREFIX = "test_" -UNITY_RUNNER_DIR = "runners" -UNITY_RUNNER_SUFFIX = "_Runner" -project_root = os.path.join("..", "..", "..") - - -def get_ruby(): - """ - Check ruby is avaliable and return the command to invoke it. - """ - interpreter_name = "ruby" - try: - dev_null = open(os.devnull, "w") - # Call the version command to check the interpreter can be run - subprocess.check_call( - [interpreter_name, "--version"], stdout=dev_null, close_fds=True - ) - except OSError as e: - print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr) - exit(1) # TODO: Check this is the correct way to kill xwaf on error - - return interpreter_name - - -def get_unity_runner_generator(project_root_path): - """ - Check the Unity generate_test_runner script is avaliable, and return the - path to it. - """ - unity_runner_generator = os.path.join( - project_root_path, "Unity", "auto", "generate_test_runner.rb" - ) - if not os.path.exists(unity_runner_generator): - print("Unity repo not found in workspace", file=sys.stderr) - exit(1) # TODO: Check this is the correct way to kill xwaf on error - return unity_runner_generator - - -def get_test_name(test_path): - """ - Return the test name by removing the extension from the filename. - """ - return os.path.splitext(os.path.basename(test_path))[0] - - -def get_file_type(filename): - """ - Return the extension from the filename. - """ - return filename.rsplit(".")[-1:][0] - - -def generate_unity_runner( - project_root_path, unity_test_path, unity_runner_dir, unity_runner_suffix -): - """ - Invoke the Unity runner generation script for the given test file, and - return the path to the generated file. The output directory will be created - if it does not already exist. - """ - runner_path = os.path.join( - os.path.join(unity_runner_dir, get_test_name(unity_test_path)) - ) - if not os.path.exists(runner_path): - os.makedirs(runner_path) - - unity_runner_path = os.path.join( - runner_path, get_test_name(unity_test_path) + unity_runner_suffix + "." + "c" - ) - - try: - subprocess.check_call( - [ - get_ruby(), - get_unity_runner_generator(project_root_path), - unity_test_path, - unity_runner_path, - ] - ) - except OSError as e: - print( - "Ruby generator failed for {}\n\t{}".format(unity_test_path, e), - file=sys.stderr, - ) - exit(1) # TODO: Check this is the correct way to kill xwaf on error - - -def find_unity_test_paths(unity_test_dir, unity_test_prefix): - """ - Return a list of all file paths with the unity_test_prefix found in the - unity_test_dir. - """ - return glob.glob(os.path.join(unity_test_dir, unity_test_prefix + "*")) - - -def find_unity_tests(unity_test_dir, unity_test_prefix): - """ - Return a dictionary of all {test names, test language} pairs with the - unity_test_prefix found in the unity_test_dir. - """ - unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) - print("unity_test_paths = ", unity_test_paths) - return {get_test_name(path): get_file_type(path) for path in unity_test_paths} - - -def find_unity_test_paths(unity_test_dir, unity_test_prefix): - """ - Return a list of all file paths with the unity_test_prefix found in the - unity_test_dir. - """ - return glob.glob(os.path.join(unity_test_dir, unity_test_prefix + "*")) - - -def generate_runners(): - UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX) - print("UNITY_TESTS = ", UNITY_TESTS) - unity_test_paths = find_unity_test_paths(UNITY_TEST_DIR, UNITY_TEST_PREFIX) - print("unity_test_paths = ", unity_test_paths) - for unity_test_path in unity_test_paths: - generate_unity_runner( - project_root, unity_test_path, UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX - ) - - -if __name__ == "__main__": - generate_runners() diff --git a/tests/xua_unit_tests/src/test_midi_parse/test_midi_parse.c b/tests/xua_unit_tests/src/test_midi_parse/test_midi_parse.c new file mode 100644 index 00000000..b2a4e45e --- /dev/null +++ b/tests/xua_unit_tests/src/test_midi_parse/test_midi_parse.c @@ -0,0 +1,183 @@ +// Copyright 2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include + +#include "xua_unit_tests.h" +#include "../../../lib_xua/src/midi/midiinparse.h" + +#define NUM_CHANS 16 +#define NOTE_OFF 128 +#define NOTE_ON 144 +#define PRESSURE 160 +#define CONTROL 176 +#define PROGRAM 192 +#define PRESSURE_VAL 208 +#define RANGE 224 +#define MANUFACTURE_ID 240 + +#define DATA_RANGE 128 +#define DATA_MASK (DATA_RANGE - 1) + +#define NUM_TESTS_PER_TEST 30 + +#define CABLE_NUM 2 +#define RANDOM_SEED 6031769 + +unsigned midi_in_parse_ut(unsigned midi[3]){ + // printf("Composing data: 0x%x 0x%x 0x%x\n", midi[0], midi[1], midi[2]); + + struct midi_in_parse_state m_state; + void * mips = &m_state; + reset_midi_state_c_wrapper(mips); + + unsigned valid = 0; + unsigned packed = 0; + + midi_in_parse_c_wrapper(mips, CABLE_NUM, midi[0], &valid, &packed); + // printf("Valid: %d data: %u\n", valid, packed); + if(valid){ + return packed; + } + midi_in_parse_c_wrapper(mips, CABLE_NUM, midi[1], &valid, &packed); + // printf("Valid: %d data: %u\n", valid, packed); + if(valid){ + return packed; + } + midi_in_parse_c_wrapper(mips, CABLE_NUM, midi[2], &valid, &packed); + // printf("Valid: %d data: %u\n", valid, packed); + if(valid){ + return packed; + } + + return 0; +} + +unsigned rndm = RANDOM_SEED; + + +void test_midi_note(void) { + for(int cmd = NOTE_OFF; cmd < NOTE_ON + NUM_CHANS; cmd++){ + for(int test = 0; test < NUM_TESTS_PER_TEST; test++){ + unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK}; + unsigned packed = midi_in_parse_ut(midi_ref); + unsigned midi_dut[3] = {0}; + unsigned size = 0; + midi_out_parse_c_wrapper(packed, midi_dut, &size); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]); + //TEST_ASSERT_EQUAL_UINT32_ARRAY not working!? + for(int i = 0; i < size; i++){ + TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]); + } + } + } +} + +void test_midi_pressure(void) { + for(int cmd = PRESSURE; cmd < PRESSURE + NUM_CHANS; cmd++){ + for(int test = 0; test < NUM_TESTS_PER_TEST; test++){ + unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK}; + unsigned packed = midi_in_parse_ut(midi_ref); + unsigned midi_dut[3] = {0}; + unsigned size = 0; + midi_out_parse_c_wrapper(packed, midi_dut, &size); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]); + //TEST_ASSERT_EQUAL_UINT32_ARRAY not working!? + for(int i = 0; i < size; i++){ + TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]); + } + } + } +} + +void test_midi_control(void) { + for(int cmd = CONTROL; cmd < CONTROL + NUM_CHANS; cmd++){ + for(int test = 0; test < NUM_TESTS_PER_TEST; test++){ + unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK}; + unsigned packed = midi_in_parse_ut(midi_ref); + unsigned midi_dut[3] = {0}; + unsigned size = 0; + midi_out_parse_c_wrapper(packed, midi_dut, &size); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]); + //TEST_ASSERT_EQUAL_UINT32_ARRAY not working!? + for(int i = 0; i < size; i++){ + TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]); + } + } + } +} + +void test_midi_program(void) { + for(int cmd = PROGRAM; cmd < PROGRAM + NUM_CHANS; cmd++){ + for(int test = 0; test < NUM_TESTS_PER_TEST; test++){ + unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK}; + unsigned packed = midi_in_parse_ut(midi_ref); + unsigned midi_dut[3] = {0}; + unsigned size = 0; + midi_out_parse_c_wrapper(packed, midi_dut, &size); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]); + //TEST_ASSERT_EQUAL_UINT32_ARRAY not working!? + for(int i = 0; i < size; i++){ + TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]); + } + } + } +} + +void test_midi_pressure_val(void) { + for(int cmd = PRESSURE_VAL; cmd < PRESSURE_VAL + NUM_CHANS; cmd++){ + for(int test = 0; test < NUM_TESTS_PER_TEST; test++){ + unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK}; + unsigned packed = midi_in_parse_ut(midi_ref); + unsigned midi_dut[3] = {0}; + unsigned size = 0; + midi_out_parse_c_wrapper(packed, midi_dut, &size); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]); + //TEST_ASSERT_EQUAL_UINT32_ARRAY not working!? + for(int i = 0; i < size; i++){ + TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]); + } + } + } +} + +void test_midi_range(void) { + for(int cmd = RANGE; cmd < RANGE + NUM_CHANS; cmd++){ + for(int test = 0; test < NUM_TESTS_PER_TEST; test++){ + unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK}; + unsigned packed = midi_in_parse_ut(midi_ref); + unsigned midi_dut[3] = {0}; + unsigned size = 0; + midi_out_parse_c_wrapper(packed, midi_dut, &size); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]); + //TEST_ASSERT_EQUAL_UINT32_ARRAY not working!? + for(int i = 0; i < size; i++){ + TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]); + } + } + } +} + +void test_midi_manufacturer_id(void) { + for(int cmd = MANUFACTURE_ID; cmd < MANUFACTURE_ID + NUM_CHANS; cmd++){ + for(int test = 0; test < NUM_TESTS_PER_TEST; test++){ + unsigned midi_ref[3] = {cmd, random(&rndm) & DATA_MASK, random(&rndm) & DATA_MASK}; + unsigned packed = midi_in_parse_ut(midi_ref); + unsigned midi_dut[3] = {0}; + unsigned size = 0; + midi_out_parse_c_wrapper(packed, midi_dut, &size); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_ref[0], midi_ref[1], midi_ref[2]); + // printf("size: %d data: 0x%x 0x%x 0x%x\n", size, midi_dut[0], midi_dut[1], midi_dut[2]); + //TEST_ASSERT_EQUAL_UINT32_ARRAY not working!? + for(int i = 0; i < size; i++){ + TEST_ASSERT_EQUAL_UINT32(midi_ref[i], midi_dut[i]); + } + } + } +} \ No newline at end of file diff --git a/tests/xua_unit_tests/src/test_midi_queue/test_midi_queue.c b/tests/xua_unit_tests/src/test_midi_queue/test_midi_queue.c new file mode 100644 index 00000000..07e643e0 --- /dev/null +++ b/tests/xua_unit_tests/src/test_midi_queue/test_midi_queue.c @@ -0,0 +1,77 @@ +// Copyright 2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include + +#include "xua_unit_tests.h" +#include "../../../lib_xua/src/midi/queue.h" + +#define DEBUG 0 + +#if DEBUG +#define dprintf(...) printf(__VA_ARGS__) +#else +#define dprintf(...) +#endif + + +#define RANDOM_SEED 55378008 +#define USB_MIDI_DEVICE_OUT_FIFO_SIZE 1024 +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +unsigned rndm = RANDOM_SEED; + + +void test_midi_queue_init(void) { + queue_t symbol_fifo; + unsigned symbol_fifo_storage[USB_MIDI_DEVICE_OUT_FIFO_SIZE]; + queue_init_c_wrapper(&symbol_fifo, ARRAY_SIZE(symbol_fifo_storage)); + + int empty = queue_is_empty_c_wrapper(&symbol_fifo); + TEST_ASSERT_EQUAL_INT32(1, empty); + + int full = queue_is_full_c_wrapper(&symbol_fifo); + TEST_ASSERT_EQUAL_INT32(0, full); + + unsigned items = queue_items_c_wrapper(&symbol_fifo); + TEST_ASSERT_EQUAL_UINT32(0, items); + + unsigned space = queue_space_c_wrapper(&symbol_fifo); + TEST_ASSERT_EQUAL_UINT32(USB_MIDI_DEVICE_OUT_FIFO_SIZE, space); +} + +void test_midi_queue_push_pop(void) { + queue_t symbol_fifo; + unsigned symbol_fifo_storage[USB_MIDI_DEVICE_OUT_FIFO_SIZE]; + queue_init_c_wrapper(&symbol_fifo, ARRAY_SIZE(symbol_fifo_storage)); + + for(unsigned i = 0; i < USB_MIDI_DEVICE_OUT_FIFO_SIZE; i++){ + int items = queue_items_c_wrapper(&symbol_fifo); + dprintf("Pre i: %u items: %d\n", i, items); + TEST_ASSERT_EQUAL_UINT32(i, items); + + unsigned entry = i + 1000; + queue_push_word_c_wrapper(&symbol_fifo, symbol_fifo_storage, entry); + dprintf("pushed: %u\n", entry); + + items = queue_items_c_wrapper(&symbol_fifo); + TEST_ASSERT_EQUAL_UINT32(i + 1, items); + + dprintf("Post items: %d\n", items); + } + + unsigned counter = 0; + for(int i = USB_MIDI_DEVICE_OUT_FIFO_SIZE; i > 0; i--){ + int items = queue_items_c_wrapper(&symbol_fifo); + dprintf("i: %u items: %d\n", i, items); + TEST_ASSERT_EQUAL_UINT32(i, items); + + unsigned entry = queue_pop_word_c_wrapper(&symbol_fifo, symbol_fifo_storage); + unsigned expected = 1000 + counter; + + dprintf("expected: %u got: %d\n", expected, entry); + TEST_ASSERT_EQUAL_UINT32(expected, entry); + + counter++; + } +} \ No newline at end of file diff --git a/tests/xua_unit_tests/src/test_multi_report/test_hid_multi_report.c b/tests/xua_unit_tests/src/test_multi_report/test_hid_multi_report.c index d9fbac7c..fbb9a7da 100644 --- a/tests/xua_unit_tests/src/test_multi_report/test_hid_multi_report.c +++ b/tests/xua_unit_tests/src/test_multi_report/test_hid_multi_report.c @@ -1,4 +1,4 @@ -// Copyright 2021-2022 XMOS LIMITED. +// Copyright 2021-2024 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include #include @@ -47,23 +47,26 @@ static unsigned construct_usage_header( unsigned size ) return header; } -void setUp( void ) +void test_init( void ) { hidReportInit(); hidResetReportDescriptor(); } void test_validate_report( void ) { + test_init(); unsigned retVal = hidReportValidate(); TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); } void test_reportid_in_use( void ) { + test_init(); unsigned reportIdInUse = hidIsReportIdInUse(); TEST_ASSERT_EQUAL_UINT( 1, reportIdInUse ); } void test_get_next_valid_report_id( void ) { + test_init(); unsigned reportId = 0U; reportId = hidGetNextValidReportId(reportId); @@ -80,6 +83,7 @@ void test_get_next_valid_report_id( void ) { } void test_is_report_id_valid( void ) { + test_init(); unsigned isValid = 0; unsigned reportId = 0; @@ -106,6 +110,7 @@ void test_is_report_id_valid( void ) { // Basic report descriptor tests void test_unprepared_hidGetReportDescriptor( void ) { + test_init(); unsigned char* reportDescPtr = hidGetReportDescriptor(); TEST_ASSERT_NULL( reportDescPtr ); @@ -118,6 +123,7 @@ void test_unprepared_hidGetReportDescriptor( void ) void test_prepared_hidGetReportDescriptor( void ) { + test_init(); hidPrepareReportDescriptor(); unsigned char* reportDescPtr = hidGetReportDescriptor(); TEST_ASSERT_NOT_NULL( reportDescPtr ); @@ -137,6 +143,7 @@ void test_prepared_hidGetReportDescriptor( void ) void test_reset_unprepared_hidGetReportDescriptor( void ) { + test_init(); hidPrepareReportDescriptor(); hidResetReportDescriptor(); unsigned char* reportDescPtr = hidGetReportDescriptor(); @@ -145,6 +152,7 @@ void test_reset_unprepared_hidGetReportDescriptor( void ) void test_reset_prepared_hidGetReportDescriptor( void ) { + test_init(); hidPrepareReportDescriptor(); hidResetReportDescriptor(); hidPrepareReportDescriptor(); @@ -154,6 +162,7 @@ void test_reset_prepared_hidGetReportDescriptor( void ) void test_report_id_limit( void ) { + test_init(); unsigned reportIdLimit = hidGetReportIdLimit(); TEST_ASSERT_EQUAL_UINT( HID_REPORTID_LIMIT, reportIdLimit ); } @@ -161,6 +170,7 @@ void test_report_id_limit( void ) // Basic item tests void test_max_loc_hidGetReportItem( void ) { + test_init(); unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ]; unsigned char header; unsigned char page; @@ -199,6 +209,7 @@ void test_max_loc_hidGetReportItem( void ) void test_min_loc_hidGetReportItem( void ) { + test_init(); unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ]; unsigned char header; unsigned char page; @@ -236,6 +247,7 @@ void test_min_loc_hidGetReportItem( void ) void test_invalid_report_id( void ) { + test_init(); unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD2 }; unsigned char header = 0x33; unsigned char page = 0x44; @@ -253,6 +265,7 @@ void test_invalid_report_id( void ) void test_unused_report_id( void ) { + test_init(); unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD2 }; unsigned char header = 0x33; unsigned char page = 0x44; @@ -270,6 +283,7 @@ void test_unused_report_id( void ) void test_overflow_bit_hidGetReportItem( void ) { + test_init(); unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; unsigned char header = 0xAA; unsigned char page = 0x44; @@ -307,6 +321,7 @@ void test_overflow_bit_hidGetReportItem( void ) void test_overflow_byte_hidGetReportItem( void ) { + test_init(); unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; unsigned char header = 0xAA; unsigned char page = 0x44; @@ -344,6 +359,7 @@ void test_overflow_byte_hidGetReportItem( void ) void test_underflow_bit_hidGetReportItem( void ) { + test_init(); unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; unsigned char header = 0xAA; unsigned char page = 0x44; @@ -381,6 +397,7 @@ void test_underflow_bit_hidGetReportItem( void ) void test_underflow_byte_hidGetReportItem( void ) { + test_init(); unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; unsigned char header = 0xAA; unsigned char page = 0x44; @@ -419,6 +436,7 @@ void test_underflow_byte_hidGetReportItem( void ) // Configurable and non-configurable item tests void test_configurable_item_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 1; const unsigned bit = REPORT1_MIN_VALID_BIT; const unsigned byte = REPORT1_MIN_VALID_BYTE; @@ -440,6 +458,7 @@ void test_configurable_item_hidSetReportItem( void ) // Testing that the high byte of the report gets correctly cleared void test_configurable_item_hidSetReportItem_multibyte_orig( void ) { + test_init(); const unsigned reportId = 2; const unsigned bit = 1; // This byte&bit combo is originally set be 2 bytes long in the header const unsigned byte = 0; @@ -460,6 +479,7 @@ void test_configurable_item_hidSetReportItem_multibyte_orig( void ) void test_nonconfigurable_item_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 1; const unsigned bit = 1; // This bit and byte combination should not appear in the const unsigned byte = 0; // hidConfigurableElements list in hid_report_descriptors.c. @@ -474,6 +494,7 @@ void test_nonconfigurable_item_hidSetReportItem( void ) // Bit range tests void test_max_bit_hidSetReportItem( void ) { + test_init(); const unsigned char header = construct_usage_header( 0 ); unsigned reportId = 1; @@ -500,6 +521,7 @@ void test_max_bit_hidSetReportItem( void ) void test_min_bit_hidSetReportItem( void ) { + test_init(); const unsigned char header = construct_usage_header( 0 ); unsigned reportId = 1; @@ -526,6 +548,7 @@ void test_min_bit_hidSetReportItem( void ) void test_overflow_bit_hidSetReportItem( void ) { + test_init(); const unsigned char header = construct_usage_header( 0 ); unsigned reportId = 1; @@ -552,6 +575,7 @@ void test_overflow_bit_hidSetReportItem( void ) void test_underflow_bit_hidSetReportItem( void ) { + test_init(); const unsigned char header = construct_usage_header( 0 ); unsigned reportId = 1; @@ -578,6 +602,7 @@ void test_underflow_bit_hidSetReportItem( void ) void test_overflow_byte_hidSetReportItem( void ) { + test_init(); const unsigned char header = construct_usage_header( 0 ); unsigned reportId = 1; @@ -604,6 +629,7 @@ void test_overflow_byte_hidSetReportItem( void ) void test_underflow_byte_hidSetReportItem( void ) { + test_init(); const unsigned char header = construct_usage_header( 0 ); unsigned reportId = 1; @@ -631,6 +657,7 @@ void test_underflow_byte_hidSetReportItem( void ) // Size range tests void test_max_size_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 1; const unsigned bit = REPORT1_MIN_VALID_BIT; const unsigned byte = REPORT1_MIN_VALID_BYTE; @@ -644,6 +671,7 @@ void test_max_size_hidSetReportItem( void ) void test_min_size_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 1; const unsigned bit = REPORT1_MIN_VALID_BIT; const unsigned byte = REPORT1_MIN_VALID_BYTE; @@ -656,6 +684,7 @@ void test_min_size_hidSetReportItem( void ) void test_unsupported_size_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = REPORT1_MIN_VALID_BIT; const unsigned byte = REPORT1_MIN_VALID_BYTE; @@ -669,6 +698,7 @@ void test_unsupported_size_hidSetReportItem( void ) // Combined function tests void test_initial_modification_without_subsequent_preparation( void ) { + test_init(); const unsigned reportId = 2; const unsigned bit = REPORT2_MIN_VALID_BIT; const unsigned byte = REPORT2_MIN_VALID_BYTE; @@ -685,6 +715,7 @@ void test_initial_modification_without_subsequent_preparation( void ) void test_initial_modification_with_subsequent_preparation( void ) { + test_init(); const unsigned reportId = 2; const unsigned bit = REPORT2_MIN_VALID_BIT; const unsigned byte = REPORT2_MIN_VALID_BYTE; @@ -702,6 +733,7 @@ void test_initial_modification_with_subsequent_preparation( void ) void test_initial_modification_with_subsequent_verification_1( void ) { + test_init(); const unsigned reportId = 2; const unsigned bit = REPORT2_MIN_VALID_BIT; const unsigned byte = REPORT2_MIN_VALID_BYTE; @@ -727,6 +759,7 @@ void test_initial_modification_with_subsequent_verification_1( void ) void test_initial_modification_with_subsequent_verification_2( void ) { + test_init(); const unsigned reportId = 3; const unsigned bit = REPORT3_MIN_VALID_BIT; const unsigned byte = REPORT3_MIN_VALID_BYTE; @@ -775,6 +808,7 @@ void test_initial_modification_with_subsequent_verification_2( void ) //setIdle and associated timing functionality tests void test_set_idle( void ) { + test_init(); unsigned reportId = 1; unsigned reportId2 = 2; @@ -794,6 +828,7 @@ void test_set_idle( void ) void test_set_all_idle( void ) { + test_init(); unsigned reportId = 1; unsigned reportId2 = 2; @@ -812,6 +847,7 @@ void test_set_all_idle( void ) void test_change_pending( void ) { + test_init(); unsigned reportId = 1; unsigned reportId2 = 2; @@ -831,6 +867,7 @@ void test_change_pending( void ) void test_change_pending_all( void ) { + test_init(); unsigned reportId = 1; unsigned changePending = hidIsChangePending( reportId ); @@ -845,6 +882,7 @@ void test_change_pending_all( void ) void test_report_time( void ) { + test_init(); unsigned reportTime1 = 123; unsigned reportTime2 = 456; @@ -859,6 +897,7 @@ void test_report_time( void ) void test_report_time_calc( void ) { + test_init(); unsigned reportTime1 = 123; unsigned reportTime2 = 456; unsigned reportPeriod1 = 10; diff --git a/tests/xua_unit_tests/src/test_simple/test_hid.c b/tests/xua_unit_tests/src/test_simple/test_hid.c index 3bd930bc..28250bd6 100644 --- a/tests/xua_unit_tests/src/test_simple/test_hid.c +++ b/tests/xua_unit_tests/src/test_simple/test_hid.c @@ -1,4 +1,4 @@ -// Copyright 2021-2022 XMOS LIMITED. +// Copyright 2021-2024 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include #include @@ -34,23 +34,26 @@ static unsigned construct_usage_header( unsigned size ) return header; } -void setUp( void ) +void test_init( void ) { hidReportInit(); hidResetReportDescriptor(); } void test_validate_report( void ) { + test_init(); unsigned retVal = hidReportValidate(); TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); } void test_reportid_in_use( void ) { + test_init(); unsigned reportIdInUse = hidIsReportIdInUse(); TEST_ASSERT_EQUAL_UINT( 0, reportIdInUse ); } void test_get_next_valid_report_id( void ) { + test_init(); unsigned reportId = 0U; reportId = hidGetNextValidReportId(reportId); @@ -61,6 +64,7 @@ void test_get_next_valid_report_id( void ) { } void test_is_report_id_valid( void ) { + test_init(); unsigned isValid = 0; unsigned reportId = 0; @@ -75,6 +79,7 @@ void test_is_report_id_valid( void ) { // Basic report descriptor tests void test_unprepared_hidGetReportDescriptor( void ) { + test_init(); const unsigned reportId = 0; unsigned char* reportDescPtr = hidGetReportDescriptor(); TEST_ASSERT_NULL( reportDescPtr ); @@ -85,6 +90,7 @@ void test_unprepared_hidGetReportDescriptor( void ) void test_prepared_hidGetReportDescriptor( void ) { + test_init(); const unsigned reportId = 0; hidPrepareReportDescriptor(); @@ -97,6 +103,7 @@ void test_prepared_hidGetReportDescriptor( void ) void test_reset_unprepared_hidGetReportDescriptor( void ) { + test_init(); hidPrepareReportDescriptor(); hidResetReportDescriptor(); unsigned char* reportDescPtr = hidGetReportDescriptor(); @@ -105,6 +112,7 @@ void test_reset_unprepared_hidGetReportDescriptor( void ) void test_reset_prepared_hidGetReportDescriptor( void ) { + test_init(); hidPrepareReportDescriptor(); hidResetReportDescriptor(); hidPrepareReportDescriptor(); @@ -114,6 +122,7 @@ void test_reset_prepared_hidGetReportDescriptor( void ) void test_report_id_limit( void ) { + test_init(); unsigned reportIdLimit = hidGetReportIdLimit(); TEST_ASSERT_EQUAL_UINT( HID_REPORTID_LIMIT, reportIdLimit ); } @@ -121,6 +130,7 @@ void test_report_id_limit( void ) // Basic item tests void test_max_loc_hidGetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MAX_VALID_BIT; const unsigned byte = MAX_VALID_BYTE; @@ -138,6 +148,7 @@ void test_max_loc_hidGetReportItem( void ) void test_min_loc_hidGetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -155,6 +166,7 @@ void test_min_loc_hidGetReportItem( void ) void test_overflow_bit_hidGetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MAX_VALID_BIT + 1; const unsigned byte = MAX_VALID_BYTE; @@ -172,6 +184,7 @@ void test_overflow_bit_hidGetReportItem( void ) void test_overflow_byte_hidGetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MAX_VALID_BIT; const unsigned byte = MAX_VALID_BYTE + 1; @@ -189,6 +202,7 @@ void test_overflow_byte_hidGetReportItem( void ) void test_underflow_bit_hidGetReportItem( void ) { + test_init(); const unsigned reportId = 0; const int bit = MIN_VALID_BIT - 1; const unsigned byte = MIN_VALID_BYTE; @@ -206,6 +220,7 @@ void test_underflow_bit_hidGetReportItem( void ) void test_underflow_byte_hidGetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const int byte = MIN_VALID_BYTE - 1; @@ -224,6 +239,7 @@ void test_underflow_byte_hidGetReportItem( void ) // Configurable and non-configurable item tests void test_configurable_item_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -237,6 +253,7 @@ void test_configurable_item_hidSetReportItem( void ) void test_nonconfigurable_item_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MAX_VALID_BIT; // This bit and byte combination should not appear in the const unsigned byte = MIN_VALID_BYTE; // hidConfigurableElements list in hid_report_descriptors.c. @@ -251,6 +268,7 @@ void test_nonconfigurable_item_hidSetReportItem( void ) // Bit range tests void test_max_bit_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MAX_VALID_BIT; // Only byte 1 has bit 7 not reserved, See the const unsigned byte = MAX_VALID_BYTE; // hidConfigurableElements list in hid_report_descriptors.c. @@ -263,6 +281,7 @@ void test_max_bit_hidSetReportItem( void ) void test_min_bit_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -275,6 +294,7 @@ void test_min_bit_hidSetReportItem( void ) void test_overflow_bit_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MAX_VALID_BIT + 1; const unsigned byte = MIN_VALID_BYTE; @@ -287,6 +307,7 @@ void test_overflow_bit_hidSetReportItem( void ) void test_underflow_bit_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const int bit = MIN_VALID_BIT - 1; const unsigned byte = MIN_VALID_BYTE; @@ -300,6 +321,7 @@ void test_underflow_bit_hidSetReportItem( void ) // Byte range tests void test_max_byte_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MAX_VALID_BYTE; @@ -312,6 +334,7 @@ void test_max_byte_hidSetReportItem( void ) void test_min_byte_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -324,6 +347,7 @@ void test_min_byte_hidSetReportItem( void ) void test_overflow_byte_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MAX_VALID_BYTE + 1; @@ -336,6 +360,7 @@ void test_overflow_byte_hidSetReportItem( void ) void test_underflow_byte_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const int byte = MIN_VALID_BYTE - 1; @@ -349,6 +374,7 @@ void test_underflow_byte_hidSetReportItem( void ) // Size range tests void test_max_size_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -362,6 +388,7 @@ void test_max_size_hidSetReportItem( void ) void test_min_size_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -374,6 +401,7 @@ void test_min_size_hidSetReportItem( void ) void test_unsupported_size_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -387,6 +415,7 @@ void test_unsupported_size_hidSetReportItem( void ) // Header tag and type tests void test_bad_tag_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -402,6 +431,7 @@ void test_bad_tag_hidSetReportItem( void ) void test_global_type_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -415,6 +445,7 @@ void test_global_type_hidSetReportItem( void ) void test_local_type_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -428,6 +459,7 @@ void test_local_type_hidSetReportItem( void ) void test_main_type_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -441,6 +473,7 @@ void test_main_type_hidSetReportItem( void ) void test_reserved_type_hidSetReportItem( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -455,6 +488,7 @@ void test_reserved_type_hidSetReportItem( void ) // Combined function tests void test_initial_modification_without_subsequent_preparation( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -471,6 +505,7 @@ void test_initial_modification_without_subsequent_preparation( void ) void test_initial_modification_with_subsequent_preparation( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -488,6 +523,7 @@ void test_initial_modification_with_subsequent_preparation( void ) void test_initial_modification_with_subsequent_verification_1( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -513,6 +549,7 @@ void test_initial_modification_with_subsequent_verification_1( void ) void test_initial_modification_with_subsequent_verification_2( void ) { + test_init(); const unsigned reportId = 0; const unsigned bit = MIN_VALID_BIT; const unsigned byte = MIN_VALID_BYTE; @@ -560,6 +597,7 @@ void test_initial_modification_with_subsequent_verification_2( void ) void test_modification_without_subsequent_preparation( void ) { + test_init(); hidPrepareReportDescriptor(); unsigned char* reportDescPtr = hidGetReportDescriptor(); TEST_ASSERT_NOT_NULL( reportDescPtr ); @@ -581,6 +619,7 @@ void test_modification_without_subsequent_preparation( void ) void test_modification_with_subsequent_preparation( void ) { + test_init(); hidPrepareReportDescriptor(); unsigned char* reportDescPtr = hidGetReportDescriptor(); TEST_ASSERT_NOT_NULL( reportDescPtr ); @@ -604,6 +643,7 @@ void test_modification_with_subsequent_preparation( void ) //setIdle functionality tests void test_set_idle( void ) { + test_init(); unsigned reportId = 0; unsigned setIdle = hidIsIdleActive( reportId ); @@ -616,6 +656,7 @@ void test_set_idle( void ) void test_change_pending( void ) { + test_init(); unsigned reportId = 0; unsigned changePending = hidIsChangePending( reportId ); @@ -632,6 +673,7 @@ void test_change_pending( void ) void test_report_time( void ) { + test_init(); unsigned reportTime = 123; unsigned reportPeriod = 10; diff --git a/tests/xua_unit_tests/src/xua_unit_test_helper.xc b/tests/xua_unit_tests/src/xua_unit_test_helper.xc index a2df0db9..cb7c321b 100644 --- a/tests/xua_unit_tests/src/xua_unit_test_helper.xc +++ b/tests/xua_unit_tests/src/xua_unit_test_helper.xc @@ -1,10 +1,13 @@ -// Copyright 2021 XMOS LIMITED. +// Copyright 2021-2024 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifdef __XC__ #include #include #include +#include "../../../lib_xua/src/midi/midiinparse.h" +#include "../../../lib_xua/src/midi/midioutparse.h" +#include "../../../lib_xua/src/midi/queue.h" #endif // __XC__ @@ -26,3 +29,87 @@ void AudioHwInit() { ; // nothing } + +unsigned random(unsigned &x){ + crc32(x, -1, 0xEB31D82E); + return x; +} + +////////////////////// Wrappers for midi parse because C doesn't support return tuples +void midi_in_parse_c_wrapper(void * unsafe mips, unsigned cable_number, unsigned char b, unsigned * unsafe valid, unsigned *unsafe packed){ + unsafe{ + struct midi_in_parse_state * unsafe ptr = mips; + {*valid, *packed} = midi_in_parse(*ptr, cable_number, b); + } +} + +void midi_out_parse_c_wrapper(unsigned tx_data, unsigned midi[3], unsigned * unsafe size){ + unsafe{ + {midi[0], midi[1], midi[2], *size} = midi_out_parse(tx_data); + } +} + +void reset_midi_state_c_wrapper(void * unsafe mips){ + unsafe{ + struct midi_in_parse_state * unsafe ptr = mips; + reset_midi_state(*ptr); + } +} + +/////////////////////// Wrappers for queue test + + +void queue_init_c_wrapper(queue_t *q, unsigned size) { + unsafe{ + queue_init(*q, size); + } +} + +int queue_is_empty_c_wrapper(queue_t *unsafe q) { + unsafe{ + return queue_is_empty(*q); + } +} + +int queue_is_full_c_wrapper(queue_t *unsafe q) { + unsafe{ + return queue_is_full(*q); + } +} + +void queue_push_word_c_wrapper(queue_t *q, unsigned array[], unsigned data){ + unsafe{ + queue_push_word(*q, array, data); + } +} + +unsigned queue_pop_word_c_wrapper(queue_t *q, unsigned array[]){ + unsafe{ + return queue_pop_word(*q, array); + } +} + +void queue_push_byte_c_wrapper(queue_t *q, unsigned char array[], unsigned data){ + unsafe{ + queue_push_byte(*q, array, data); + } +} + +unsigned queue_pop_byte_c_wrapper(queue_t *q, unsigned char array[]){ + unsafe{ + return queue_pop_byte(*q, array); + } +} + +unsigned queue_items_c_wrapper(const queue_t *q){ + unsafe{ + return queue_items(*q); + } +} + +unsigned queue_space_c_wrapper(const queue_t *q){ + unsafe{ + return queue_space(*q); + } +} + diff --git a/tests/xua_unit_tests/src/xua_unit_tests.h b/tests/xua_unit_tests/src/xua_unit_tests.h index 0b1d82d9..5deec2dc 100644 --- a/tests/xua_unit_tests/src/xua_unit_tests.h +++ b/tests/xua_unit_tests/src/xua_unit_tests.h @@ -1,9 +1,28 @@ -// Copyright 2021 XMOS LIMITED. +// Copyright 2021-2024 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef XUA_UNIT_TESTS_H_ #define XUA_UNIT_TESTS_H_ #include "unity.h" #include "xua_conf.h" +#include "../../../lib_xua/src/midi/queue.h" + +#ifndef __XC__ +void midi_in_parse_c_wrapper(void * mips, unsigned cable_number, unsigned char b, unsigned * valid, unsigned * packed); +void midi_out_parse_c_wrapper(unsigned tx_data, unsigned midi[3], unsigned * size); +void reset_midi_state_c_wrapper(void *mips); +unsigned random(unsigned *x); + +void queue_init_c_wrapper(queue_t *q, unsigned size); +int queue_is_empty_c_wrapper(const queue_t *q); +int queue_is_full_c_wrapper(const queue_t *q); +void queue_push_word_c_wrapper(queue_t *q, unsigned array[], unsigned data); +unsigned queue_pop_word_c_wrapper(queue_t *q, unsigned array[]); +void queue_push_byte_c_wrapper(queue_t *q, unsigned char array[], unsigned data); +unsigned queue_pop_byte_c_wrapper(queue_t *q, unsigned char array[]); +unsigned queue_items_c_wrapper(const queue_t *q); +unsigned queue_space_c_wrapper(const queue_t *q); + +#endif #endif /* XUA_UNIT_TESTS_H_ */ diff --git a/tests/xua_unit_tests/wscript b/tests/xua_unit_tests/wscript deleted file mode 100644 index a31dbfaf..00000000 --- a/tests/xua_unit_tests/wscript +++ /dev/null @@ -1,260 +0,0 @@ -from __future__ import print_function -import glob -import os.path -import subprocess -import sys -from waflib import Options -from waflib.Build import BuildContext, CleanContext - -TARGETS = ['xcore200', 'xcoreai'] - -def get_ruby(): - """ - Check ruby is avaliable and return the command to invoke it. - """ - interpreter_name = 'ruby' - try: - dev_null = open(os.devnull, 'w') - # Call the version command to check the interpreter can be run - subprocess.check_call([interpreter_name, '--version'], - stdout=dev_null, - close_fds=True) - except OSError as e: - print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr) - exit(1) # TODO: Check this is the correct way to kill xwaf on error - - return interpreter_name - - -def get_unity_runner_generator(project_root_path): - """ - Check the Unity generate_test_runner script is avaliable, and return the - path to it. - """ - unity_runner_generator = os.path.join( - project_root_path, 'Unity', 'auto', 'generate_test_runner.rb') - if not os.path.exists(unity_runner_generator): - print("Unity repo not found in workspace", file=sys.stderr) - exit(1) # TODO: Check this is the correct way to kill xwaf on error - return unity_runner_generator - - -def get_test_name(test_path): - """ - Return the test name by removing the extension from the filename. - """ - return os.path.splitext(os.path.basename(test_path))[0] - - -def get_file_type(filename): - """ - Return the extension from the filename. - """ - return filename.rsplit('.')[-1:][0] - - -def generate_unity_runner(project_root_path, unity_test_path, unity_runner_dir, - unity_runner_suffix): - """ - Invoke the Unity runner generation script for the given test file, and - return the path to the generated file. The output directory will be created - if it does not already exist. - """ - runner_path = os.path.join(os.path.join(unity_runner_dir, get_test_name(unity_test_path))) - if not os.path.exists(runner_path): - os.makedirs(runner_path) - - unity_runner_path = os.path.join( - runner_path, get_test_name(unity_test_path) + unity_runner_suffix - + '.' + 'c') - - try: - subprocess.check_call([get_ruby(), - get_unity_runner_generator(project_root_path), - unity_test_path, - unity_runner_path]) - except OSError as e: - print("Ruby generator failed for {}\n\t{}".format(unity_test_path, e), - file=sys.stderr) - exit(1) # TODO: Check this is the correct way to kill xwaf on error - - -def add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path, - unity_runner_build_flags, target): - """ - Add a config to xwaf to build each Unity test runner into an xCORE - executable. - """ - print(f"get_test_name(unity_test_path) = {get_test_name(unity_test_path)}. target = {target}") - waf_conf.setenv(get_test_name(unity_test_path) + '_' + target) - waf_conf.load('xwaf.compiler_xcc') - waf_conf.env.XCC_FLAGS = unity_runner_build_flags - waf_conf.env.PROJECT_ROOT = project_root_path - # TODO: can the xwaf boilerplate help here? - - -def prepare_unity_test_for_build(waf_conf, project_root_path, unity_test_path, - unity_runner_dir, unity_runner_suffix, target): - print("unity_test_path: " + str(unity_test_path)) - generate_unity_runner(project_root_path, unity_test_path, - unity_runner_dir, unity_runner_suffix) - runner_build_flags = '' # Could extract flags from the test name - add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path, - runner_build_flags, target) - - -def find_unity_test_paths(unity_test_dir, unity_test_prefix): - """ - Return a list of all file paths with the unity_test_prefix found in the - unity_test_dir. - """ - file_list = [] - for root, dirs, files in os.walk(unity_test_dir): - for f in files: - if f.startswith(unity_test_prefix): - file_list.append(os.path.join(root,f)) - return file_list - - -def find_unity_tests(unity_test_dir, unity_test_prefix): - """ - Return a dictionary of all {test names, test language} pairs with the - unity_test_prefix found in the unity_test_dir. - """ - unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) - return {get_test_name(path): {"language": get_file_type(path), "dir": os.path.dirname(path)} - for path in unity_test_paths} - - -def generate_all_unity_runners(waf_conf, project_root_path, - unity_test_dir, unity_test_prefix, - unity_runner_dir, unity_runner_suffix): - """ - Generate a runner and a build config for each test file in the - unity_test_dir. - """ - # FIXME: pass unity_tests in? - unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) - for trgt in TARGETS: - for unity_test_path in unity_test_paths: - prepare_unity_test_for_build(waf_conf, project_root_path, - unity_test_path, - unity_runner_dir, unity_runner_suffix, trgt) - - -# TODO: can the xwaf boilerplate help here? -def create_waf_contexts(configs): - for trgt in TARGETS: - for test_name, params in configs.items(): - print(f"test_name {test_name}, test_language {params['language']}") - for ctx in (BuildContext, CleanContext): - raw_context = ctx.__name__.replace('Context', '').lower() - - class tmp(ctx): - cmd = raw_context + '_' + test_name + '_' + trgt - variant = test_name + '_' + trgt - #cmd = raw_context + '_' + test_name - #variant = test_name - language = params["language"] - target = trgt - runner = test_name - directory = params["dir"] - print(f"cmd {cmd}, variant {variant}, language {language}") - - -UNITY_TEST_DIR = 'src' -UNITY_TEST_PREFIX = 'test_' -UNITY_RUNNER_DIR = 'runners' -UNITY_RUNNER_SUFFIX = '_Runner' -UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX) - -print("UNITY_TESTS: " + str(UNITY_TESTS)) - -create_waf_contexts(UNITY_TESTS) - -def options(opt): - opt.add_option('--target', action='store', default='xcore200') - opt.load('xwaf.xcommon') - -def configure(conf): - # TODO: move the call to generate_all_unity_runners() to build() - project_root = os.path.join('..', '..', '..') - generate_all_unity_runners(conf, project_root, - UNITY_TEST_DIR, UNITY_TEST_PREFIX, - UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX) - conf.load('xwaf.xcommon') - -def build(bld): - if not bld.variant: - print('Adding test runners to build queue') - trgt = [ - c for c in TARGETS if c == bld.options.target - ] - - if len(trgt) == 0: - bld.fatal('specify a target with --target.\nAvailable targets: {}'.format(', '.join(TARGETS))) - return - - for name in UNITY_TESTS: - Options.commands.insert(0, 'build_' + name + '_' + trgt[0]) - #Options.commands.insert(0, 'build_' + name) - print('Build queue {}'.format(Options.commands)) - else: - print('Building runner {}'.format(bld.runner)) - bld.env.XSCOPE = bld.path.find_resource('config.xscope') - - depends_on = ['lib_xua', - 'lib_xud', - 'lib_spdif', - 'lib_mic_array', - 'lib_logging', - 'lib_xassert', - 'Unity'] - - makefile_opts = {} - makefile_opts['SOURCE_DIRS'] = ['src', bld.directory, os.path.join('runners',bld.runner)] - if(bld.target == 'xcoreai'): - print('TARGET XCOREAI') - makefile_opts['TARGET'] = ['XCORE-AI-EXPLORER'] - else: - print('TARGET XCORE200') - makefile_opts['TARGET'] = ['XCORE-200-EXPLORER'] - - makefile_opts['INCLUDE_DIRS'] = ['src', - bld.directory, - '../../lib_xua/api', - '../../lib_xua/src/core/pdm_mics', - '../../lib_xua/src/hid', - '../../../lib_xud/lib_xud/src/user/class'] - - makefile_opts['XCC_FLAGS'] = ['-O2', - '-g', - '-Wall', - '-DHID_CONTROLS=1', - '-DUNITY_SUPPORT_64', - '-DUNITY_INCLUDE_DOUBLE', - '-DXUD_CORE_CLOCK=600', - '-DXUD_SERIES_SUPPORT=4'] - - makefile_opts['APP_NAME'] = [bld.variant] - makefile_opts['USED_MODULES'] = depends_on - makefile_opts['XCOMMON_MAKEFILE'] = ['Makefile.common'] - bld.do_xcommon(makefile_opts) - - -def test(bld): - # Call pytest to run Unity tests inside axe or xsim - try: - test_output = subprocess.check_output(['pytest']) - except subprocess.CalledProcessError as e: - # pytest exits non-zero if an assertion fails - test_output = e.output - print(test_output) - - -# TODO: ensure clean deletes the runners dir/ -def dist(ctx): - ctx.load('xwaf.xcommon') - -def distcheck(ctx): - ctx.load('xwaf.xcommon')