Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ migrate a file to the wrappers when you're already touching it and it's straight
Windows/Linux; `~/Library/Application Support/SerialPrograms/DownloadedResources` on macOS).
- Headless Linux: `QT_QPA_PLATFORM=offscreen` and
`TESSDATA_PREFIX=<bin>/Resources/Tesseract`.
- `SerialProgramsTests` (built by default next to `SerialPrograms`; `-DTEST_RUNNER_EXE=OFF`
turns it off) runs the same tests without the GUI, settings or device scans, so it can run
while SerialPrograms is open: `ninja SerialProgramsTests`, then
`SerialProgramsTests --filter <text>` (repeatable). Its exit code is the number
of failed tests. Same requirements as above.
- Test framework: test classes derive `UnitTest` (`Common/Cpp/TestRunners/UnitTest.h`) and are
collected via per-module `add_tests(UnitTestDatabase&)` — see
`SerialPrograms/Source/Tests/CommandLineTests.h`.
Expand Down
7 changes: 7 additions & 0 deletions SerialPrograms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ find_package(Threads REQUIRED)
# Define a toggle option (ON by default)
option(COMMAND_LINE_EXE "Build the optional executable" ON)

# Build SerialProgramsTests, which runs the unit tests without the GUI.
option(TEST_RUNNER_EXE "Build the unit test executable, SerialProgramsTests" ON)

# Build only the GUI-free core: CoreLib and SerialProgramsCommandLine. This needs
# no Qt, OpenCV, ONNX Runtime, Tesseract, DPP or Discord SDK, so none of them are
# searched for.
Expand Down Expand Up @@ -336,6 +339,10 @@ if(NOT EXISTS "${DEST_DIR}/Scripts")
)
endif()

if(TEST_RUNNER_EXE)
include(Source/Tests/TestRunnerExecutable.cmake)
endif()

