NavisworksTransport/UnitTests/Core/AliasTreeTests.cs
tian 4feb12f6b9 refactor: AliasNodeIdentity dual-field + reuse ModelItemAnalysisHelper
- 新增 IndexPath(COM 树索引路径),复用 ModelItemAnalysisHelper.GetModelItemIndexPaths
- 保留 DisplayPath(父链 DisplayName 拼接)作为降级匹配
- 序列化格式:"IndexPath||DisplayPath",向后兼容纯 DisplayPath
- FindModelItemByNodeKey: IndexPath 优先定位,DisplayPath 降级
- BuildNode: 新增旧格式兼容查找
- 单元测试全面更新覆盖新/旧格式、双字段解析、前缀匹配
2026-05-31 12:52:26 +08:00

362 lines
12 KiB
C#

using Microsoft.VisualStudio.TestTools.UnitTesting;
using NavisworksTransport.Core.AliasTree;
using System;
using System.Data.SQLite;
using System.IO;
namespace NavisworksTransport.UnitTests.Core
{
[TestClass]
public class AliasNodeIdentityTests
{
// ── 构造 ──
[TestMethod]
public void Constructor_ShouldSetBothFields()
{
var id = new AliasNodeIdentity("0,2,5", "building/floor/wall#1");
Assert.AreEqual("0,2,5", id.IndexPath);
Assert.AreEqual("building/floor/wall#1", id.DisplayPath);
}
[TestMethod]
public void Constructor_WithEmptyIndexPath_IsValid()
{
var id = new AliasNodeIdentity("", "building/floor");
Assert.AreEqual("", id.IndexPath);
Assert.AreEqual("building/floor", id.DisplayPath);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void Constructor_NullDisplayPath_ShouldThrow()
{
new AliasNodeIdentity("0,0", null);
}
// ── 序列化 ──
[TestMethod]
public void ToKey_WithBothFields_ShouldFormatCorrectly()
{
var id = new AliasNodeIdentity("0,2,5", "模型/楼层/墙#0");
Assert.AreEqual("0,2,5||模型/楼层/墙#0", id.ToKey());
}
[TestMethod]
public void ToKey_WithEmptyIndexPath_ShouldReturnDisplayPathOnly()
{
var id = new AliasNodeIdentity("", "模型/楼层/墙#0");
Assert.AreEqual("模型/楼层/墙#0", id.ToKey());
}
[TestMethod]
public void FromKey_NewFormat_ShouldParseCorrectly()
{
var id = AliasNodeIdentity.FromKey("0,2,5||模型/楼层/墙#0");
Assert.AreEqual("0,2,5", id.IndexPath);
Assert.AreEqual("模型/楼层/墙#0", id.DisplayPath);
}
[TestMethod]
public void FromKey_OldFormat_ShouldRetroCompatible()
{
var id = AliasNodeIdentity.FromKey("building/floor/wall#2");
Assert.AreEqual("", id.IndexPath);
Assert.AreEqual("building/floor/wall#2", id.DisplayPath);
}
[TestMethod]
public void FromKey_OldFormatNoIndex_ShouldParse()
{
var id = AliasNodeIdentity.FromKey("building/floor");
Assert.AreEqual("", id.IndexPath);
Assert.AreEqual("building/floor", id.DisplayPath);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void FromKey_EmptyString_ShouldThrow()
{
AliasNodeIdentity.FromKey("");
}
[TestMethod]
[ExpectedException(typeof(FormatException))]
public void FromKey_EmptyDisplayAfterSeparator_ShouldThrow()
{
AliasNodeIdentity.FromKey("0,0||");
}
// ── TryParse ──
[TestMethod]
public void TryParseKey_NewFormat_ShouldSucceed()
{
Assert.IsTrue(AliasNodeIdentity.TryParseKey("0,0||path#0", out var id));
Assert.AreEqual("0,0", id.IndexPath);
Assert.AreEqual("path#0", id.DisplayPath);
}
[TestMethod]
public void TryParseKey_OldFormat_ShouldSucceed()
{
Assert.IsTrue(AliasNodeIdentity.TryParseKey("path#0", out var id));
Assert.AreEqual("", id.IndexPath);
}
[TestMethod]
public void TryParseKey_Empty_ShouldReturnFalse()
{
Assert.IsFalse(AliasNodeIdentity.TryParseKey("", out _));
}
// ── 查询辅助 ──
[TestMethod]
public void GetHierarchicalPath_WithIndex_ShouldStripSuffix()
{
var id = new AliasNodeIdentity("0,0", "a/b/c#2");
Assert.AreEqual("a/b/c", id.GetHierarchicalPath());
}
[TestMethod]
public void GetHierarchicalPath_WithoutIndex_ShouldReturnFull()
{
var id = new AliasNodeIdentity("0,0", "a/b/c");
Assert.AreEqual("a/b/c", id.GetHierarchicalPath());
}
[TestMethod]
public void GetSiblingIndex_WithIndex_ShouldReturn()
{
var id = new AliasNodeIdentity("0,0", "a/b#3");
Assert.AreEqual(3, id.GetSiblingIndex());
}
[TestMethod]
public void GetSiblingIndex_WithoutIndex_ShouldReturnZero()
{
var id = new AliasNodeIdentity("0,0", "a/b");
Assert.AreEqual(0, id.GetSiblingIndex());
}
[TestMethod]
public void HasPathPrefix_Matching_ShouldReturnTrue()
{
var id = new AliasNodeIdentity("0,0", "building/floor/wall#0");
Assert.IsTrue(id.HasPathPrefix("building/floor"));
Assert.IsTrue(id.HasPathPrefix("building"));
Assert.IsTrue(id.HasPathPrefix("building/floor/wall"));
}
[TestMethod]
public void HasPathPrefix_NonMatching_ShouldReturnFalse()
{
var id = new AliasNodeIdentity("0,0", "building/floor/wall#0");
Assert.IsFalse(id.HasPathPrefix("building/roof"));
Assert.IsFalse(id.HasPathPrefix("Build"));
}
// ── 值语义 ──
[TestMethod]
public void Equals_Identical_ShouldMatch()
{
var a = new AliasNodeIdentity("0,1", "path#0");
var b = new AliasNodeIdentity("0,1", "path#0");
Assert.AreEqual(a, b);
Assert.IsTrue(a == b);
Assert.AreEqual(a.GetHashCode(), b.GetHashCode());
}
[TestMethod]
public void Equals_DifferentIndexPath_ShouldNotMatch()
{
var a = new AliasNodeIdentity("0,1", "path");
var b = new AliasNodeIdentity("0,2", "path");
Assert.AreNotEqual(a, b);
}
[TestMethod]
public void Equals_DifferentDisplayPath_ShouldNotMatch()
{
var a = new AliasNodeIdentity("0,1", "path/a");
var b = new AliasNodeIdentity("0,1", "path/b");
Assert.AreNotEqual(a, b);
}
[TestMethod]
public void ToString_ShouldMatchToKey()
{
var id = new AliasNodeIdentity("0,1", "path#0");
Assert.AreEqual(id.ToKey(), id.ToString());
}
}
[TestClass]
public class AliasDataStoreTests
{
private string _dbPath;
private SQLiteConnection _connection;
[TestInitialize]
public void Setup()
{
_dbPath = Path.Combine(Path.GetTempPath(), $"AliasDataStoreTest_{Guid.NewGuid():N}.db");
_connection = new SQLiteConnection($"Data Source={_dbPath};Version=3;");
_connection.Open();
}
[TestCleanup]
public void Cleanup()
{
_connection?.Close();
_connection?.Dispose();
try { File.Delete(_dbPath); } catch { }
}
private AliasDataStore CreateStore()
{
var store = new AliasDataStore(_connection);
store.EnsureTableExists();
return store;
}
private AliasNodeIdentity Id(string displayPath, string indexPath = "")
=> new AliasNodeIdentity(indexPath, displayPath);
// ── CRUD ──
[TestMethod]
public void SetAndGetAlias_ShouldReturnSavedValue()
{
var store = CreateStore();
store.SetAlias(Id("building/floor", "0,1"), "1F-办公层");
Assert.AreEqual("1F-办公层", store.GetAlias(Id("building/floor", "0,1")));
}
[TestMethod]
public void GetAlias_Nonexistent_ShouldReturnNull()
{
var store = CreateStore();
Assert.IsNull(store.GetAlias(Id("nonexistent")));
}
[TestMethod]
public void SetAlias_Overwrite_ShouldUpdateValue()
{
var store = CreateStore();
store.SetAlias(Id("wall", "0,0"), "旧名称");
store.SetAlias(Id("wall", "0,0"), "新名称");
Assert.AreEqual("新名称", store.GetAlias(Id("wall", "0,0")));
}
[TestMethod]
public void SetAlias_EmptyString_ShouldDelete()
{
var store = CreateStore();
store.SetAlias(Id("x", "0,0"), "原本");
Assert.AreEqual(1, store.Count);
store.SetAlias(Id("x", "0,0"), " ");
Assert.IsNull(store.GetAlias(Id("x", "0,0")));
Assert.AreEqual(0, store.Count);
}
[TestMethod]
public void DeleteAlias_ShouldRemoveEntry()
{
var store = CreateStore();
store.SetAlias(Id("x"), "别名");
Assert.AreEqual(1, store.Count);
store.DeleteAlias(Id("x"));
Assert.AreEqual(0, store.Count);
}
// ── 批量 ──
[TestMethod]
public void DeleteByPathPrefix_ShouldRemoveAllUnderPath()
{
var store = CreateStore();
store.SetAlias(Id("building/floor/1"));
store.SetAlias(Id("building/floor/2"));
store.SetAlias(Id("building/roof/3"));
store.DeleteAliasesByPathPrefix("building/floor");
Assert.IsNull(store.GetAlias(Id("building/floor/1")));
Assert.IsNull(store.GetAlias(Id("building/floor/2")));
Assert.IsNotNull(store.GetAlias(Id("building/roof/3")));
}
[TestMethod]
public void LoadAll_ShouldReturnAll()
{
var store = CreateStore();
store.SetAlias(Id("a", "0,0"), "别名A");
store.SetAlias(Id("b", "1,0"), "别名B");
var all = store.LoadAll();
Assert.AreEqual(2, all.Count);
Assert.AreEqual("别名A", all[Id("a", "0,0").ToKey()]);
Assert.AreEqual("别名B", all[Id("b", "1,0").ToKey()]);
}
[TestMethod]
public void Count_ShouldReflectInserts()
{
var store = CreateStore();
Assert.AreEqual(0, store.Count);
store.SetAlias(Id("a"), "alias1");
store.SetAlias(Id("b"), "alias2");
Assert.AreEqual(2, store.Count);
}
// ── ImportBatch ──
[TestMethod]
public void ImportBatch_ShouldMergeAndOverwrite()
{
var store = CreateStore();
store.SetAlias(Id("existing", "0,0"), "保留值");
var entries = new System.Collections.Generic.Dictionary<string, string>
{
{ Id("existing", "0,0").ToKey(), "覆盖值" },
{ Id("new", "1,0").ToKey(), "新值" }
};
store.ImportBatch(entries);
Assert.AreEqual("覆盖值", store.GetAlias(Id("existing", "0,0")));
Assert.AreEqual("新值", store.GetAlias(Id("new", "1,0")));
Assert.AreEqual(2, store.Count);
}
[TestMethod]
public void ClearAll_ShouldRemoveEverything()
{
var store = CreateStore();
store.SetAlias(Id("a"), "别名1");
store.SetAlias(Id("b"), "别名2");
store.ClearAll();
Assert.AreEqual(0, store.Count);
}
// ── 新格式 → 旧格式 兼容 ──
[TestMethod]
public void OldFormatKey_StillWorks()
{
var store = CreateStore();
string oldKey = "building/floor/wall#0";
store.SetAlias(AliasNodeIdentity.FromKey(oldKey), "别名");
Assert.AreEqual("别名", store.GetAlias(AliasNodeIdentity.FromKey(oldKey)));
// 同一个节点用新格式读取
var newId = new AliasNodeIdentity("", "building/floor/wall#0");
Assert.AreEqual("别名", store.GetAlias(newId));
}
}
}