#include #include "vde/brep/incremental_update.h" #include "vde/brep/modeling.h" #include "vde/brep/feature_tree.h" using namespace vde::brep; using namespace vde::core; // Helper: create a FeatureHistory with an initial BrepModel static FeatureHistory make_history() { FeatureHistory history; FeatureParams params; params.name = "box"; params.values = {1.0, 1.0, 1.0}; history.apply(make_box(1, 1, 1), FeatureType::PrimitiveBox, params); return history; } TEST(IncrementalUpdateTest, RegisterNode) { IncrementalUpdateEngine engine; engine.register_node(1); EXPECT_EQ(engine.total_nodes(), 1); } TEST(IncrementalUpdateTest, RegisterWithDeps) { IncrementalUpdateEngine engine; engine.register_node(1); engine.register_node(2, {1}); engine.register_node(3, {1, 2}); EXPECT_EQ(engine.total_nodes(), 3); } TEST(IncrementalUpdateTest, MarkDirtyPropagates) { IncrementalUpdateEngine engine; engine.register_node(1); engine.register_node(2, {1}); engine.register_node(3, {2}); engine.mark_dirty(1); EXPECT_EQ(engine.dirty_count(), 3); } TEST(IncrementalUpdateTest, MarkDirtyNoUpstream) { IncrementalUpdateEngine engine; engine.register_node(1); engine.register_node(2, {1}); engine.mark_dirty(2); EXPECT_EQ(engine.dirty_count(), 1); } TEST(IncrementalUpdateTest, CacheMissOnDirty) { IncrementalUpdateEngine engine; engine.register_node(1); engine.mark_dirty(1); EXPECT_TRUE(engine.get_cache(1) == nullptr); EXPECT_EQ(engine.cache_misses(), 1); } TEST(IncrementalUpdateTest, RebuildClearsDirty) { IncrementalUpdateEngine engine; engine.register_node(1); engine.mark_dirty(1); EXPECT_EQ(engine.dirty_count(), 1); auto history = make_history(); engine.incremental_rebuild(history); EXPECT_EQ(engine.dirty_count(), 0); } TEST(IncrementalUpdateTest, RebuildWithCallback) { IncrementalUpdateEngine engine; engine.register_node(1); engine.mark_dirty(1); int cb = 0; auto history = make_history(); engine.incremental_rebuild(history, [&](int nid, const BrepModel&) { cb++; }); EXPECT_EQ(cb, 1); } TEST(IncrementalUpdateTest, RebuildClearsCacheDirty) { IncrementalUpdateEngine engine; engine.register_node(1); engine.mark_dirty(1); auto history = make_history(); engine.incremental_rebuild(history); const auto* cache = engine.get_cache(1); ASSERT_TRUE(cache != nullptr); EXPECT_FALSE(cache->dirty); EXPECT_EQ(engine.cache_hits(), 1); } TEST(IncrementalUpdateTest, ChainDependencyOrder) { IncrementalUpdateEngine engine; engine.register_node(1); engine.register_node(2, {1}); engine.mark_dirty(1); std::vector order; auto history = make_history(); engine.incremental_rebuild(history, [&](int nid, const BrepModel&) { order.push_back(nid); }); ASSERT_GE(order.size(), 2u); // Node 1 must be rebuilt before node 2 (upstream before downstream) EXPECT_LT(std::find(order.begin(), order.end(), 1) - order.begin(), std::find(order.begin(), order.end(), 2) - order.begin()); } TEST(IncrementalUpdateTest, IncrementalVsFull) { IncrementalUpdateEngine engine; for (int i = 1; i <= 10; ++i) { std::vector deps; if (i > 1) deps.push_back(i - 1); engine.register_node(i, deps); } auto history = make_history(); // Full rebuild (clear_all then rebuild) engine.clear_all(); int fc = engine.incremental_rebuild(history); EXPECT_EQ(fc, 10); // Mark only node 5 dirty — should rebuild fewer nodes engine.clear_all(); engine.incremental_rebuild(history); engine.mark_dirty(5); int ic = engine.incremental_rebuild(history); EXPECT_LT(ic, fc); EXPECT_GT(ic, 0); } TEST(IncrementalUpdateTest, InvalidateCachePropagates) { IncrementalUpdateEngine engine; engine.register_node(1); engine.register_node(2, {1}); engine.register_node(3, {2}); auto history = make_history(); engine.incremental_rebuild(history); // build all EXPECT_EQ(engine.dirty_count(), 0); engine.invalidate_cache(1); // Node 1 and its dependents (2, 3) should all be dirty EXPECT_EQ(engine.dirty_count(), 3); } TEST(IncrementalUpdateTest, HitRate) { IncrementalUpdateEngine engine; // No cache access yet → 0 hit rate EXPECT_DOUBLE_EQ(engine.hit_rate(), 0.0); engine.register_node(1); // get_cache on dirty node → miss engine.mark_dirty(1); engine.get_cache(1); EXPECT_DOUBLE_EQ(engine.hit_rate(), 0.0); // After rebuild, get_cache should hit auto history = make_history(); engine.incremental_rebuild(history); engine.get_cache(1); EXPECT_GT(engine.hit_rate(), 0.0); } TEST(IncrementalUpdateTest, ClearAllMarksAllDirty) { IncrementalUpdateEngine engine; engine.register_node(1); engine.register_node(2); auto history = make_history(); engine.incremental_rebuild(history); EXPECT_EQ(engine.dirty_count(), 0); engine.clear_all(); EXPECT_EQ(engine.dirty_count(), 2); } TEST(IncrementalUpdateTest, RebuildWithoutEvaluator) { IncrementalUpdateEngine engine; engine.register_node(1); // no evaluator registered (just topology) engine.mark_dirty(1); int cb = 0; auto history = make_history(); engine.incremental_rebuild(history, [&](int, const BrepModel&) { cb++; }); EXPECT_EQ(cb, 1); // callback still fires EXPECT_EQ(engine.dirty_count(), 0); } TEST(IncrementalUpdateTest, FullRebuild) { IncrementalUpdateEngine engine; engine.register_node(1); engine.register_node(2, {1}); engine.register_node(3, {2}); auto history = make_history(); int count = engine.full_rebuild(history); EXPECT_EQ(count, 3); EXPECT_EQ(engine.dirty_count(), 0); }