if (WIN32)
add_library(OpenCV_lib IMPORTED UNKNOWN)
target_include_directories(SerialProgramsLib SYSTEM PRIVATE ../3rdParty/opencv-4.12.0/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ namespace PokemonAutomation{
namespace ComputerPrograms{


namespace{

bool matches_any(std::string_view test_name, std::span<const std::string_view> filters){
if (filters.empty()){
return true;
}
for (std::string_view filter : filters){
if (test_name.contains(filter)){
return true;
}
}
return false;
}

} // namespace





Expand Down Expand Up @@ -84,6 +101,17 @@ const UnitTestDatabase& UNIT_TESTS_ALL(){
static UnitTestDatabase database = make_UNIT_TESTS_ALL();
return database;
}

std::vector<std::string> unit_test_names(std::span<const std::string_view> filters){
std::vector<std::string> ret;
for (const auto& [test_name, test] : UNIT_TESTS_ALL()){
if (matches_any(test_name, filters)){
ret.emplace_back(test_name);
}
}
return ret;
}

StringSelectDatabase make_TEST_DATABASE(){
StringSelectDatabase database;
for (auto& test : UNIT_TESTS_ALL()){
Expand Down Expand Up @@ -355,15 +383,15 @@ void UnitTestRunner::on_test_finished(



bool CommandLineUnitTestRunner::run(bool augment_color_distortions){
size_t CommandLineUnitTestRunner::run(bool augment_color_distortions, std::span<const std::string_view> filters){
if (augment_color_distortions){
// Load the distortion data before any test runs. Each `ImageUnitTest` checks the global
// manager when it runs, and only runs with distortions if it is loaded.
try{
initialize_test_image_distortions(UNIT_TEST_RESOURCE_PATH());
}catch (Exception& e){
m_logger.log("Unable to load the test image distortions: " + e.to_str(), COLOR_RED);
return true;
return 1;
}
const TestImageDistortionManager& manager = *test_image_distortion_manager();
std::string labels;
Expand All @@ -381,8 +409,10 @@ bool CommandLineUnitTestRunner::run(bool augment_color_distortions){
GlobalThreadPools::unlimited_normal()
);
runner.add_listener(*this);
for (const auto& test : UNIT_TESTS_ALL()){
runner.add_test(test.second);
for (const auto& [test_name, test] : UNIT_TESTS_ALL()){
if (matches_any(test_name, filters)){
runner.add_test(test);
}
}
runner.run();

Expand All @@ -392,7 +422,7 @@ bool CommandLineUnitTestRunner::run(bool augment_color_distortions){
"\n Failed: " + tostr_u_commas(m_failed_tests.load(std::memory_order_acquire)) +
"\n Skipped: " + tostr_u_commas(m_skipped_tests.load(std::memory_order_acquire))
);
return m_failed_tests.load(std::memory_order_acquire) > 0;
return m_failed_tests.load(std::memory_order_acquire);
}
void CommandLineUnitTestRunner::on_test_finished(
std::shared_ptr<const UnitTest> test,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
#define PokemonAutomation_ComputerPrograms_UnitTestRunner_H

#include <atomic>
#include <span>
#include <string>
#include <string_view>
#include <vector>
#include "Common/Cpp/TestRunners/ParallelUnitTestRunner.h"
#include "Common/Cpp/Options/StaticTextOption.h"
#include "Common/Cpp/Options/EnumDropdownOption.h"
Expand Down Expand Up @@ -89,12 +93,13 @@ class CommandLineUnitTestRunner : public PokemonAutomation::UnitTestRunner::List
, m_failed_tests(0)
{}

// Run all the unit tests. Return true if any test failed.
// Run the unit tests whose names contain any of `filters`, or all of them if `filters` is
// empty. Return the number of failed tests.
// If `augment_color_distortions` is true, also run each image test under different color
// distortion augmentation to widen the test coverage, using the distortion data in the
// CaptureCardCalibration/ folder of the test repo. If that data can't be loaded, log the
// error and return true without running any test.
bool run(bool augment_color_distortions);
// error and return 1 without running any test.
size_t run(bool augment_color_distortions, std::span<const std::string_view> filters);


private:
Expand All @@ -112,6 +117,11 @@ class CommandLineUnitTestRunner : public PokemonAutomation::UnitTestRunner::List
};


// The names of the unit tests that contain any of `filters`, or of all the tests if `filters`
// is empty, in name order.
std::vector<std::string> unit_test_names(std::span<const std::string_view> filters);




}
Expand Down
9 changes: 7 additions & 2 deletions SerialPrograms/Source/Tests/CommandLineTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ namespace PokemonAutomation{


int run_command_line_tests(bool augment_color_distortions){
return run_unit_tests(augment_color_distortions, {}) == 0 ? 0 : 1;
}

size_t run_unit_tests(bool augment_color_distortions, std::span<const std::string_view> filters){
install_crash_backtrace_handlers();
// Keep stdout line-buffered: when piped, stdio defaults to full
// buffering, and the last few KB of test output - including the lines
Expand All @@ -28,8 +32,9 @@ int run_command_line_tests(bool augment_color_distortions){
setvbuf(stdout, nullptr, _IOLBF, 8192);
cout << "Running parallel unit tests..." << endl;
ComputerPrograms::CommandLineUnitTestRunner runner(global_logger_command_line());
if (runner.run(augment_color_distortions)){
return 1;
size_t failed = runner.run(augment_color_distortions, filters);
if (failed > 0){
return failed;
}
cout << "Running parallel unit tests... Done!" << endl;
return 0;
Expand Down
12 changes: 11 additions & 1 deletion SerialPrograms/Source/Tests/CommandLineTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
* Run all the unit tests from the command line, without launching the GUI.
*
* How to run:
* Launch the program with the argument "--command-line-test-mode", or set
* Run the SerialProgramsTests executable (Tests/TestRunner_Main.cpp): it runs only the tests,
* with no GUI, no settings and no device scans, and takes "--filter <text>".
* Or launch the program with the argument "--command-line-test-mode", or set
* "20-GlobalSettings": "COMMAND_LINE_TESTS": "RUN" to true in SerialPrograms-Settings.json.
*
* How the tests work:
Expand Down Expand Up @@ -40,6 +42,9 @@
#ifndef PokemonAutomation_Tests_CommandLineTests_H
#define PokemonAutomation_Tests_CommandLineTests_H

#include <cstddef>
#include <span>
#include <string_view>

namespace PokemonAutomation{

Expand All @@ -51,6 +56,11 @@ namespace PokemonAutomation{
// Return 0 if all tests are passed, and 1 if any test failed or the distortion data can't be loaded.
int run_command_line_tests(bool augment_color_distortions);

// Run the unit tests whose names contain any of `filters`, or all of them if `filters` is empty,
// and print the results to stdout. Return the number of failed tests, or 1 if
// `augment_color_distortions` is set and the distortion data can't be loaded.
size_t run_unit_tests(bool augment_color_distortions, std::span<const std::string_view> filters);



}
Expand Down
19 changes: 19 additions & 0 deletions SerialPrograms/Source/Tests/TestRunnerExecutable.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This cmake file is to be included by CMakeLists.txt to build SerialProgramsTests, an executable
# that runs the unit tests without the GUI (see Source/Tests/TestRunner_Main.cpp).

add_executable(SerialProgramsTests Source/Tests/TestRunner_Main.cpp)

# The tests are registered by explicit calls, not by static constructors, so the linker may
# leave out the parts of the library that the tests don't use.
target_link_libraries(SerialProgramsTests PRIVATE SerialProgramsLib)
apply_common_target_properties(SerialProgramsTests)

# The tests read Resources/ from in or above the executable's folder, like SerialPrograms.
add_dependencies(SerialProgramsTests clone_packages)
if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/Resources")
add_custom_command(
TARGET SerialProgramsTests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "[Deploy] Copying resources ${RESOURCES_DIR} to $<TARGET_FILE_DIR:SerialProgramsTests>/Resources"
COMMAND ${CMAKE_COMMAND} -E copy_directory "${RESOURCES_DIR}" "$<TARGET_FILE_DIR:SerialProgramsTests>/Resources"
)
endif()
Loading
Loading