add more splitter tests

This commit is contained in:
2026-04-25 21:33:59 +02:00
parent 134f23d69b
commit 5f7211dbe4

View File

@@ -365,8 +365,10 @@ TEST_CASE("BeltSystem: splitter alternates between outputA and outputB", "[belt]
// Splitter — filter routing
// ---------------------------------------------------------------------------
TEST_CASE("BeltSystem: splitter routes filtered item to matching output", "[belt]")
TEST_CASE("BeltSystem: splitter routes to preferred output when item matches both filters", "[belt]")
{
// filterA = iron_ore, filterB = {} (accept all) → iron_ore matches both.
// With nextOutputIsA=true initially, alternation sends the item to A.
BeltSystem bs(kFastBeltSpeed);
const QPoint tileIn(0, 0);
@@ -379,13 +381,11 @@ TEST_CASE("BeltSystem: splitter routes filtered item to matching output", "[belt
bs.placeBelt(tileA, Rotation::North);
bs.placeBelt(tileB, Rotation::South);
// Filter: outputA = iron_ore only; outputB = accept all.
// iron_ore matches both filters → alternation; preferred = outputA (nextOutputIsA=true).
bs.setSplitterFilters(tileSpl, {ItemType{"iron_ore"}}, {});
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick(); // tileIn -> splitter back
bs.tick(); // back -> frontA (filter + alternation preferred outputA)
bs.tick(); // back -> frontA (both match, alternation, preferred A)
bs.tick(); // frontA -> tileA
bs.tick(); // item at tileA output edge
@@ -393,6 +393,163 @@ TEST_CASE("BeltSystem: splitter routes filtered item to matching output", "[belt
REQUIRE_FALSE(bs.tryTakeItem(Port{tileB, Rotation::South}).has_value());
}
TEST_CASE("BeltSystem: splitter routes item to output A when only filter A matches", "[belt]")
{
// filterA = {iron_ore}, filterB = {copper_ore}: iron_ore matches A exclusively → goes to A.
BeltSystem bs(kFastBeltSpeed);
const QPoint tileIn(0, 0);
const QPoint tileSpl(1, 0);
bs.placeBelt(tileIn, Rotation::East);
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
bs.setSplitterFilters(tileSpl, {ItemType{"iron_ore"}}, {ItemType{"copper_ore"}});
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick(); // tileIn -> splitter back
bs.tick(); // back -> frontA (exclusive match to A)
bs.tick(); // frontA reaches 1.0; no downstream belt, waits for building pickup
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value());
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
}
TEST_CASE("BeltSystem: splitter routes item to output B when only filter B matches", "[belt]")
{
// filterA = {copper_ore}, filterB = {iron_ore}: iron_ore matches B exclusively → goes to B.
BeltSystem bs(kFastBeltSpeed);
const QPoint tileIn(0, 0);
const QPoint tileSpl(1, 0);
bs.placeBelt(tileIn, Rotation::East);
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
bs.setSplitterFilters(tileSpl, {ItemType{"copper_ore"}}, {ItemType{"iron_ore"}});
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick();
bs.tick();
bs.tick();
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value());
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
}
TEST_CASE("BeltSystem: splitter alternates A then B when item matches both explicit filters", "[belt]")
{
// filterA = {iron_ore}, filterB = {iron_ore}: both match → strict alternation A, B, A.
BeltSystem bs(kFastBeltSpeed);
const QPoint tileIn(0, 0);
const QPoint tileSpl(1, 0);
bs.placeBelt(tileIn, Rotation::East);
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
bs.setSplitterFilters(tileSpl, {ItemType{"iron_ore"}}, {ItemType{"iron_ore"}});
// Item 1 → preferred A (nextOutputIsA=true initially).
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick(); bs.tick(); bs.tick();
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::North}).has_value());
// Item 2 → preferred B (nextOutputIsA toggled to false).
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick(); bs.tick(); bs.tick();
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::South}).has_value());
// Item 3 → preferred A again (nextOutputIsA toggled back to true).
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick(); bs.tick(); bs.tick();
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value());
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
}
TEST_CASE("BeltSystem: splitter routes unmatched item to the unfiltered output", "[belt]")
{
// filterA = {copper_ore} (non-empty), filterB = {} (accept all).
// iron_ore: matchesA=false, matchesB=true → goes to B.
BeltSystem bs(kFastBeltSpeed);
const QPoint tileIn(0, 0);
const QPoint tileSpl(1, 0);
bs.placeBelt(tileIn, Rotation::East);
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
bs.setSplitterFilters(tileSpl, {ItemType{"copper_ore"}}, {});
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick(); bs.tick(); bs.tick();
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value());
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
}
TEST_CASE("BeltSystem: splitter stalls when item matches neither filter", "[belt]")
{
// filterA = {copper_ore}, filterB = {iron_ingot}: iron_ore matches neither → stall.
BeltSystem bs(kFastBeltSpeed);
const QPoint tileIn(0, 0);
const QPoint tileSpl(1, 0);
bs.placeBelt(tileIn, Rotation::East);
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
bs.setSplitterFilters(tileSpl, {ItemType{"copper_ore"}}, {ItemType{"iron_ingot"}});
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick(); // tileIn -> splitter back
bs.tick(); // back reaches 0.5; routing fires but stalls (no filter match)
bs.tick(); // back stays at 0.5; stall persists
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value());
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
}
TEST_CASE("BeltSystem: splitter falls back to other output when preferred is blocked", "[belt]")
{
// filterA = filterB = {iron_ore}: both match → alternation.
// When preferred output is occupied, item goes to the other without toggling nextOutputIsA.
BeltSystem bs(kFastBeltSpeed);
const QPoint tileIn(0, 0);
const QPoint tileSpl(1, 0);
bs.placeBelt(tileIn, Rotation::East);
bs.placeSplitter(tileSpl, Rotation::North, Rotation::South);
bs.setSplitterFilters(tileSpl, {ItemType{"iron_ore"}}, {ItemType{"iron_ore"}});
// Item 1 → preferred A (nextOutputIsA=true → false after routing).
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick(); bs.tick(); bs.tick(); // frontA = item1 at 1.0
// Item 2 → preferred B (nextOutputIsA=false → true after routing). Take item2 to free frontB.
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick(); bs.tick(); bs.tick(); // frontB = item2 at 1.0
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::South}).has_value());
// frontA still holds item1; nextOutputIsA=true (prefer A).
// Item 3: both match, preferred A is occupied → fallback to B without toggling nextOutputIsA.
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick(); bs.tick(); bs.tick(); // frontB = item3 at 1.0
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value()); // item1 still in A
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value()); // item3 in B via fallback
// nextOutputIsA was not toggled by the fallback: next item should still prefer A.
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::North}).has_value()); // free frontA
REQUIRE(bs.tryTakeItem(Port{tileSpl, Rotation::South}).has_value()); // free frontB
bs.tryPutItem(tileIn, makeItem("iron_ore"));
bs.tick(); bs.tick(); bs.tick();
REQUIRE(bs.peekItem(Port{tileSpl, Rotation::North}).has_value()); // item4 → A (preferA still true)
REQUIRE_FALSE(bs.peekItem(Port{tileSpl, Rotation::South}).has_value());
}
// ---------------------------------------------------------------------------
// Splitter — direct building input (no output belts)
// ---------------------------------------------------------------------------