Add a basic overview of writing and running tests
This commit is contained in:
parent
8db57d6dc5
commit
525ed2ae1b
9 changed files with 141 additions and 27 deletions
24
BUILDING.md
24
BUILDING.md
|
@ -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
28
TESTING.md
Normal 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.
|
|
@ -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_;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace drivers {
|
||||
|
||||
TEST_CASE("battery measurement", "[integration]") {
|
||||
TEST_CASE("battery adc", "[integration]") {
|
||||
AdcBattery battery;
|
||||
|
||||
SECTION("voltage is within range") {
|
||||
|
|
32
src/drivers/test/test_samd.cpp
Normal file
32
src/drivers/test/test_samd.cpp
Normal 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
|
7
src/tangara/test/CMakeLists.txt
Normal file
7
src/tangara/test/CMakeLists.txt
Normal 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)
|
67
src/tangara/test/battery/test_battery.cpp
Normal file
67
src/tangara/test/battery/test_battery.cpp
Normal 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
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue