// Headless tests of milestone M5 (DESIGN_UE.md section 10): the packer's arithmetic, the pipe route, the ore ledger. #include "Tests/DescentTestCommon.h" #include "Systems/DescentCargo.h" #include "Vehicles/CarrierLayout.h" #if WITH_DEV_AUTOMATION_TESTS IMPLEMENT_SIMPLE_AUTOMATION_TEST(FDescentCargoPackerMathTest, "Descent.Cargo.PackerMath", DESCENT_TEST_FLAGS) bool FDescentCargoPackerMathTest::RunTest(const FString& Parameters) { // 14 ferrite + 3 cobalt in: 2 ferrite boxes, partials [2, 3, 0] Descent::Cargo::FPackerState P; for (int32 I = 0; I < 14; ++I) { P.Buffer.Add(0); } for (int32 I = 0; I < 3; ++I) { P.Buffer.Add(1); } int32 Boxes[3] = { 0, 0, 0 }; for (int32 I = 0; I < 600; ++I) { P.Step(1.0 / 30.0, true, [&Boxes](uint8 T, int32 Count) { if (Count == 6) { ++Boxes[T]; } }); } TestEqual(TEXT("2 ferrite boxes"), Boxes[0], 2); TestEqual(TEXT("no cobalt box"), Boxes[1], 0); TestEqual(TEXT("partial ferrite 2"), P.Partial[0], 2); TestEqual(TEXT("partial cobalt 3"), P.Partial[1], 3); TestEqual(TEXT("partial pearl 0"), P.Partial[2], 0); TestEqual(TEXT("the buffer is empty"), P.Buffer.Num(), 0); // without a free slot a full partial waits and its pieces stay in the buffer Descent::Cargo::FPackerState Q; for (int32 I = 0; I < 9; ++I) { Q.Buffer.Add(2); } int32 Made = 0; for (int32 I = 0; I < 600; ++I) { Q.Step(1.0 / 30.0, false, [&Made](uint8, int32) { ++Made; }); } TestEqual(TEXT("no slot: no box"), Made, 0); TestEqual(TEXT("no slot: the partial waits full"), Q.Partial[2], 6); TestEqual(TEXT("no slot: three pieces stay in the buffer"), Q.Buffer.Num(), 3); TestTrue(TEXT("the buffer takes pieces while buffer + pipe is under 24"), Q.CanAccept(20)); TestFalse(TEXT("and refuses at 24"), Q.CanAccept(21)); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FDescentCargoPipeRouteTest, "Descent.Cargo.PipeRoute", DESCENT_TEST_FLAGS) bool FDescentCargoPipeRouteTest::RunTest(const FString& Parameters) { const TArray& R = Descent::Cargo::OrePipeRouteM(); const double L = Descent::Cargo::RouteLengthM(R); AddInfo(FString::Printf(TEXT("ore pipe: %d vertices, %.2f m"), R.Num(), L)); TestTrue(TEXT("ore pipe length 72 +- 1 m"), FMath::Abs(L - 72.0) <= 1.0); for (const FVector& V : R) { TestTrue(*FString::Printf(TEXT("vertex (%.2f, %.2f, %.2f) is inside the hull"), V.X, V.Y, V.Z), FMath::Sqrt(V.Y * V.Y + V.Z * V.Z) < Descent::Carrier::HullRadius(V.X) && FMath::Abs(V.X) < 60.0); } TestTrue(TEXT("the route starts at the belly coupling"), Descent::Cargo::RoutePointM(R, 0.0).Equals(R[0], 1e-9)); TestTrue(TEXT("and ends in the packer inlet"), Descent::Cargo::RoutePointM(R, L + 5.0).Equals(R.Last(), 1e-9)); return true; } IMPLEMENT_SIMPLE_AUTOMATION_TEST(FDescentStoreLedgerTest, "Descent.Store.Ledger", DESCENT_TEST_FLAGS) bool FDescentStoreLedgerTest::RunTest(const FString& Parameters) { // ConsumeOre empties the emptiest box first TArray Counts = { 6, 2, 4 }; TestEqual(TEXT("3 taken"), Descent::Cargo::ConsumeFromCounts(Counts, 3), 3); TestTrue(TEXT("the box of 2 went first, then one from the box of 4"), Counts[0] == 6 && Counts[1] == 0 && Counts[2] == 3); TestEqual(TEXT("asking for more than there is takes what there is"), Descent::Cargo::ConsumeFromCounts(Counts, 20), 9); TestTrue(TEXT("all empty"), Counts[0] == 0 && Counts[1] == 0 && Counts[2] == 0); TestEqual(TEXT("names"), Descent::Cargo::RefinedName(1).ToString() + Descent::Cargo::OreName(2).ToString(), FString(TEXT("waferpearl"))); TestEqual(TEXT("values: ingot 115, wafer 230, lattice 500"), Descent::Tune::Cargo::RefinedValue[0] + Descent::Tune::Cargo::RefinedValue[1] + Descent::Tune::Cargo::RefinedValue[2], 845); return true; } #endif // WITH_DEV_AUTOMATION_TESTS