add more splitter tests
This commit is contained in:
@@ -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)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user