- T11 路径编辑(PathEditingAutomationTests):删除点/更新位置/正交化/去环 - T12 路径导出(ExportImportRoundTripAutomationTests):XML/JSON 导出→导入回环 - T13 播放控制(AnimationPlaybackControlAutomationTests):pause 验证帧停止/resume 前进 - T14 批量队列(BatchQueueAutomationTests):队列添加/计数递增 - T15 路径分析(PathAnalysisAutomationTests):评分结构验证 - 服务端新增 5 端点:export-route-file/edit-route/animation-playback-control/batch-queue-add/analyze-path - 产品 bug 修复:PathDatabase.CollisionResults 表已废弃→GetCollisionCount 改查 CollisionReports、 SaveCollisions 异常保护;路径编辑真删点走 PathRoute.RemovePoint(RemovePathPoint 仅 3D 标记) 验证:集成测试 24/24 通过,完整集 74.6s(15fps/5s 动画)
133 lines
6.6 KiB
C#
133 lines
6.6 KiB
C#
using System;
|
||
using System.IO;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||
using Newtonsoft.Json.Linq;
|
||
|
||
namespace NavisworksTransport.UnitTests.Integration
|
||
{
|
||
/// <summary>
|
||
/// 路径导出→导入回环集成测试(覆盖计划 T12)。
|
||
///
|
||
/// 验证路径导出(XML/JSON)与导入的完整回环:
|
||
/// - 导出路径文件成功(文件存在、格式正确)
|
||
/// - 重新导入后属性一致:名称、路径类型、点数、总长度(米)
|
||
/// </summary>
|
||
[TestClass]
|
||
[TestCategory("NavisworksIntegration")]
|
||
public class ExportImportRoundTripAutomationTests
|
||
{
|
||
private const string RouteName = "导出回环_测试";
|
||
|
||
[TestMethod]
|
||
[Timeout(240000)]
|
||
public async Task ExportImportRoundTrip_Xml_PropertiesConsistent()
|
||
{
|
||
await RunRoundTripAsync("xml").ConfigureAwait(false);
|
||
}
|
||
|
||
[TestMethod]
|
||
[Timeout(240000)]
|
||
public async Task ExportImportRoundTrip_Json_PropertiesConsistent()
|
||
{
|
||
await RunRoundTripAsync("json").ConfigureAwait(false);
|
||
}
|
||
|
||
private static async Task RunRoundTripAsync(string format)
|
||
{
|
||
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
|
||
{
|
||
// 1. 准备一条临时路径(2 点 Ground,坐标取自测试基准区域)
|
||
string sourceFile = Path.Combine(tempDir, "source-route.json");
|
||
File.WriteAllText(sourceFile, BuildSourceRouteJson(), new UTF8Encoding(false));
|
||
JObject importSource = await client.ImportRouteFileAsync(sourceFile, overwrite: true).ConfigureAwait(false);
|
||
Assert.IsTrue(importSource.Value<bool>("ok"), "准备源路径失败: " + (string)importSource["error"]);
|
||
double metersToModelUnits = importSource["data"].Value<double>("metersToModelUnits");
|
||
|
||
// 2. 导出
|
||
string exportPath = Path.Combine(tempDir, "exported-route." + format);
|
||
JObject exportResponse = await client.ExportRouteFileAsync(RouteName, format, exportPath).ConfigureAwait(false);
|
||
Assert.IsTrue(exportResponse.Value<bool>("ok"), "导出失败: " + (string)exportResponse["error"]);
|
||
Assert.IsTrue(File.Exists(exportPath), "导出文件不存在: " + exportPath);
|
||
Assert.IsTrue(new FileInfo(exportPath).Length > 0, "导出文件为空");
|
||
|
||
int sourcePointCount = (int)exportResponse["data"]["pointCount"];
|
||
double sourceLengthMeters = (double)exportResponse["data"]["totalLengthInMeters"];
|
||
|
||
// 3. 重新导入(overwrite 同名路径)
|
||
JObject reimportResponse = await client.ImportRouteFileAsync(exportPath, overwrite: true).ConfigureAwait(false);
|
||
Assert.IsTrue(reimportResponse.Value<bool>("ok"), "重新导入失败: " + (string)reimportResponse["error"]);
|
||
|
||
JObject reimported = (JObject)((JArray)reimportResponse["data"]["routes"])[0];
|
||
|
||
// 4. 回环一致性断言
|
||
Assert.AreEqual(RouteName, (string)reimported["name"], "回环后路径名不一致");
|
||
Assert.AreEqual("Ground", (string)reimported["pathType"], "回环后路径类型不一致");
|
||
Assert.AreEqual(sourcePointCount, (int)reimported["pointCount"], "回环后点数不一致");
|
||
Assert.AreEqual(
|
||
sourceLengthMeters,
|
||
(double)reimported["totalLengthInMeters"],
|
||
1e-2,
|
||
"回环后总长度不一致(源 " + sourceLengthMeters.ToString("F3") + "m)");
|
||
|
||
// 长度应等于模型单位直线距离换算(验证单位链完整;YUp 水平面为 XZ)
|
||
double expectedStraightMeters = Math.Sqrt(
|
||
(EndX - StartX) * (EndX - StartX) +
|
||
(EndZ - StartZ) * (EndZ - StartZ)) / metersToModelUnits;
|
||
Assert.AreEqual(
|
||
expectedStraightMeters,
|
||
(double)reimported["totalLengthInMeters"],
|
||
1e-2,
|
||
"回环后长度与起终点直线距离不一致(单位换算)");
|
||
}
|
||
finally
|
||
{
|
||
SafeDelete(tempDir);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 源路径起终点(模型单位,测试基准区域附近)
|
||
private const double StartX = -204.608;
|
||
private const double StartY = 14.833;
|
||
private const double StartZ = 19.250;
|
||
private const double EndX = -208.608;
|
||
private const double EndY = 14.833;
|
||
private const double EndZ = 18.250;
|
||
|
||
private static string BuildSourceRouteJson()
|
||
{
|
||
return string.Format(
|
||
System.Globalization.CultureInfo.InvariantCulture,
|
||
@"{{""PathPlanningData"":{{""version"":""1.0"",""generator"":""integration-test"",""timestamp"":""2026-08-04T21:30:00"",""ProjectInfo"":{{""name"":""test"",""description"":""test"",""units"":""meters"",""coordinateSystem"":""Global""}},""Routes"":[{{""id"":""roundtrip-{0}"",""name"":""{1}"",""description"":"""",""pathType"":""Ground"",""totalLength"":10.0,""objectLimits"":{{""maxLength"":0,""maxWidth"":0,""maxHeight"":0,""safetyMargin"":0}},""gridSize"":1.0,""liftHeight"":0.0,""created"":""2026-08-04T21:30:00"",""points"":[{{""id"":""rt-p1"",""name"":""start"",""type"":""StartPoint"",""index"":0,""x"":{2},""y"":{3},""z"":{4},""created"":""2026-08-04T21:30:00""}},{{""id"":""rt-p2"",""name"":""end"",""type"":""EndPoint"",""index"":1,""x"":{5},""y"":{6},""z"":{7},""created"":""2026-08-04T21:30:00""}}]}}]}}}}",
|
||
Guid.NewGuid().ToString("N"),
|
||
RouteName,
|
||
StartX, StartY, StartZ,
|
||
EndX, EndY, EndZ);
|
||
}
|
||
|
||
private static void SafeDelete(string directory)
|
||
{
|
||
if (!Directory.Exists(directory))
|
||
{
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
Directory.Delete(directory, true);
|
||
}
|
||
catch
|
||
{
|
||
}
|
||
}
|
||
}
|
||
}
|