From 138d8d24a07f4b63bda6a8fcc6c3f1581424c6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=82=E4=B9=8B=E9=92=B3?= Date: Sun, 26 Jul 2026 16:41:45 +0800 Subject: [PATCH] fix(v4.4): repair incremental_update test API mismatch + invalidate_cache propagation - Rewrote test_incremental_update.cpp to match new API (FeatureHistory param) - Fixed invalidate_cache to propagate dirty flag to downstream nodes via mark_dirty - 15/15 tests pass on Linux build --- src/brep/incremental_update.cpp | 14 ++-- tests/brep/test_incremental_update.cpp | 108 +++++++++++++++++++++++-- 2 files changed, 109 insertions(+), 13 deletions(-) diff --git a/src/brep/incremental_update.cpp b/src/brep/incremental_update.cpp index c9ef0c5..45e5c9a 100644 --- a/src/brep/incremental_update.cpp +++ b/src/brep/incremental_update.cpp @@ -48,11 +48,15 @@ const NodeCache* IncrementalUpdateEngine::get_cache(int node_id) const { void IncrementalUpdateEngine::invalidate_cache(int node_id) { auto it = caches_.find(node_id); - if (it != caches_.end()) { - it->second = NodeCache{}; - it->second.dirty = true; - dirty_set_.insert(node_id); - } + if (it == caches_.end()) return; + + // Clear cached data but keep the entry + it->second.model.reset(); + it->second.mesh.reset(); + it->second.bounds.reset(); + + // Propagate dirty flag to this node and all downstream + mark_dirty(node_id); } void IncrementalUpdateEngine::clear_all() { diff --git a/tests/brep/test_incremental_update.cpp b/tests/brep/test_incremental_update.cpp index 4f4c784..3062128 100644 --- a/tests/brep/test_incremental_update.cpp +++ b/tests/brep/test_incremental_update.cpp @@ -1,10 +1,21 @@ #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); @@ -49,7 +60,8 @@ TEST(IncrementalUpdateTest, RebuildClearsDirty) { engine.register_node(1); engine.mark_dirty(1); EXPECT_EQ(engine.dirty_count(), 1); - FeatureHistory history; + + auto history = make_history(); engine.incremental_rebuild(history); EXPECT_EQ(engine.dirty_count(), 0); } @@ -58,22 +70,40 @@ TEST(IncrementalUpdateTest, RebuildWithCallback) { IncrementalUpdateEngine engine; engine.register_node(1); engine.mark_dirty(1); + int cb = 0; - FeatureHistory history; + 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; - FeatureHistory history; + auto history = make_history(); engine.incremental_rebuild(history, [&](int nid, const BrepModel&) { order.push_back(nid); }); ASSERT_GE(order.size(), 2u); - EXPECT_LT(std::find(order.begin(),order.end(),1), std::find(order.begin(),order.end(),2)); + // 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) { @@ -83,10 +113,15 @@ TEST(IncrementalUpdateTest, IncrementalVsFull) { if (i > 1) deps.push_back(i - 1); engine.register_node(i, deps); } - FeatureHistory history; + + 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); @@ -95,15 +130,72 @@ TEST(IncrementalUpdateTest, IncrementalVsFull) { EXPECT_GT(ic, 0); } -TEST(IncrementalUpdateTest, HitRate) { +TEST(IncrementalUpdateTest, InvalidateCachePropagates) { IncrementalUpdateEngine engine; - EXPECT_EQ(engine.hit_rate(), 0.0); + 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, ClearAll) { +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); +}