Module test_loop

Module test_loop 

Source
Expand description

This is a framework to test async code in a way that is versatile, deterministic, easy-to-setup, and easy-to-debug.

The primary concept here is an event loop that the test framework controls. The event loop acts as a central hub for all messages, including Actix messages, network messages, timers, etc. Business logic is only executed as a response to such events.

This brings several major benefits:

  • Ease of setup:

    • There is no need to set up mock objects that implement some message sender interface, instead, the test loop provides a sender object that can be used to send messages to the event loop. For example, suppose we were to make a Client whose constructor requires a shards_manager adapter; instead of having to make a mock for the shards_manager adapter, we can simply register the shards_manager actor with testloop and pass in its sender.
    • Compared to writing synchronous tests, there is no need to manually deliver network messages or handle actix messages at certain points of the test. Instead, the event loop will invoke the appropriate event handlers whenever there is any event remaining in the event loop. This ensures that no messages are ever missed.
    • Test setup code can be modular and reusable, because the test specification consists entirely of registering the data and actors. Rather than passing a giant callback into a giant setup(…) function to customize one part of a huge integration test, we can flexibly compose specific modules with event handlers. For example, we may add an event handler to route all ShardsManager-related network messages reliably, and at the same time another event handler to drop 50% of Block network messages. Also, we can use an event handler as long as it is relevant for a test (i.e. a ForwardShardsManagerRequest event handler can be used as long as the test involves ShardsManagers), regardless of the exact architecture of the test.
  • Debuggability:

    • Because ALL execution is in response of events, the whole test can be cleanly segmented into the response to each event. The framework automatically outputs a log message at the beginning of each event execution, so that the log output can be loaded into a visualizer to show the exact sequence of events, their relationship, the exact contents of the event messages, and the log output during the handling of each event. This is especially useful when debugging multi-instance tests.
  • Determinism:

    • Many tests, especially those that involve multiple instances, are most easily written by spawning actual actors and threads. This however makes the tests inherently asynchronous and may be more flaky.
    • The test loop framework also provides a synchronous and deterministic way to invoke timers without waiting for the actual duration. This makes tests run much faster than asynchronous tests.
  • Versatility:

    • A test can be constructed with any combination of components. The framework does not dictate what components should exist, or how many instances there should be. This allows for both small and focused tests, and large multi-instance tests.
    • Timed tests can be written to check the theoretical performance of certain tasks, such as distributing chunks to other nodes within X milliseconds provided that network messages have a 10ms delay.
    • The framework does not require major migrations to existing code, e.g. it is compatible with the Actix framework and futures.

A note on the order of execution of the events: all events that are due at the same timestamp are executed in FIFO order. For example, if the events are emitted in the following order: (A due 100ms), (B due 0ms), (C due 200ms), (D due 0ms), (E due 100ms) then the actual order of execution is B, D, A, E, C.

Modules§

data
futures
Support for futures in TestLoop.
pending_events_sender
sender

Structs§

TestLoopV2
Main struct for the Test Loop framework. The TestLoopData should contain all the business logic state that is relevant to the test. All possible Event that are sent to the event loop are callbacks. See TestLoopData for mode details.