With --test the gamepad, mouse picking, UART, ZeroMQ and motion telemetry are
off, the hidden window gets no key, mouse, scroll or char callbacks, and the
window procedure that takes WM_COPYDATA commands from other programs isn't
installed, so nothing but the scenery drives a test run.
The test manager is reached through simulation::test() instead of a global,
the result cell is owned by a unique_ptr until the memory table takes it, the
cell value is compared with a margin, strings are built with std::format and
the --test settings moved to apply_test_mode_settings(). The driver's signal
check is folded into UpdateSpeedPosition(), which updates a speed table entry
and reports a signal at stop the head of the train just passed. The test.*
keys are listed with the other settings at the start of the log.
eu07 --test -s <scenery> [--test-timeout <s>] runs the scenery in ghostview with
the null renderer (models loaded, no textures), a hidden window, no sound,
network or python, and in fixed steps of 0.03 s of simulation time per frame
(three 10 ms physics steps) as fast as it can. Without -seed it uses a fixed
seed, so two runs give byte-identical physicslogs.
The scenery reports its result to the __test.result memory cell, which the
simulator always creates: value 1 passes once every vehicle has stood still for
1 s, value 0 fails at once. The run ends with exit code
0 pass, 1 fail,
2 a train passed a signal at stop (SetVelocity 0 0, as seen in its driver's
speed table; SetVelocity 0 x on automatic block doesn't count),
3 timeout (test.timeout, default 3600 s of simulation time),
4 standstill (every vehicle still for test.standstill s, default 300, with no
result; a pass with test.pass_on_standstill yes),
5 error (no -s, the scenery didn't load or has no vehicles, an exception).
The test.* keys go in the scenery's config block; test.fail_if_stop_overran no
only logs a signal passed at danger. Outside the test mode a signal passed at
danger is logged.
The null renderer gets a ui backend which draws nothing, hands out the null
texture and material instead of throwing, and ends the ui frame. A GL context
is still made for the hidden window, without asking for a version, as parts
of the ui create textures with gl calls of their own. No crash dialog or
message box in test mode; the window code no longer needs a monitor.
tests/scenarios/run.py builds a test scenery from a game install's scenery
(own trainsets removed, case fragment added), runs eu07 in ghostview with
physicslog on, and checks what the vehicle did: starts, max speed, brakes,
stops. First case: EU07 on td.scn driven by AI on the td/rozklad timetable,
with the Stacja1 exit route set by an event launcher instead of shift+4.
eu07 gets a -ini <file> option: an extra settings file read after eu07.ini,
so a test run can switch to a window and turn on physicslog without touching
the user's config.
Local only: scenery and vehicle data are not in the repo.
- the log stops through a flag kept in a function, not a global one
- the messages are put together with std::format
- a vehicle dropped on a trainset in the launcher goes in once the
trainset is drawn, so the vehicles are gone through with a plain loop
again; the drop split into a new vehicle and a moved one
- the scenario that can't be opened is caught as the runtime error it
is, a file name outside of the code page as the system error it is
- a track of an unknown type is left out through the pointer owning it
- the keyword of a track type written out by a function of its own
- the picked node forgotten with ranges::replace_if, the empty
Forget_Node of the base renderer told why it is empty
- FileCased takes the path it works on
A scenario failing past its opening popped the loader mode and went on to pop it again and push the driver mode with the scenery half loaded; it goes back to the launcher as one that can't be opened. FileCased() goes through the folder only where the file system tells the case apart: on Windows a missing file was looked for in its whole folder for nothing, and a name outside the code page threw from the parser. The Discord thread sleeps a quarter of a second outside the driver mode, not to hold the closing up.
The log service stopped with applicationQuitOrder, so whatever was logged while the simulator closed was lost; it now runs until StopLogService() and writes out what came before. The closing logs each of its steps with the time since it began, to tell where a closing that hangs stops. Outside the driver mode the Discord thread spun a core in its loop, it now sleeps a second between the checks.
Consolidates platform-specific config-directory logic (APPDATA on Windows,
~/Library/Application Support on macOS, ~/.config on Linux) into a single
user_config_path(filename) function in utilities/utilities.{cpp,h}, removing
copy-pasted #ifdef blocks from application.cpp, editor/editorSettings.cpp,
input/{keyboard,gamepad,drivermouse,uart}input.cpp.