MQL5-SyntheticTestHarness/Documentation/decisions/ADR-003-behavior-injection-via-function-pointers.md

44 lines
2.9 KiB
Markdown

# ADR-003: Behavior Injection via Function Pointers for Dynamic Mocking
## Status: Accepted
## Context
In high-reliability institutional algorithmic trading engines, execution modules must be validated against unexpected terminal and broker behaviors (Chaos Engineering):
- Successive requotes during low liquidity
- Order rejections and price slippages
- Gateway/network disconnects (`TRADE_RETCODE_CONNECTION`)
- Custom Depth of Market (DOM) snapshots
In languages like C++ or C#, mocking frameworks (e.g., GoogleMock, Moq) use dynamic proxies, closures/lambdas, or template metaprogramming to configure mock responses on the fly.
MQL5 has strict language constraints:
1. No native lambda functions or true closure captures.
2. No reflection or runtime type introspection.
3. No compile-time mock generation libraries.
Previous iterations of the test harness relied solely on static tick arrays (`MockTick.injected_error`), which required constructing rigid multi-tick datasets to test dynamic scenarios like retry loops.
## Decision
Use **C-style function pointers (`typedef`)** to implement **Behavior Injection** directly into `CSyntheticEngine`:
1. Define delegate signatures in `BehaviorDelegates.mqh` mirroring the `IMarketEnvironment` interface methods.
2. Provide setter methods in `CSyntheticEngine` (e.g., `MockOrderSend()`, `MockMarketBookGet()`) to inject delegate function pointers at runtime.
3. Use a **Dual-Mode Priority Resolution**:
- **Behavior Injection Hook (Highest Priority)**: If a function pointer is non-NULL, execute it immediately.
- **Tick-Driven Simulation (Fallback)**: If no delegate is assigned, fall back to sequential `MockTick` replay.
- **Default Safe State**: If no ticks are loaded and no behavior is set, return safe defaults.
4. For stateful behaviors across successive calls (such as counting requote retries), use **local static variables** inside standalone behavior functions (`BehaviorLibrary.mqh`).
## Consequences
### Positive
- **Dynamic Chaos Engineering**: Behaviors can be hot-swapped mid-flight in unit tests without recreating tick arrays or restarting the test environment.
- **Zero Overhead in Production**: `CLiveEnvironment` continues to delegate directly to global MT5 functions via the scope resolution operator (`::`), unaffected by function pointer indirection.
- **Clean Architecture & Decoupling**: Separation of behavior definitions (`BehaviorDelegates.mqh`), behavior implementations (`BehaviorLibrary.mqh`), and the mock engine (`CSyntheticEngine.mqh`).
- **Idiomatic MQL5**: Full compatibility with strict MQL5 compiler without requiring external tooling.
### Negative
- **Manual State Reset**: Stateful behavior functions relying on internal `static` variables require careful sequencing or automatic reset after completion cycles.
- **Function Signature Constraints**: Injected functions must strictly adhere to the `typedef` delegate signature.