From 6790a84d86c3190c0bef912cf6610b765064650e Mon Sep 17 00:00:00 2001 From: "E.Gavrilov" Date: Tue, 18 Aug 2026 03:19:28 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BE=D0=BA=D1=80=D0=B0=D1=82=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20lifecycle=20overhead=20=D1=80=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D0=B8=D1=85=20=D0=B1=D0=BE=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../game/EarlyLevelPlaythroughTest.java | 39 ++++++++++--------- .../game/LevelPlaytestBotTest.java | 24 ++++++++++++ 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/app/src/androidTest/java/com/projectsacrifice/game/EarlyLevelPlaythroughTest.java b/app/src/androidTest/java/com/projectsacrifice/game/EarlyLevelPlaythroughTest.java index eb89abb..4b5fa44 100644 --- a/app/src/androidTest/java/com/projectsacrifice/game/EarlyLevelPlaythroughTest.java +++ b/app/src/androidTest/java/com/projectsacrifice/game/EarlyLevelPlaythroughTest.java @@ -17,30 +17,27 @@ import static org.junit.Assert.assertTrue; public final class EarlyLevelPlaythroughTest { @Test public void levelOneProvesMovementAndJumpWithoutDeaths() { - for (int pass = 1; pass <= 3; pass++) { - LevelPlaytestBot.Result result = run(EarlyLevelScenarios.levelOne(), 20_000L); + runThreeConsecutive(EarlyLevelScenarios.levelOne(), 20_000L, (pass, result) -> { assertTrue(passDiagnostic(pass, result), result.completed()); assertEquals("Pass " + pass + ": level 1 must not require a volunteer", 0, result.deaths()); - } + }); } @Test public void levelTwoBuildsTheIntendedThreeCorpseBridge() { - for (int pass = 1; pass <= 3; pass++) { - LevelPlaytestBot.Result result = run(EarlyLevelScenarios.levelTwo(), 30_000L); + runThreeConsecutive(EarlyLevelScenarios.levelTwo(), 30_000L, (pass, result) -> { assertTrue(passDiagnostic(pass, result), result.completed()); assertEquals("Pass " + pass + ": the intended bridge uses three volunteers", 3, result.deaths()); assertEquals("Pass " + pass + ": all three bridge bodies must remain", 3, result.corpseCount()); - } + }); } @Test public void levelThreeLeavesASawCorpseOnThePlateAndOpensTheDoor() { - for (int pass = 1; pass <= 3; pass++) { - LevelPlaytestBot.Result result = run(EarlyLevelScenarios.levelThree(), 25_000L); + runThreeConsecutive(EarlyLevelScenarios.levelThree(), 25_000L, (pass, result) -> { assertTrue(passDiagnostic(pass, result), result.completed()); assertEquals("Pass " + pass + ": the saw solution requires one volunteer", 1, result.deaths()); @@ -48,35 +45,41 @@ public final class EarlyLevelPlaythroughTest { 1, result.corpseCount()); assertTrue("Pass " + pass + ": the corpse must keep the pressure door open", result.doorOpened()); - } + }); } @Test public void levelFourTogglesAndRidesTheLiftToTheUpperGallery() { - for (int pass = 1; pass <= 3; pass++) { - LevelPlaytestBot.Result result = run(EarlyLevelScenarios.levelFour(), 30_000L); + runThreeConsecutive(EarlyLevelScenarios.levelFour(), 30_000L, (pass, result) -> { assertTrue(passDiagnostic(pass, result), result.completed()); assertEquals("Pass " + pass + ": the lift route must not require a volunteer", 0, result.deaths()); - } + }); } private static String passDiagnostic(int pass, LevelPlaytestBot.Result result) { return "Consecutive pass " + pass + ": " + result.diagnostic(); } - private static LevelPlaytestBot.Result run(LevelPlaytestBot.Scenario scenario, long timeoutMillis) { + private static void runThreeConsecutive(LevelPlaytestBot.Scenario scenario, long timeoutMillis, + ResultAssertion assertion) { try (ActivityScenario activity = ActivityScenario.launch(MainActivity.class)) { final GameView[] view = new GameView[1]; activity.onActivity(mainActivity -> view[0] = mainActivity.gameViewForAutomation()); - return new LevelPlaytestBot().run( - new AndroidDriver(InstrumentationRegistry.getInstrumentation(), view[0]), - scenario, - timeoutMillis - ); + AndroidDriver driver = new AndroidDriver( + InstrumentationRegistry.getInstrumentation(), view[0]); + LevelPlaytestBot bot = new LevelPlaytestBot(); + for (int pass = 1; pass <= 3; pass++) { + assertion.verify(pass, bot.run(driver, scenario, timeoutMillis)); + } } } + @FunctionalInterface + private interface ResultAssertion { + void verify(int pass, LevelPlaytestBot.Result result); + } + private static final class AndroidDriver implements LevelPlaytestBot.Driver { private final Instrumentation instrumentation; private final GameView gameView; diff --git a/app/src/test/java/com/projectsacrifice/game/LevelPlaytestBotTest.java b/app/src/test/java/com/projectsacrifice/game/LevelPlaytestBotTest.java index f8df9f5..3c19fdf 100644 --- a/app/src/test/java/com/projectsacrifice/game/LevelPlaytestBotTest.java +++ b/app/src/test/java/com/projectsacrifice/game/LevelPlaytestBotTest.java @@ -25,6 +25,25 @@ public final class LevelPlaytestBotTest { assertEquals("Reach the goal", result.lastStep()); } + @Test + public void sameDriverCanRestartTheCompleteScenarioThreeTimes() { + ScriptedDriver driver = new ScriptedDriver(); + LevelPlaytestBot.Scenario scenario = LevelPlaytestBot.Scenario + .builder(7, "Repeated scenario") + .step("Create a corpse", 1_000L, LevelPlaytestBot.Input.right(), + snapshot -> snapshot.deaths() >= 1) + .step("Reach the goal", 1_000L, LevelPlaytestBot.Input.left(), + GameView.AutomationSnapshot::completed) + .build(); + + for (int pass = 1; pass <= 3; pass++) { + LevelPlaytestBot.Result result = new LevelPlaytestBot().run(driver, scenario, 5_000L); + assertTrue("Pass " + pass + ": " + result.diagnostic(), result.completed()); + assertEquals("Pass " + pass, "Reach the goal", result.lastStep()); + assertEquals("Pass " + pass, 1, result.deaths()); + } + } + @Test public void stepTimeoutReportsItsNameAndObservableSnapshot() { ScriptedDriver driver = new ScriptedDriver(false); @@ -81,6 +100,11 @@ public final class LevelPlaytestBotTest { @Override public void startLevel(int oneBasedLevelNumber) { startedLevel = oneBasedLevelNumber; + nowMillis = 0L; + moveLeft = false; + moveRight = false; + deaths = 0; + completed = false; } @Override