mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-11 09:25:05 -04:00
Use a deterministic clock for input expression tests
This commit is contained in:
@@ -9,11 +9,24 @@
|
||||
#include <unordered_map>
|
||||
|
||||
namespace InputExpr {
|
||||
#ifdef MKW_INPUT_EXPR_TEST_CLOCK
|
||||
// Only the standalone test target supplies this clock; runtime builds use the
|
||||
// steady clock directly, with no mutable override or extra runtime state.
|
||||
std::chrono::steady_clock::time_point TestClockNow();
|
||||
#endif
|
||||
namespace {
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using FSec = std::chrono::duration<double>;
|
||||
|
||||
static Clock::time_point Now() {
|
||||
#ifdef MKW_INPUT_EXPR_TEST_CLOCK
|
||||
return TestClockNow();
|
||||
#else
|
||||
return Clock::now();
|
||||
#endif
|
||||
}
|
||||
|
||||
enum class Kind {
|
||||
Literal, Input, Not, Add, Sub, Mul, Div, And, Or, Xor,
|
||||
Greater, Less, Equal,
|
||||
@@ -56,7 +69,7 @@ struct Node {
|
||||
mutable bool state = false;
|
||||
mutable unsigned taps = 0;
|
||||
mutable double value = 0.0;
|
||||
mutable Clock::time_point mark = Clock::now();
|
||||
mutable Clock::time_point mark = Now();
|
||||
mutable bool marked = false;
|
||||
};
|
||||
|
||||
@@ -400,7 +413,7 @@ double Eval(const Node& node, const InputSource& source) {
|
||||
return std::copysign(std::max(0.0, std::abs(v) - dz) / (1.0 - dz), v);
|
||||
}
|
||||
case Kind::FnTimer: {
|
||||
const auto now = Clock::now();
|
||||
const auto now = Now();
|
||||
if (!node.marked) {
|
||||
node.mark = now;
|
||||
node.marked = true;
|
||||
@@ -431,7 +444,7 @@ double Eval(const Node& node, const InputSource& source) {
|
||||
return node.state ? 1.0 : 0.0;
|
||||
}
|
||||
case Kind::FnHold: {
|
||||
const auto now = Clock::now();
|
||||
const auto now = Now();
|
||||
if (!node.marked) {
|
||||
node.mark = now;
|
||||
node.marked = true;
|
||||
@@ -448,7 +461,7 @@ double Eval(const Node& node, const InputSource& source) {
|
||||
return node.state ? 1.0 : 0.0;
|
||||
}
|
||||
case Kind::FnTap: {
|
||||
const auto now = Clock::now();
|
||||
const auto now = Now();
|
||||
if (!node.marked) {
|
||||
node.mark = now;
|
||||
node.marked = true;
|
||||
@@ -480,7 +493,7 @@ double Eval(const Node& node, const InputSource& source) {
|
||||
return desired == node.taps ? 1.0 : 0.0;
|
||||
}
|
||||
case Kind::FnPulse: {
|
||||
const auto now = Clock::now();
|
||||
const auto now = Now();
|
||||
const double input = Arg(node, 0, source);
|
||||
if (input < kConditionThreshold) {
|
||||
node.released = true;
|
||||
@@ -502,7 +515,7 @@ double Eval(const Node& node, const InputSource& source) {
|
||||
return node.state ? 1.0 : 0.0;
|
||||
}
|
||||
case Kind::FnSmooth: {
|
||||
const auto now = Clock::now();
|
||||
const auto now = Now();
|
||||
if (!node.marked) {
|
||||
node.mark = now;
|
||||
node.marked = true;
|
||||
|
||||
@@ -36,6 +36,7 @@ target_include_directories(mkw_nand_move_tests PRIVATE "${runtime_root}/src/hle/
|
||||
mkw_add_test(mkw_nand_settings_tests nand_settings_tests.cpp)
|
||||
mkw_add_test(mkw_sc_serial_tests sc_serial_tests.cpp)
|
||||
mkw_add_test(mkw_input_expr_tests test_expr.cpp "${runtime_root}/src/input_expr.cpp")
|
||||
target_compile_definitions(mkw_input_expr_tests PRIVATE MKW_INPUT_EXPR_TEST_CLOCK)
|
||||
|
||||
if(WIN32)
|
||||
mkw_add_test(mkw_windows_host_context_tests host_context_tests.cpp
|
||||
|
||||
@@ -8,7 +8,14 @@
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#ifndef MKW_INPUT_EXPR_TEST_CLOCK
|
||||
#error "Build this test through CMake so the deterministic clock is enabled"
|
||||
#endif
|
||||
|
||||
static std::chrono::steady_clock::time_point g_now{};
|
||||
namespace InputExpr {
|
||||
std::chrono::steady_clock::time_point TestClockNow() { return g_now; }
|
||||
}
|
||||
|
||||
static int g_failures = 0;
|
||||
static std::map<std::string, double> g_inputs;
|
||||
@@ -41,7 +48,7 @@ static bool Pressed(const InputExpr::Expression& e) {
|
||||
return e.Evaluate(Source()) > InputExpr::kConditionThreshold;
|
||||
}
|
||||
|
||||
static void Sleep(int ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); }
|
||||
static void AdvanceTime(int ms) { g_now += std::chrono::milliseconds(ms); }
|
||||
|
||||
int main() {
|
||||
std::printf("Dolphin expression engine\n");
|
||||
@@ -82,7 +89,7 @@ int main() {
|
||||
auto hold = Compile("hold(`H`, 0.05)");
|
||||
g_inputs["H"] = 1.0;
|
||||
Check(!Pressed(hold), "hold not satisfied immediately");
|
||||
Sleep(70);
|
||||
AdvanceTime(70);
|
||||
Check(Pressed(hold), "hold satisfied after the interval");
|
||||
g_inputs["H"] = 0.0;
|
||||
Check(!Pressed(hold), "hold clears on release");
|
||||
@@ -93,7 +100,7 @@ int main() {
|
||||
pulse.Evaluate(Source());
|
||||
g_inputs["P"] = 1.0;
|
||||
Check(Pressed(pulse), "pulse fires on rising edge");
|
||||
Sleep(80);
|
||||
AdvanceTime(80);
|
||||
Check(!Pressed(pulse), "pulse expires");
|
||||
|
||||
// The timing-window idiom seen in shared Dolphin configs.
|
||||
@@ -102,9 +109,9 @@ int main() {
|
||||
window.Evaluate(Source());
|
||||
g_inputs["W"] = 1.0;
|
||||
Check(!Pressed(window), "window closed before its start");
|
||||
Sleep(90);
|
||||
AdvanceTime(90);
|
||||
Check(Pressed(window), "window open between the two pulses");
|
||||
Sleep(90);
|
||||
AdvanceTime(90);
|
||||
Check(!Pressed(window), "window closed after its end");
|
||||
|
||||
// timer ramps 0..1 and wraps, so a threshold turns it into a square wave.
|
||||
@@ -114,7 +121,7 @@ int main() {
|
||||
int low = 0;
|
||||
for (int i = 0; i < 40; ++i) {
|
||||
(Pressed(timer) ? high : low)++;
|
||||
Sleep(5);
|
||||
AdvanceTime(5);
|
||||
}
|
||||
Check(high > 5 && low > 5, "timer alternates high and low");
|
||||
|
||||
@@ -130,7 +137,7 @@ int main() {
|
||||
high = low = 0;
|
||||
for (int i = 0; i < 60; ++i) {
|
||||
(Pressed(dolphinLine) ? high : low)++;
|
||||
Sleep(2);
|
||||
AdvanceTime(2);
|
||||
}
|
||||
Check(high > 5 && low > 5, "LB alternates via timer(0.01)");
|
||||
|
||||
@@ -192,7 +199,7 @@ int main() {
|
||||
auto sm = Compile("smooth(`A`, 0)");
|
||||
g_inputs["A"] = 1.0;
|
||||
sm.Evaluate(Source());
|
||||
Sleep(5);
|
||||
AdvanceTime(5);
|
||||
Check(std::isfinite(sm.Evaluate(Source())), "smooth with a zero rate stays finite");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user