main() tells a test run (--test) apart before anything runs. Only a test run
catches every exception and installs a std::terminate handler, both ending the
run with the error code and a test: line; any other run keeps master's
exception handling, so on Linux an uncaught runtime_error aborts at the throw
site again.
A test run doesn't start the Discord RPC thread: it published the test
scenery as the player's presence and held the closing up to 5 s.
run.py drops a case scenery's .sbt cache after a run cut short (wall_timeout,
error, a crash, killed by run.py), which could have been writing it.
The -edit branch of the test command line parser had no effect (a token that
isn't an option is ignored either way) and is gone; its test checks what can
be seen instead.
The runtime_error the simulator throws on a fatal problem was caught on Windows
only. On Linux a test run aborted on it, with the code of abort() and no
`test:` line. It is caught there too in test mode and ends the test with the
error code and the reason; outside a test it is thrown on as before.
The crash handler of WITHDUMPGEN builds (on by default) shows a message
box before it writes the dump. It also gets the C++ exceptions nothing
catches, since it replaces the CRT's unhandled exception filter. In test
mode nobody is there to close the box, so a crashing test waited for it
until whatever started it gave up (run.py after the case's timeout,
600 s by default). The dump is still written, the exit code is the
exception's.
- `--test` is looked for before the rest of the command line is read. An
unknown option before it (e.g. `-s x.scn --test-timout 90 --test`) used
to end the run with exit code 0, which a caller reads as a pass.
- `--test-timeout` without a value, or with one that isn't a number of
seconds, fails the run instead of leaving the default or, through atof,
turning the timeout off.
- These, a missing -s, a failed start and an exception caught in main()
end the test through test_manager::conclude(), which writes the usual
`test: error (exit code 5) ... : <reason>` line to stdout and the log.
Before, `eu07 --test` alone exited with 5 and nothing anywhere: the
error went to the log queue and the process ended before the log
thread wrote it. In test mode main() now stops the log thread and
waits for it before _Exit().
Outside the test mode nothing changes: conclude() does nothing there.
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.