feat: 集成测试补 T16 剖面盒导出(25/25 通过)
- 服务端新增 export-section-box 端点:mode=bbox(指定包围盒,测试可控)/ mode=activeView(UI 剖面盒) - 新增 SectionBoxExportAutomationTests:导出 NWD 文件存在/对象数/文件大小/隐藏节点断言 - 客户端 ExportSectionBoxAsync 方法
This commit is contained in:
parent
8e5d9e845f
commit
ac454196f1
@ -84,6 +84,7 @@
|
||||
<Compile Include="UnitTests\Integration\PathEditingAutomationTests.cs" />
|
||||
<Compile Include="UnitTests\Integration\RealObjectAnimationAutomationTests.cs" />
|
||||
<Compile Include="UnitTests\Integration\RouteGridDiagnosticsAutomationTests.cs" />
|
||||
<Compile Include="UnitTests\Integration\SectionBoxExportAutomationTests.cs" />
|
||||
<Compile Include="UnitTests\Integration\VirtualCollisionAutomationTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\HostCoordinateAdapterTests.cs" />
|
||||
<Compile Include="UnitTests\CoordinateSystem\CanonicalRailPoseBuilderTests.cs" />
|
||||
|
||||
@ -293,6 +293,23 @@ namespace NavisworksTransport.UnitTests.Integration
|
||||
return await PostJsonAsync(requestUri, 60).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 剖面盒导出(指定包围盒模式),返回导出文件信息。
|
||||
/// </summary>
|
||||
public async Task<JObject> ExportSectionBoxAsync(
|
||||
string filePath,
|
||||
double x1, double y1, double z1,
|
||||
double x2, double y2, double z2)
|
||||
{
|
||||
string requestUri = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"/api/test/export-section-box?mode=bbox&x1={0}&y1={1}&z1={2}&x2={3}&y2={4}&z2={5}&path={6}",
|
||||
x1, y1, z1, x2, y2, z2,
|
||||
Uri.EscapeDataString(filePath));
|
||||
|
||||
return await PostJsonAsync(requestUri, 120).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<JObject> GetJsonAsync(string requestUri, int timeoutSeconds = 30)
|
||||
{
|
||||
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)))
|
||||
|
||||
83
UnitTests/Integration/SectionBoxExportAutomationTests.cs
Normal file
83
UnitTests/Integration/SectionBoxExportAutomationTests.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace NavisworksTransport.UnitTests.Integration
|
||||
{
|
||||
/// <summary>
|
||||
/// 剖面盒导出集成测试(覆盖计划 T16)。
|
||||
///
|
||||
/// 验证 SectionBoxExporter 的指定包围盒导出链路:
|
||||
/// - 导出 NWD 文件成功(文件存在且非空)
|
||||
/// - 包围盒内对象数 > 0、文件大小 > 0
|
||||
/// - 隐藏节点计算完成(hiddenNodeCount >= 0)
|
||||
///
|
||||
/// 使用指定包围盒模式(bbox),不依赖 UI 剖面盒状态;包围盒覆盖模型 Hoisting/Rail 区域。
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
[TestCategory("NavisworksIntegration")]
|
||||
public class SectionBoxExportAutomationTests
|
||||
{
|
||||
// 模型单位:覆盖 Hoisting/Rail 区域的包围盒(已验证含 57 个对象)
|
||||
private const double MinX = -240, MinY = 10, MinZ = -20;
|
||||
private const double MaxX = -170, MaxY = 30, MaxZ = 25;
|
||||
|
||||
[TestMethod]
|
||||
[Timeout(240000)]
|
||||
public async Task SectionBoxBBoxExport_ProducesValidNwd()
|
||||
{
|
||||
using (var client = new NavisworksTestAutomationClient())
|
||||
{
|
||||
await client.EnsureServiceReadyAsync(TimeSpan.FromSeconds(90)).ConfigureAwait(false);
|
||||
|
||||
string tempDir = Path.Combine(Path.GetTempPath(), "NavisworksTransportIntegrationTests", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(tempDir);
|
||||
try
|
||||
{
|
||||
string exportPath = Path.Combine(tempDir, "sectionbox-export.nwd");
|
||||
|
||||
JObject response = await client.ExportSectionBoxAsync(
|
||||
exportPath,
|
||||
MinX, MinY, MinZ, MaxX, MaxY, MaxZ).ConfigureAwait(false);
|
||||
Assert.IsTrue(response.Value<bool>("ok"), "剖面盒导出失败: " + (string)response["error"]);
|
||||
|
||||
JObject data = (JObject)response["data"];
|
||||
|
||||
// 文件已生成且非空
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace((string)data["filePath"]), "导出文件路径为空");
|
||||
Assert.IsTrue(File.Exists(exportPath), "导出文件不存在: " + exportPath);
|
||||
Assert.IsTrue((long)data["fileSize"] > 0, "导出文件大小应大于 0");
|
||||
Assert.IsTrue(new FileInfo(exportPath).Length > 0, "导出文件实际为空");
|
||||
|
||||
// 包围盒内对象与隐藏节点
|
||||
Assert.IsTrue((int)data["objectCount"] > 0, "包围盒内应找到对象");
|
||||
Assert.IsTrue((int)data["hiddenNodeCount"] >= 0, "隐藏节点数不能为负");
|
||||
|
||||
Assert.AreEqual("bbox", (string)data["mode"], "导出模式应为 bbox");
|
||||
}
|
||||
finally
|
||||
{
|
||||
SafeDelete(tempDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SafeDelete(string directory)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Directory.Delete(directory, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,6 +54,7 @@
|
||||
- [x] **T13 动画播放控制**(2026-08-04,`AnimationPlaybackControlAutomationTests`)— prepare→start→pause 验证→resume→finished
|
||||
- [x] **T14 批量任务队列**(2026-08-04,`BatchQueueAutomationTests`)— 队列添加/计数递增(轻量版,不执行批量动画)
|
||||
- [x] **T15 路径分析**(2026-08-04,`PathAnalysisAutomationTests`)— 评分结构验证;顺带修复产品 bug(CollisionResults 表已废弃导致路径分析 no such table)
|
||||
- [x] **T16 剖面盒导出**(2026-08-04,`SectionBoxExportAutomationTests`)— 指定包围盒模式导出 NWD(对象数/文件大小/隐藏节点),不依赖 UI 剖面盒状态
|
||||
|
||||
## 4. 测试服务支撑能力
|
||||
|
||||
@ -74,6 +75,7 @@
|
||||
| animation-playback-control | ✅ 已实现 | 播放控制序列(T13) |
|
||||
| batch-queue-add | ✅ 已实现 | 批量队列添加(T14) |
|
||||
| analyze-path | ✅ 已实现 | 路径分析评分(T15) |
|
||||
| export-section-box | ✅ 已实现 | 剖面盒导出 NWD(bbox/activeView 模式,T16) |
|
||||
| 其余待扩展 | — | 批量执行、TimeLiner 集成等后续迭代补充 |
|
||||
|
||||
## 5. 完成记录
|
||||
@ -88,3 +90,4 @@
|
||||
| 2026-08-04 | `44214ba` | T05/T06 + 基础修复(Free 路径、报告断言、动态解析、串行锁、脚本、加速 15fps/5s) |
|
||||
| 2026-08-04 | 待提交 | T07-T10(障碍绕行、多策略、网格诊断、持久化)+ GetDetectionRecordById + detection-record 端点;P0+P1 完成,18/18 |
|
||||
| 2026-08-04 | 待提交 | T11-T15(路径编辑/导出/播放控制/批量队列/路径分析)+ 服务端 5 端点 + 产品 bug 修复(PathDatabase CollisionResults→CollisionReports、RemovePoint 链路);P0+P1+P2 全部完成,集成测试 24/24,完整集 74.6s |
|
||||
| 2026-08-04 | 待提交 | T16 剖面盒导出(SectionBoxExportAutomationTests + export-section-box 端点,bbox 模式),25/25 |
|
||||
|
||||
@ -298,6 +298,14 @@ namespace NavisworksTransport.Core.Services
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.Equals(method, "POST", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(request.Path, "/api/test/export-section-box", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
object payload = InvokeOnUiThread(() => ExportSectionBoxPayload(request.Query));
|
||||
await WriteJsonResponseAsync(writer, 200, BuildEnvelope(true, payload)).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.Equals(method, "POST", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(request.Path, "/api/test/export-debug-snapshot", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@ -697,6 +705,81 @@ namespace NavisworksTransport.Core.Services
|
||||
/// <summary>
|
||||
/// 路径分析(效率/安全评分),集成测试验证分析链路与评分结构。
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 剖面盒导出(T16):mode=bbox 用指定包围盒导出 NWD(集成测试可控,不依赖 UI 剖面盒状态);
|
||||
/// mode=activeView 用当前视图剖面盒(需 UI 已激活 Box 模式)。
|
||||
/// </summary>
|
||||
private static object ExportSectionBoxPayload(Dictionary<string, string> query)
|
||||
{
|
||||
Document document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||
if (document == null)
|
||||
{
|
||||
throw new InvalidOperationException("当前没有活动文档");
|
||||
}
|
||||
|
||||
string mode = GetOptionalQueryValue(query, "mode") ?? "bbox";
|
||||
string filePath = GetOptionalQueryValue(query, "path");
|
||||
if (string.IsNullOrWhiteSpace(filePath))
|
||||
{
|
||||
filePath = Path.Combine(Path.GetTempPath(), $"sectionbox-export-{Guid.NewGuid():N}.nwd");
|
||||
}
|
||||
|
||||
var exporter = new SectionBoxExporter();
|
||||
|
||||
if (string.Equals(mode, "bbox", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
double x1 = ParseRequiredDouble(query, "x1");
|
||||
double y1 = ParseRequiredDouble(query, "y1");
|
||||
double z1 = ParseRequiredDouble(query, "z1");
|
||||
double x2 = ParseRequiredDouble(query, "x2");
|
||||
double y2 = ParseRequiredDouble(query, "y2");
|
||||
double z2 = ParseRequiredDouble(query, "z2");
|
||||
|
||||
var bounds = new BoundingBox3D(
|
||||
new Point3D(Math.Min(x1, x2), Math.Min(y1, y2), Math.Min(z1, z2)),
|
||||
new Point3D(Math.Max(x1, x2), Math.Max(y1, y2), Math.Max(z1, z2)));
|
||||
|
||||
var traversal = exporter.GetObjectsAndHiddenItems(document, bounds);
|
||||
if (traversal.ObjectsInBox.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("指定包围盒内没有找到对象");
|
||||
}
|
||||
|
||||
string exportedPath = exporter.ExportToNwd(document, traversal, filePath);
|
||||
if (string.IsNullOrEmpty(exportedPath) || !File.Exists(exportedPath))
|
||||
{
|
||||
throw new InvalidOperationException($"NWD 导出失败: {filePath}");
|
||||
}
|
||||
|
||||
LogManager.Info($"[测试HTTP] 剖面盒导出完成(bbox): 对象={traversal.ObjectsInBox.Count}, 隐藏={traversal.ItemsToHide.Count}, 文件={exportedPath}");
|
||||
|
||||
return new
|
||||
{
|
||||
mode = "bbox",
|
||||
filePath = exportedPath,
|
||||
objectCount = traversal.ObjectsInBox.Count,
|
||||
hiddenNodeCount = traversal.ItemsToHide.Count,
|
||||
fileSize = new FileInfo(exportedPath).Length
|
||||
};
|
||||
}
|
||||
|
||||
// activeView 模式:依赖当前视图剖面盒
|
||||
var result = exporter.ExportSectionBoxFromActiveView(document, filePath);
|
||||
if (!result.Success)
|
||||
{
|
||||
throw new InvalidOperationException(result.ErrorMessage ?? "剖面盒导出失败");
|
||||
}
|
||||
|
||||
return new
|
||||
{
|
||||
mode = "activeView",
|
||||
filePath = result.FilePath,
|
||||
objectCount = result.ObjectCount,
|
||||
hiddenNodeCount = result.HiddenNodeCount,
|
||||
fileSize = result.FileSize
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 路径编辑集成测试端点(T11):action=remove-point/update-point/orthogonalize/remove-loops。
|
||||
/// 直接操作临时测试路径,验证 PathPlanningManager 编辑链路。
|
||||
|
||||
Loading…
Reference in New Issue
Block a user