Add a basic overview of writing and running tests

This commit is contained in:
jacqueline 2024-06-25 16:09:36 +10:00
parent 8db57d6dc5
commit 525ed2ae1b
9 changed files with 141 additions and 27 deletions

View file

@ -1,9 +1,3 @@
<!--
Copyright 2023 jacqueline <me@jacqueline.id.au>
SPDX-License-Identifier: CC0-1.0
-->
# 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

28
TESTING.md Normal file
View file

@ -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.

View file

@ -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_;

View file

@ -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)

View file

@ -12,7 +12,7 @@
namespace drivers {
TEST_CASE("battery measurement", "[integration]") {
TEST_CASE("battery adc", "[integration]") {
AdcBattery battery;
SECTION("voltage is within range") {

View file

@ -0,0 +1,32 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "drivers/samd.hpp"
#include <cstdint>
#include "catch2/catch.hpp"
#include "i2c_fixture.hpp"
namespace drivers {
TEST_CASE("samd21 interface", "[integration]") {
I2CFixture i2c;
auto samd = std::make_unique<Samd>();
REQUIRE(samd);
SECTION("usb reports connection") {
samd->UpdateUsbStatus();
auto status = samd->GetUsbStatus();
REQUIRE(status == Samd::UsbStatus::kAttachedIdle);
}
}
} // namespace drivers

View file

@ -0,0 +1,7 @@
# Copyright 2023 jacqueline <me@jacqueline.id.au>
#
# SPDX-License-Identifier: GPL-3.0-only
idf_component_register(
SRC_DIRS "battery"
INCLUDE_DIRS "." REQUIRES catch2 cmock tangara fixtures)

View file

@ -0,0 +1,67 @@
/*
* Copyright 2024 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "battery/battery.hpp"
#include <cstdint>
#include <memory>
#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<drivers::Samd> samd{drivers::Samd::Create()};
FakeAdc* adc = new FakeAdc{}; // Freed by Battery.
Battery battery{*samd, std::unique_ptr<drivers::AdcBattery>{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

View file

@ -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)