using System; using System.Collections.Generic; using System.Linq; using FsCheck; using XplorePlane.Models; namespace XplorePlane.Tests.Generators { /// /// FsCheck 自定义生成器:为配方相关模型定义 Arbitrary 生成器。 /// 使用 Arb.Register<RecipeGenerators>() 注册。 /// public class RecipeGenerators { // ── PipelineModel: class with settable properties ── public static Arbitrary PipelineModelArb() { var gen = from id in Arb.Default.Guid().Generator from name in Arb.Default.NonEmptyString().Generator from deviceId in Arb.Default.NonEmptyString().Generator from ticks in Gen.Choose(0, int.MaxValue) let createdAt = DateTime.MinValue.AddTicks((long)ticks * 10000) let updatedAt = createdAt select new PipelineModel { Id = id, Name = name.Get, DeviceId = deviceId.Get, CreatedAt = createdAt, UpdatedAt = updatedAt, Nodes = new List() }; return Arb.From(gen); } // ── RecipeStep: reuses StateGenerators for hardware states ── public static Arbitrary RecipeStepArb() { var gen = from stepIndex in Gen.Choose(0, 100) from motion in StateGenerators.MotionStateArb().Generator from ray in StateGenerators.RaySourceStateArb().Generator from detector in StateGenerators.DetectorStateArb().Generator from pipeline in PipelineModelArb().Generator select new RecipeStep(stepIndex, motion, ray, detector, pipeline); return Arb.From(gen); } // ── InspectionRecipe: 0-10 steps with sequential StepIndex ── public static Arbitrary InspectionRecipeArb() { var gen = from id in Arb.Default.Guid().Generator from name in Arb.Default.NonEmptyString().Generator from ticks in Gen.Choose(0, int.MaxValue) let createdAt = DateTime.MinValue.AddTicks((long)ticks * 10000) let updatedAt = createdAt from stepCount in Gen.Choose(0, 10) from steps in GenSequentialSteps(stepCount) select new InspectionRecipe( id, name.Get, createdAt, updatedAt, steps.AsReadOnly()); return Arb.From(gen); } // ── RecipeExecutionState ── public static Arbitrary RecipeExecutionStateArb() { var gen = from totalSteps in Gen.Choose(0, 50) from currentStep in Gen.Choose(0, Math.Max(totalSteps, 1) - 1) from status in Gen.Elements( RecipeExecutionStatus.Idle, RecipeExecutionStatus.Running, RecipeExecutionStatus.Paused, RecipeExecutionStatus.Completed, RecipeExecutionStatus.Error) from recipeName in Arb.Default.NonEmptyString().Generator select new RecipeExecutionState(currentStep, totalSteps, status, recipeName.Get); return Arb.From(gen); } // ── Helper: generate a list of steps with sequential StepIndex 0..n-1 ── private static Gen> GenSequentialSteps(int count) { if (count == 0) return Gen.Constant(new List()); return Gen.Sequence( Enumerable.Range(0, count).Select(i => from motion in StateGenerators.MotionStateArb().Generator from ray in StateGenerators.RaySourceStateArb().Generator from detector in StateGenerators.DetectorStateArb().Generator from pipeline in PipelineModelArb().Generator select new RecipeStep(i, motion, ray, detector, pipeline) ) ).Select(steps => steps.ToList()); } } }