From 525ed2ae1bc710e2a80de0fc10300da83d594ccb Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 25 Jun 2024 16:09:36 +1000 Subject: [PATCH] Add a basic overview of writing and running tests --- BUILDING.md | 24 +------- TESTING.md | 28 ++++++++++ src/drivers/include/drivers/adc.hpp | 4 +- src/drivers/test/CMakeLists.txt | 2 +- src/drivers/test/test_adc.cpp | 2 +- src/drivers/test/test_samd.cpp | 32 +++++++++++ src/tangara/test/CMakeLists.txt | 7 +++ src/tangara/test/battery/test_battery.cpp | 67 +++++++++++++++++++++++ test/CMakeLists.txt | 2 +- 9 files changed, 141 insertions(+), 27 deletions(-) create mode 100644 TESTING.md create mode 100644 src/drivers/test/test_samd.cpp create mode 100644 src/tangara/test/CMakeLists.txt create mode 100644 src/tangara/test/battery/test_battery.cpp diff --git a/BUILDING.md b/BUILDING.md index 094c1551..571aa359 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1,9 +1,3 @@ - - # Building and flashing 1. Make sure you've got all of the submodules in this repo correctly initialised: @@ -34,28 +28,14 @@ There is also a `.env.fish` for fish users. onto your board, something like: ``` -idf.py -p /dev/ttyUSB0 -b 1000000 flash +idf.py -p /dev/serial/by-id/usb-cool_tech_zone_Tangara_* -b 1000000 flash ``` (give or take the correct serial port) # Running tests -(Note: tests are currently broken, and have been for a while. Sorry! IOU a working test suite by the time we ship devices :)) - -Tests are implemented as a separate application build, located in the `test` -directory. We use Catch2 as our test framework. - -To run them, navigate to the test directory, then build and flash as normal. -Connect to your device via UART, and you will be presented with a terminal -prompt that you may run tests from. - -To add new tests to a components, you must: - 1. Create a `test` subcomponent within that component. See `drivers/test` for - an example of this. - 2. Include the component in the test build and list of testable components, in - `test/CMakeLists.txt`. - +See `TESTING.md` for an overview of how to write and run our on-device test suite. # VSCode setup diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 00000000..3776c239 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,28 @@ +# Running tests + +Our test suite currently must be run on an actual device. A subset of our tests may run correctly on a bare ESP32-WROVER module, but in general they do rely on the real Tangara hardware being available. + +Tests are implemented as a separate application build, located in the `test` +directory. We use Catch2 as our test framework. + +To run them, navigate to the test directory, then build and flash as normal. e.g. + +``` +idf.py -p /dev/serial/by-id/usb-cool_tech_zone_Tangara_* app-flash +``` + +Connect to your device via serial, and you will be presented with + variant of our standard dev console. To run all tests, simply execute `catch` in the console. + +The `catch` command accepts additional arguments as if it were a standard Catch2 test runner binary. You can therefore see a brief guide to the available options with `catch -?`. + +# Writing tests + +Tests live within the `test` subcomponent of the component that the tests are written for. In practice, this means that device driver tests should live in `src/drivers/test`, whilst most other tests live in `src/tangara/test`. + +## Tags + +Catch2 has a flexible system of test tags that can be used to categorise different test cases. Feel free to add new tags as-needed. In general, most tests should be tagged with one of: + +- `[integration]`, for tests that rely on the hardware being in a specific state +- `[unit]`, for tests that operate purely in-memory, either without any additional device drivers needed, or by using test doubles rather than real drivers. diff --git a/src/drivers/include/drivers/adc.hpp b/src/drivers/include/drivers/adc.hpp index 3e94a9ee..2fd8a626 100644 --- a/src/drivers/include/drivers/adc.hpp +++ b/src/drivers/include/drivers/adc.hpp @@ -22,12 +22,12 @@ class AdcBattery { public: static auto Create() -> AdcBattery* { return new AdcBattery(); } AdcBattery(); - ~AdcBattery(); + virtual ~AdcBattery(); /** * Returns the current battery level in millivolts. */ - auto Millivolts() -> uint32_t; + virtual auto Millivolts() -> uint32_t; private: adc_oneshot_unit_handle_t adc_handle_; diff --git a/src/drivers/test/CMakeLists.txt b/src/drivers/test/CMakeLists.txt index ff1dab0b..f18dc357 100644 --- a/src/drivers/test/CMakeLists.txt +++ b/src/drivers/test/CMakeLists.txt @@ -3,5 +3,5 @@ # SPDX-License-Identifier: GPL-3.0-only idf_component_register( - SRCS "test_adc.cpp" "test_storage.cpp" "test_dac.cpp" + SRCS "test_adc.cpp" "test_storage.cpp" "test_dac.cpp" "test_samd.cpp" INCLUDE_DIRS "." REQUIRES catch2 cmock drivers fixtures) diff --git a/src/drivers/test/test_adc.cpp b/src/drivers/test/test_adc.cpp index df103af3..a8dad4bd 100644 --- a/src/drivers/test/test_adc.cpp +++ b/src/drivers/test/test_adc.cpp @@ -12,7 +12,7 @@ namespace drivers { -TEST_CASE("battery measurement", "[integration]") { +TEST_CASE("battery adc", "[integration]") { AdcBattery battery; SECTION("voltage is within range") { diff --git a/src/drivers/test/test_samd.cpp b/src/drivers/test/test_samd.cpp new file mode 100644 index 00000000..c466d88e --- /dev/null +++ b/src/drivers/test/test_samd.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2023 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "drivers/samd.hpp" + +#include + +#include "catch2/catch.hpp" + +#include "i2c_fixture.hpp" + +namespace drivers { + +TEST_CASE("samd21 interface", "[integration]") { + I2CFixture i2c; + auto samd = std::make_unique(); + + REQUIRE(samd); + + SECTION("usb reports connection") { + samd->UpdateUsbStatus(); + + auto status = samd->GetUsbStatus(); + + REQUIRE(status == Samd::UsbStatus::kAttachedIdle); + } +} + +} // namespace drivers diff --git a/src/tangara/test/CMakeLists.txt b/src/tangara/test/CMakeLists.txt new file mode 100644 index 00000000..728c06b0 --- /dev/null +++ b/src/tangara/test/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright 2023 jacqueline +# +# SPDX-License-Identifier: GPL-3.0-only + +idf_component_register( + SRC_DIRS "battery" + INCLUDE_DIRS "." REQUIRES catch2 cmock tangara fixtures) diff --git a/src/tangara/test/battery/test_battery.cpp b/src/tangara/test/battery/test_battery.cpp new file mode 100644 index 00000000..7b55bd59 --- /dev/null +++ b/src/tangara/test/battery/test_battery.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2024 jacqueline + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include "battery/battery.hpp" + +#include +#include + +#include "catch2/catch.hpp" +#include "drivers/adc.hpp" +#include "i2c_fixture.hpp" + +namespace battery { + +class FakeAdc : public drivers::AdcBattery { + private: + uint32_t mv_; + + public: + virtual auto Millivolts() -> uint32_t override { return mv_; } + auto Millivolts(uint32_t mv) -> void { mv_ = mv; } +}; + +TEST_CASE("battery charge state", "[unit]") { + I2CFixture i2c; + + // FIXME: mock the SAMD21 as well. + std::unique_ptr samd{drivers::Samd::Create()}; + FakeAdc* adc = new FakeAdc{}; // Freed by Battery. + Battery battery{*samd, std::unique_ptr{adc}}; + + SECTION("full charge is 100%") { + // NOTE: in practice, our curve-fitting slightly undershoots + adc->Millivolts(4210); + + battery.Update(); + + auto state = battery.State(); + REQUIRE(state.has_value()); + REQUIRE(state->percent == 100); + } + + SECTION("empty charge is 0%") { + adc->Millivolts(3000); + + battery.Update(); + + auto state = battery.State(); + REQUIRE(state.has_value()); + REQUIRE(state->percent == 0); + } + + SECTION("overcharge is clamped to 100%") { + adc->Millivolts(5000); + + battery.Update(); + + auto state = battery.State(); + REQUIRE(state.has_value()); + REQUIRE(state->percent == 100); + } +} + +} // namespace battery diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 072a350f..ee553498 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,6 +27,6 @@ list(APPEND EXTRA_COMPONENT_DIRS ) # List all components that include tests here. -set(TEST_COMPONENTS "drivers") +set(TEST_COMPONENTS "drivers" "tangara") project(device_tests)