位置节点增加保存图像到本地的功能;支持输入图像
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using FsCheck;
|
||||
using FsCheck.Fluent;
|
||||
using FsCheck.Xunit;
|
||||
using XplorePlane.Models;
|
||||
|
||||
namespace XplorePlane.Tests.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// FsCheck property-based tests for BatchCaptureResult JSON serialization round-trip.
|
||||
/// Property 10: 摘要 JSON 序列化往返
|
||||
/// </summary>
|
||||
public class SummarySerializationPropertyTests
|
||||
{
|
||||
// ── Generators ───────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Generates a non-null string (FsCheck may generate nulls by default).
|
||||
/// </summary>
|
||||
private static Gen<string> NonNullStringGen =>
|
||||
ArbMap.Default.GeneratorFor<NonEmptyString>()
|
||||
.Select(s => s.Get)
|
||||
.Or(Gen.Constant(string.Empty));
|
||||
|
||||
/// <summary>
|
||||
/// Generates a nullable string (either null or a non-empty string).
|
||||
/// </summary>
|
||||
private static Gen<string> NullableStringGen =>
|
||||
Gen.OneOf(
|
||||
Gen.Constant<string>(null),
|
||||
ArbMap.Default.GeneratorFor<NonEmptyString>().Select(s => s.Get));
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random PositionResult with valid field values.
|
||||
/// </summary>
|
||||
private static Gen<PositionResult> PositionResultGen =>
|
||||
from nodeName in NonNullStringGen
|
||||
from nodeIndex in Gen.Choose(0, 1000)
|
||||
from status in Gen.Elements("Success", "Failed")
|
||||
from errorMessage in NullableStringGen
|
||||
from imagePath in NullableStringGen
|
||||
select new PositionResult
|
||||
{
|
||||
NodeName = nodeName,
|
||||
NodeIndex = nodeIndex,
|
||||
Status = status,
|
||||
ErrorMessage = errorMessage,
|
||||
ImagePath = imagePath
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random BatchCaptureResult with valid field values.
|
||||
/// </summary>
|
||||
private static Gen<BatchCaptureResult> BatchCaptureResultGen =>
|
||||
from programName in NonNullStringGen
|
||||
from startTime in NonNullStringGen
|
||||
from durationSeconds in Gen.Choose(0, 100000).Select(i => i / 100.0)
|
||||
from totalPositions in Gen.Choose(0, 50)
|
||||
from succeededPositions in Gen.Choose(0, 50)
|
||||
from failedPositions in Gen.Choose(0, 50)
|
||||
from savedImageCount in Gen.Choose(0, 100)
|
||||
from status in Gen.Elements("Completed", "Cancelled")
|
||||
from completedBeforeCancel in Gen.OneOf(
|
||||
Gen.Constant<int?>(null),
|
||||
Gen.Choose(0, 50).Select(i => (int?)i))
|
||||
from notExecutedAfterCancel in Gen.OneOf(
|
||||
Gen.Constant<int?>(null),
|
||||
Gen.Choose(0, 50).Select(i => (int?)i))
|
||||
from positionCount in Gen.Choose(0, 5)
|
||||
from positions in Gen.ListOf<PositionResult>(PositionResultGen, positionCount)
|
||||
select new BatchCaptureResult
|
||||
{
|
||||
ProgramName = programName,
|
||||
StartTime = startTime,
|
||||
DurationSeconds = durationSeconds,
|
||||
TotalPositions = totalPositions,
|
||||
SucceededPositions = succeededPositions,
|
||||
FailedPositions = failedPositions,
|
||||
SavedImageCount = savedImageCount,
|
||||
Status = status,
|
||||
CompletedBeforeCancel = completedBeforeCancel,
|
||||
NotExecutedAfterCancel = notExecutedAfterCancel,
|
||||
Positions = positions.ToList()
|
||||
};
|
||||
|
||||
// ── Property 10 ─────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// **Validates: Requirements 4.3**
|
||||
///
|
||||
/// For any valid BatchCaptureResult object, serializing to JSON and
|
||||
/// deserializing back produces an object with identical field values.
|
||||
/// </summary>
|
||||
[Property(MaxTest = 100)]
|
||||
public Property BatchCaptureResult_JsonRoundTrip_PreservesAllFields()
|
||||
{
|
||||
return Prop.ForAll(
|
||||
BatchCaptureResultGen.ToArbitrary(),
|
||||
original =>
|
||||
{
|
||||
// Serialize to JSON
|
||||
var json = JsonSerializer.Serialize(original);
|
||||
|
||||
// Deserialize back
|
||||
var deserialized = JsonSerializer.Deserialize<BatchCaptureResult>(json);
|
||||
|
||||
// Verify all scalar properties
|
||||
bool scalarFieldsMatch =
|
||||
deserialized.ProgramName == original.ProgramName &&
|
||||
deserialized.StartTime == original.StartTime &&
|
||||
deserialized.DurationSeconds == original.DurationSeconds &&
|
||||
deserialized.TotalPositions == original.TotalPositions &&
|
||||
deserialized.SucceededPositions == original.SucceededPositions &&
|
||||
deserialized.FailedPositions == original.FailedPositions &&
|
||||
deserialized.SavedImageCount == original.SavedImageCount &&
|
||||
deserialized.Status == original.Status;
|
||||
|
||||
// Verify nullable int properties
|
||||
bool nullableFieldsMatch =
|
||||
deserialized.CompletedBeforeCancel == original.CompletedBeforeCancel &&
|
||||
deserialized.NotExecutedAfterCancel == original.NotExecutedAfterCancel;
|
||||
|
||||
// Verify list property count
|
||||
bool listCountMatch =
|
||||
deserialized.Positions.Count == original.Positions.Count;
|
||||
|
||||
// Verify each position in the list
|
||||
bool positionsMatch = true;
|
||||
for (int i = 0; i < original.Positions.Count; i++)
|
||||
{
|
||||
var orig = original.Positions[i];
|
||||
var deser = deserialized.Positions[i];
|
||||
if (deser.NodeName != orig.NodeName ||
|
||||
deser.NodeIndex != orig.NodeIndex ||
|
||||
deser.Status != orig.Status ||
|
||||
deser.ErrorMessage != orig.ErrorMessage ||
|
||||
deser.ImagePath != orig.ImagePath)
|
||||
{
|
||||
positionsMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return scalarFieldsMatch && nullableFieldsMatch &&
|
||||
listCountMatch && positionsMatch;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user