test_routes_server_lifecycle
Tests for the routes server lifecycle across a session reload.
A reload tears the session down and builds a new one on a worker thread, so every step exercised here runs off Revit's STA UI thread, where an escaping exception terminates the host. Two properties matter:
- stopping must not raise, and must release the port whatever else fails - a port left bound is a port the reloaded session cannot rebind;
- the registration must be cleared even when the stop failed, otherwise the next activation hands the dead server back instead of binding, and the reloaded session silently serves no routes at all.
Both were lost in the reload that killed Revit (#3473). Nothing on this path may write to stderr either, for the reason the request path may not: an exception printed off a worker thread is what terminated the process.
Classes
RoutesServerStopTests
Bases: TestCase
Tests for RoutesServer.stop, which runs on the session reload thread.
Methods:
setUp()
Isolate the server module logger.
tearDown()
test_stop_joins_the_accept_loop_and_closes_the_socket()
A stop shuts the accept loop down, releases the socket and forgets the thread.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_stop_releases_the_port_even_when_shutdown_fails()
A failed shutdown must still release the listening socket.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_stop_contains_failures_of_either_kind()
Shutdown and close failures are contained whatever exception type is raised.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_stop_does_not_wait_on_a_dead_accept_loop()
An exited accept loop can never acknowledge, so the stop must skip the wait.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_stop_gives_up_on_a_loop_that_never_acknowledges()
A shutdown that never returns must not hold the reload.
BaseServer.shutdown blocks until the accept loop acknowledges, with no timeout of its own, so a wedged loop would keep the port bound and the registration alive - the reload would never finish. The wait is bounded instead, and the port is released on the way out either way.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_stop_spends_one_budget_across_both_waits()
A wedged loop must not cost the reload one timeout per wait.
The shutdown request and the join that follows each get a timeout, so taken separately they let a reload wait twice what it was promised. The second wait has to be given only what the first one left over.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_stop_does_not_wait_on_a_server_that_never_started()
A server whose accept loop never started must still be releasable.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_stop_is_safe_to_call_twice()
Deactivation may reach an already stopped server.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_stop_does_not_write_to_stderr()
A failing stop must never reach the script output console.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
RoutesServerStartTests
Bases: TestCase
Tests for the accept loop that RoutesServer.start owns.
Methods:
setUp()
Isolate the server module logger.
tearDown()
test_a_live_server_runs_exactly_one_accept_loop()
A second start must not put a second accept loop on the same socket.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_a_stopped_server_reports_itself_as_not_running()
A stopped server no longer reports a live accept loop.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_stop_releases_the_port_for_the_next_session()
The next session can bind the port the stopped server had.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_request_threads_are_not_joined_on_close()
A request in flight during a reload must not hold the host.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
DeactivateServerTests
Bases: _LifecycleTestCase
Tests for routes.deactivate_server, the reload's teardown step.
Methods:
test_deactivation_stops_and_deregisters_the_server()
Deactivation stops the server and drops its registration.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_deactivation_clears_registration_even_when_stop_fails()
A dead server must not stay registered: the next activation would skip binding.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_deactivation_survives_a_failing_unregister()
A failed deregistration still clears the session env var.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_deactivation_deregisters_when_clearing_the_env_var_fails()
A failing env var write must not skip the deregistration.
The deregistration is what keeps the next session from being handed this server, so it cannot be the step that an earlier failure takes out.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_deactivation_with_no_active_server_does_nothing()
Deactivating with nothing active touches nothing.
test_deactivation_does_not_write_to_stderr()
A failing deactivation must never reach the script output console.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_a_failed_stop_is_reported()
A stop that failed during a reload has to be visible.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
setUp()
Swap in the recording logger, env var store and registry stubs.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
tearDown()
Restore the session env var access and the serverinfo registry.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
ActivateServerTests
Bases: _LifecycleTestCase
Tests for routes.activate_server, the reload's last step.
Methods:
test_activation_hands_back_a_server_that_is_already_active()
Activating twice in one session reuses the running server.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_activation_replaces_a_server_whose_accept_loop_exited()
A registered server that no longer accepts is torn down, not handed back.
Handing it back is the dead registration #3473 was reported for, reached from the other end: the server is in the env var but nothing is listening.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_init_stops_the_active_server_before_activating_again()
Every session load still stops the previous server.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_a_failed_activation_is_reported()
A server that cannot bind the port is reported.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
test_a_failed_activation_survives_a_failing_unregister()
A failed activation reports both failures instead of raising the second.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
setUp()
Swap in the recording logger, env var store and registry stubs.
Source code in pyrevitlib/pyrevit/unittests/test_routes_server_lifecycle.py
tearDown()
Restore the session env var access and the serverinfo registry.