#include "test_harness.h" #include using json = nlohmann::json; static const char* testJsonPath() { // TEST_DATA_DIR is defined via CMake target_compile_definitions return TEST_DATA_DIR "/test_models.json"; } TEST(registry_singleton_is_same) { CHECK_EQ(&ModelRegistry::instance(), &ModelRegistry::instance()); } TEST(registry_load_models_and_get_builtin) { auto& reg = ModelRegistry::instance(); reg.loadModels(testJsonPath()); IModel* m = reg.getOrCreate("const_100", 0.0f, "builtin_1"); CHECK(m != nullptr); CHECK_FLOAT_EQ(m->evaluate(0), 100.0f, 0.001f); CHECK_FLOAT_EQ(m->evaluate(999), 100.0f, 0.001f); } TEST(registry_linear_model_via_template) { auto& reg = ModelRegistry::instance(); IModel* m = reg.getOrCreate("linear_k1", 5.0f, "linear_1"); CHECK(m != nullptr); CHECK_FLOAT_EQ(m->evaluate(0), 5.0f, 0.001f); CHECK_FLOAT_EQ(m->evaluate(3), 8.0f, 0.001f); } TEST(registry_same_key_returns_same_instance) { auto& reg = ModelRegistry::instance(); IModel* a = reg.getOrCreate("const_100", 10.0f, "same_key"); IModel* b = reg.getOrCreate("linear_k1", 20.0f, "same_key"); // same instanceKey → returns cached instance, not a new one CHECK_EQ(a, b); } TEST(registry_different_keys_different_instances) { auto& reg = ModelRegistry::instance(); IModel* a = reg.getOrCreate("const_100", 0.0f, "key_a"); IModel* b = reg.getOrCreate("const_100", 0.0f, "key_b"); CHECK(a != b); } TEST(registry_empty_spec_falls_back_to_normal_tiny) { auto& reg = ModelRegistry::instance(); IModel* m = reg.getOrCreate("", 0.0f, "empty_spec_test"); CHECK(m != nullptr); // normal_tiny → NormalModel with sigma=0.01, returns near-default float v = m->evaluate(0); (void)v; } TEST(registry_find_by_model_name) { auto& reg = ModelRegistry::instance(); // "const_100" was created via getOrCreate above; should be tracked auto found = reg.findByModelName("const_100"); CHECK(!found.empty()); } TEST(registry_find_nonexistent) { auto& reg = ModelRegistry::instance(); auto found = reg.findByModelName("no_such_model_xyz"); CHECK(found.empty()); } TEST(registry_composite_syntax) { auto& reg = ModelRegistry::instance(); // const_100 + linear_k1: evaluate(0) = 100 + 0 = 100, evaluate(3) = 100 + 8 = 108 IModel* m = reg.getOrCreate("const_100+linear_k1", 0.0f, "composite_1"); CHECK(m != nullptr); CHECK_FLOAT_EQ(m->evaluate(0), 100.0f, 0.001f); CHECK_FLOAT_EQ(m->evaluate(3), 108.0f, 0.001f); } TEST(registry_not_syntax) { auto& reg = ModelRegistry::instance(); // !toggle_1s: negates the bool toggle IModel* m = reg.getOrCreate("!toggle_1s", 0.0f, "not_1"); CHECK(m != nullptr); // toggle_1s period_ms=1000 → 50 ticks, first 25 true. // !toggle_1s at t=0 should be false CHECK_EQ(m->evaluateBool(0), false); CHECK_EQ(m->evaluateBool(26), true); // toggle is false at 26, so ! is true }