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
This commit is contained in:
茂之钳
2026-07-26 16:41:45 +08:00
parent 1db1a83c5d
commit 138d8d24a0
2 changed files with 109 additions and 13 deletions
+9 -5
View File
@@ -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() {
+100 -8
View File
@@ -1,10 +1,21 @@
#include <gtest/gtest.h>
#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<int> 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);
}