mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 09:49:19 +02:00
eu07_unit_tests: C++ tests (doctest) of the simulation, which set up the
conditions, run the code and check the outcome. No game data, no window.
- the code under test is the core library (eu07_core), the very objects
the game links, built with the game's settings; engine_stubs.cpp stands
in for the logs, settings and scenery layers the library reaches for.
python gets linked with the settings but isn't needed to run the tests
- test vehicles are JSON definitions in tests/unit/data, with the sections
and keys of the game's vehicle definitions (tables as "rows", _keys are
comments), changed by the tests where needed and loaded from memory by
the game's own loader; an unknown section is an error rather than
silently skipped
- test::vehicle creates the mover the way TDynamicObject::Init() does and
steps it the way the simulation loop does, on a straight track of given
friction and gradient, optionally holding the brake pipe pressure the
way a coupled locomotive would
- the random numbers of the simulation are reseeded before every test
case, so a test gives the same result alone (IDE) or among the others
- building the game runs the tests at the end, once the game is linked,
and prints a one line summary ("Unit tests: 11/11 passed (80 checks)").
A failure prints the full report and fails the build, in CI and locally
alike, and removes the game binary so the next build reruns them. A
change of the tests alone relinks the game so they run (Ninja and
Makefile generators). -DWITH_TESTS=OFF leaves them out
- first tests: definitions reach the vehicle, KE service application and
release, deeper reduction brakes harder, every implemented distributor
valve charges/applies/releases, service braking stops a moving wagon
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
94 lines
3.2 KiB
C++
94 lines
3.2 KiB
C++
#include "stdafx.h"
|
|
#include "doctest.h"
|
|
#include "vehicle.h"
|
|
|
|
using test::definition;
|
|
using test::vehicle;
|
|
|
|
// the brake pipe of a lone wagon is held at a pressure the way a coupled locomotive would hold it.
|
|
// pressures are in bar, the brake pipe runs at 5.0, a 1.5 bar reduction is a full service application.
|
|
|
|
TEST_CASE("brakes: KE wagon applies up to its tare limit on a service application and releases again") {
|
|
vehicle wagon{definition::load("wagon")}; // empty wagon, TareMaxBP=2.3
|
|
|
|
wagon.hold_brake_pipe(5.0);
|
|
wagon.run(60.0);
|
|
CHECK(wagon->BrakePress < 0.1);
|
|
CHECK(wagon->Hamulec->GetBRP() > 4.5); // auxiliary reservoir charged
|
|
|
|
wagon.hold_brake_pipe(3.5);
|
|
wagon.run(10.0);
|
|
CHECK(wagon->BrakePress == doctest::Approx(2.3).epsilon(0.05));
|
|
|
|
wagon.hold_brake_pipe(5.0);
|
|
wagon.run(60.0);
|
|
CHECK(wagon->BrakePress < 0.1);
|
|
}
|
|
|
|
TEST_CASE("brakes: a deeper brake pipe reduction brakes harder") {
|
|
auto applied = [](double Pipe) {
|
|
vehicle wagon{definition::load("wagon").set("Brake", "TareMaxBP", 3.8)}; // no tare limit in the way
|
|
wagon.hold_brake_pipe(5.0);
|
|
wagon.run(60.0);
|
|
wagon.hold_brake_pipe(Pipe);
|
|
wagon.run(20.0);
|
|
return wagon->BrakePress;
|
|
};
|
|
auto const light{applied(4.6)};
|
|
auto const medium{applied(4.2)};
|
|
auto const full{applied(3.5)};
|
|
CHECK(light > 0.1);
|
|
CHECK(light < medium);
|
|
CHECK(medium < full);
|
|
CHECK(full <= 3.8 + 0.1); // MaxBP
|
|
}
|
|
|
|
// every distributor valve the engine implements goes through the same cycle; the ESt family needs the size
|
|
// of its reservoirs, as in the real .fiz files using it
|
|
TEST_CASE("brakes: every implemented distributor valve charges, applies and releases") {
|
|
for (auto const *valve : {"W", "K", "KE", "ESt", "ESt3", "NESt3", "ESt4", "ESt3AL2", "LSt", "EStED", "EP1", "EP2", "CV1"}) {
|
|
CAPTURE(valve);
|
|
vehicle wagon{definition::load("wagon").set("Brake", "BrakeValve", valve).set("Brake", "Size", 16)};
|
|
wagon.hold_brake_pipe(5.0);
|
|
wagon.run(90.0);
|
|
CHECK(wagon->BrakePress < 0.1);
|
|
wagon.hold_brake_pipe(3.5);
|
|
wagon.run(20.0);
|
|
CHECK(wagon->BrakePress > 1.0);
|
|
CHECK(wagon->BrakePress <= 3.8 + 0.1); // MaxBP
|
|
wagon.hold_brake_pipe(5.0);
|
|
wagon.run(90.0);
|
|
CHECK(wagon->BrakePress < 0.1);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("brakes: service braking stops a wagon running at 60 km/h and keeps it stopped") {
|
|
vehicle wagon{definition::load("wagon"), 60.0};
|
|
wagon.hold_brake_pipe(5.0);
|
|
wagon.run(10.0);
|
|
REQUIRE(wagon.speed() > 50.0); // still running before the braking
|
|
|
|
wagon.hold_brake_pipe(3.5);
|
|
wagon.run(30.0);
|
|
CHECK(std::abs(wagon.speed()) < 0.1);
|
|
wagon.run(30.0);
|
|
CHECK(std::abs(wagon.speed()) < 0.1);
|
|
}
|
|
|
|
TEST_CASE("brakes: a released wagon rolls on") {
|
|
vehicle wagon{definition::load("wagon"), 60.0};
|
|
wagon.hold_brake_pipe(5.0);
|
|
wagon.run(30.0);
|
|
CHECK(wagon.speed() > 50.0);
|
|
CHECK(wagon.speed() < 60.0); // running resistance slows it down a little
|
|
}
|
|
|
|
// the example in README.md
|
|
TEST_CASE("brakes: service braking stops a heavier wagon too") {
|
|
// the default wagon, with the keys the test is about changed
|
|
test::vehicle wagon{test::definition::load("wagon").set("Param", "M", 60000), 40.0 /* km/h */};
|
|
wagon.hold_brake_pipe(3.5); // bar, as a coupled locomotive would hold it
|
|
wagon.run(40.0 /* s */, test::track{0.15 /* friction */, 0.0 /* gradient */});
|
|
CHECK(std::abs(wagon.speed()) < 0.1);
|
|
}
|