disruptor-cpp

repository·master·Indexed 19 days ago

https://github.com/abc-arbitrage/disruptor-cpp

A C++ port of the LMAX disruptor implementing features from Java Disruptor v3.3.7. It is designed for high-performance inter-thread communication using a ring buffer pattern.

Tokens
134.4K
Snippets
373
Records
533
Agent score
66%

What's inside disruptor-cpp

  1. Overview of Google Test and Google Mock

    master

    Google Test is a C++ testing framework that provides an XUnit-style testing environment. It includes features such as test discovery, a rich set of assertions, death tests, and support for both value-parameterized and type-parameterized tests.

    Google Mock is an extension to Google Test specifically designed for writing and using C++ mock classes. The two projects are maintained together in this repository.

  2. Overview of Google Mock

    master

    Google Mock is a C++ framework for creating and using mock classes. It is designed to help improve system design and testing quality by allowing developers to simulate complex behaviors.

    Key capabilities include:

    • Trivial Mock Creation: Use simple macros to define mock classes.
    • Rich Matchers and Actions: Validate function arguments and control mock behavior using a wide set of built-in tools.
    • Flexible Expectations: Support for unordered, partially ordered, or completely ordered function call expectations.
    • Automatic Verification: Expectations are verified automatically, eliminating the need for a manual record-and-replay cycle.
    • Extensibility: Users can define custom matchers and actions.
    • Hybrid Mocks: Supports partial mocks that combine real object logic with mocked behavior.
    • No Exceptions: The framework does not rely on C++ exceptions.
  3. Key features and benefits of Google Test

    master

    Google Test is a portable C++ testing framework designed to work across Linux, Mac OS X, Windows, and various embedded systems without requiring exceptions or RTTI.

    Key advantages include:

    • Nonfatal assertions (EXPECT_*): Allows tests to report multiple failures in a single execution cycle.
    • Informative messages: Supports stream syntax for appending context to failures (e.g., ASSERT_EQ(5, Foo(i)) << " where i = " << i;).
    • Automatic test detection: No need to manually enumerate tests.
    • Death tests: Verifies that production code triggers assertions under specific conditions.
    • SCOPED_TRACE: Provides context for assertion failures occurring inside loops or sub-routines.
    • Test filtering: Supports running specific tests using name patterns.
    • Extensibility: Users can define custom predicates, teach the framework how to print custom types, implement custom macros via the Service Provider Interface, or intercept test events to change output formats.
    // Example of informative assertion messages
    ASSERT_EQ(5, Foo(i)) << " where i = " << i;
  4. What is a Mock Object vs. a Fake Object?

    master

    In the context of testing, it is important to distinguish between Fakes and Mocks:

    • Fake objects: Have working implementations but usually take a shortcut (e.g., an in-memory file system instead of a real disk) to make operations less expensive. They are not suitable for production.
    • Mock objects: Are pre-programmed with expectations. They form a specification of the calls they are expected to receive (which methods are called, in what order, how many times, with what arguments, and what they should return).

    Google Mock is used to check the interaction between the code under test and the mock object.

  5. What is a Mock Object and how does it differ from a Fake?

    master

    In testing, a mock object implements the same interface as a real object but allows you to specify its behavior at runtime. You can define which methods are called, in what order, how many times, with which arguments, and what they should return. The primary purpose of a mock is to check the interaction between the code under test and the object being mocked.

    It is important to distinguish between Fakes and Mocks:

    • Fake: Has a working implementation but uses a shortcut (e.g., an in-memory file system instead of a real disk) to make operations faster or simpler. It is not suitable for production.
    • Mock: An object pre-programmed with expectations, which act as a specification of the calls the object is expected to receive.
  6. What is Pump and how does it work?

    master

    Pump is a meta-programming tool for C++ designed to solve the problem of repetitive code generation (e.g., creating many classes or functions that vary only by the number of arguments). Instead of writing complex scripts or relying on non-portable variadic templates, you write a .pump file containing standard C++ code interspersed with a concise meta-programming Domain-Specific Language (DSL).

    Key Characteristics:

    • Ultra-portable: The implementation is a single Python script; no installation or build process is required.
    • Non-intrusive: The syntax is designed to be compatible with standard C++ editors (like Emacs).
    • Smart Formatting: It automatically breaks long lines to fit within 80 columns and handles indentation for generated code.
    • Workflow: You write filename.pump, and the Pump compiler translates it into filename.cpp (or .h).
  7. What is Pump and when to use it

    master

    Pump is a meta-programming tool for C++ designed to solve the problem of repetitive code generation (e.g., creating many classes or functions that vary only by the number of arguments).

    Instead of writing complex scripts or relying on non-standard variadic templates/macros, you write a .pump file containing C++ code interspersed with a concise meta-language. This meta-language allows for iterations, nested loops, local variables, arithmetic, and conditionals.

    Key Benefits:

    • Ultra Portable: Implemented as a single Python script; no installation or build required.
    • Non-intrusive: Designed to be readable and compatible with standard C++ editors (like Emacs).
    • Style-aware: Automatically breaks long lines to fit within 80 columns and handles indentation correctly.
  8. Shard tests across multiple machines

    master

    To run tests in parallel across multiple machines (shards), your test runner must configure the following environment variables on each shard:

    1. GTEST_TOTAL_SHARDS: The total number of shards (must be identical on all machines).
    2. GTEST_SHARD_INDEX: The zero-based index of the current shard (must be unique across shards, in range [0, GTEST_TOTAL_SHARDS - 1]).

    Google Test will then automatically select a subset of tests for each shard so that every test function is run exactly once across the entire cluster.

    Detecting Sharding Support: To determine if a test program supports sharding, a runner can set GTEST_SHARD_STATUS_FILE to a non-existent path. If the program supports sharding, it will create this file.

    # Example: Running on Machine #1 of 3
    export GTEST_TOTAL_SHARDS=3
    export GTEST_SHARD_INDEX=1
    ./my_test
  9. How Google Test assertions and failures work

    master

    Google Test uses assertions (macros) to check conditions. An assertion can result in three states:

    • Success: The condition is met.
    • Nonfatal failure: The assertion failed, but the current function continues to execute. Use EXPECT_* macros for this.
    • Fatal failure: The assertion failed, and the current function is immediately aborted. Use ASSERT_* macros for this.

    Key Concepts

    • Tests: Use assertions to verify behavior. A test fails if an assertion fails or if the test crashes.
    • Test Case: A grouping of one or many related tests. Test cases should reflect the structure of the tested code.
    • Test Fixture: A class used when multiple tests in a test case need to share common objects or subroutines.
    • Test Program: A collection of multiple test cases.
  10. Implement test sharding for parallel execution

    master

    To run tests across multiple machines (shards) in parallel, your test runner must configure the following environment variables on each shard:

    1. GTEST_TOTAL_SHARDS: The total number of shards (must be identical on all shards).
    2. GTEST_SHARD_INDEX: The zero-based index of the current shard (e.g., 0 for the first machine, 1 for the second).

    Google Test will then automatically select a subset of tests for each shard so that every test function is run exactly once across the entire cluster.

    To detect if a test program supports sharding, a runner can set GTEST_SHARD_STATUS_FILE to a non-existent path. A sharding-aware test program will create this file to acknowledge support.

    # On Machine 0
    export GTEST_TOTAL_SHARDS=3
    export GTEST_SHARD_INDEX=0
    ./my_test
    
    # On Machine 1
    export GTEST_TOTAL_SHARDS=3
    export GTEST_SHARD_INDEX=1
    ./my_test
    
    # On Machine 2
    export GTEST_TOTAL_SHARDS=3
    export GTEST_SHARD_INDEX=2
    ./my_test
  11. Configure death test styles

    master

    Death tests can be run in different modes to balance speed and thread safety. This is controlled by the ::testing::FLAGS_gtest_death_test_style flag.

    • fast (Default): Uses fork() (on POSIX) to spawn a child. It is faster but can be problematic in multithreaded programs.
    • threadsafe: Re-executes the test binary in a new process to ensure a clean environment. This is much safer for multithreaded tests but significantly slower.

    You can set the style globally in main() or locally within a specific TEST block.

    Note: On Linux, Google Test uses clone() instead of fork() to mitigate hangs in multithreaded processes.

    // Set globally in main
    int main(int argc, char** argv) {
      ::testing::InitGoogleTest(&argc, argv);
      ::testing::FLAGS_gtest_death_test_style = "fast";
      return RUN_ALL_TESTS();
    }
    
    // Set locally for a specific test
    TEST(MyDeathTest, TestOne) {
      ::testing::FLAGS_gtest_death_test_style = "threadsafe";
      ASSERT_DEATH(ThisShouldDie(), "");
    }
  12. Enforce expectation order with After and Sequences

    master

    By default, Google Mock matches expectations in any order. To enforce a specific order, use one of the following methods:

    1. The After Clause

    Use Expectation objects to create dependencies between calls.

    • Expectation e = EXPECT_CALL(...);
    • EXPECT_CALL(...).After(e);
    • For multiple dependencies, use ExpectationSet to collect several Expectation objects and pass the set to .After().

    2. Sequences

    Use Sequence objects to group expectations into a chain.

    • Create a Sequence s;
    • Use .InSequence(s) on multiple EXPECT_CALL statements to ensure they occur in the order they are written.
    • For strict ordering of all expectations in a scope, use the InSequence dummy object.

    Note: Modifying an ExpectationSet after it has been used in an .After() call does not change the existing dependency.

    // Using After
    using ::testing::Expectation;
    Expectation init_x = EXPECT_CALL(foo, InitX());
    EXPECT_CALL(foo, Bar()).After(init_x);
    
    // Using InSequence scope
    using ::testing::InSequence;
    {
      InSequence dummy;
      EXPECT_CALL(foo, Step1());
      EXPECT_CALL(foo, Step2());
    }