Files
goon-game/app/src/androidTest/java/com/projectsacrifice/game/LateLevelPlaythroughTest.java
T

200 lines
8.5 KiB
Java

package com.projectsacrifice.game;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.app.Instrumentation;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Production-input acceptance coverage for levels 6, 7, 9 and 10. */
@RunWith(AndroidJUnit4.class)
public final class LateLevelPlaythroughTest {
private static final int CONSECUTIVE_RUNS = 3;
@Test
public void frozenVolunteerIsUsedToCompleteLevelSixThreeTimes() {
try (ActivityScenario<MainActivity> activity = ActivityScenario.launch(MainActivity.class)) {
GameDriver driver = GameDriver.from(activity);
for (int run = 1; run <= CONSECUTIVE_RUNS; run++) {
LevelPlaytestBot.Result result = new LevelPlaytestBot().run(
driver,
LateLevelScenarios.levelSix(),
30_000L
);
assertTrue("run=" + run + "; " + result.diagnostic(), result.completed());
assertEquals("run=" + run + "; intended route uses one frozen volunteer",
1, result.deaths());
assertEquals("run=" + run + "; frozen block must remain in the puzzle",
1, result.corpseCount());
}
}
}
@Test
public void conductiveVolunteerKeepsLevelSevenDoorOpenThreeTimes() {
try (ActivityScenario<MainActivity> activity = ActivityScenario.launch(MainActivity.class)) {
GameDriver driver = GameDriver.from(activity);
for (int run = 1; run <= CONSECUTIVE_RUNS; run++) {
LevelPlaytestBot.Result result = new LevelPlaytestBot().run(
driver,
LateLevelScenarios.levelSeven(),
25_000L
);
assertTrue("run=" + run + "; " + result.diagnostic(), result.completed());
assertEquals("run=" + run + "; intended route uses one conductive volunteer",
1, result.deaths());
assertEquals("run=" + run + "; conductive body must remain in the relay",
1, result.corpseCount());
assertTrue("run=" + run + "; conductive relay must open the door",
result.doorOpened());
}
}
}
@Test
public void weightAndPowerOpenLevelNineDoorThreeTimes() {
try (ActivityScenario<MainActivity> activity = ActivityScenario.launch(MainActivity.class)) {
GameDriver driver = GameDriver.from(activity);
for (int run = 1; run <= CONSECUTIVE_RUNS; run++) {
LevelPlaytestBot.Result result = new LevelPlaytestBot().run(
driver,
LateLevelScenarios.levelNine(),
35_000L
);
assertTrue("run=" + run + "; " + result.diagnostic(), result.completed());
assertEquals("run=" + run + "; weight and power require two volunteers",
2, result.deaths());
assertEquals("run=" + run + "; both signal bodies must remain",
2, result.corpseCount());
assertTrue("run=" + run + "; both signals must open the AND door",
result.doorOpened());
}
}
}
@Test
public void masteryProcessionCompletesLevelTenThreeTimes() {
try (ActivityScenario<MainActivity> activity = ActivityScenario.launch(MainActivity.class)) {
GameDriver driver = GameDriver.from(activity);
for (int run = 1; run <= CONSECUTIVE_RUNS; run++) {
LevelPlaytestBot.Result result = new LevelPlaytestBot().run(
driver,
LateLevelScenarios.levelTen(),
55_000L
);
assertTrue("run=" + run + "; " + result.diagnostic(), result.completed());
assertEquals("run=" + run + "; mastery route uses three volunteers",
3, result.deaths());
assertEquals("run=" + run + "; launch, freeze and relay bodies must remain",
3, result.corpseCount());
assertTrue("run=" + run + "; conductive relay must open the final door",
result.doorOpened());
}
}
}
@Test
public void masteryProcessionSurvivesThrottledFinalTraversalDecisions() {
try (ActivityScenario<MainActivity> activity = ActivityScenario.launch(MainActivity.class)) {
GameDriver driver = GameDriver.withFinalTraversalDelay(activity, 96L);
LevelPlaytestBot.Result result = new LevelPlaytestBot().run(
driver,
LateLevelScenarios.levelTen(),
65_000L
);
assertTrue(result.diagnostic(), result.completed());
assertEquals("throttling must not add accidental volunteers", 3, result.deaths());
assertEquals("all three intended bodies must remain", 3, result.corpseCount());
assertTrue("the conductive relay must still open the final door",
result.doorOpened());
}
}
private static final class GameDriver implements LevelPlaytestBot.Driver {
private final Instrumentation instrumentation;
private final GameView gameView;
private final long minimumDecisionMillis;
private final boolean throttleFinalTraversalOnly;
private GameView.AutomationSnapshot lastSnapshot;
private GameDriver(Instrumentation instrumentation, GameView gameView,
long minimumDecisionMillis, boolean throttleFinalTraversalOnly) {
this.instrumentation = instrumentation;
this.gameView = gameView;
this.minimumDecisionMillis = minimumDecisionMillis;
this.throttleFinalTraversalOnly = throttleFinalTraversalOnly;
}
static GameDriver from(ActivityScenario<MainActivity> activity) {
return create(activity, 32L, false);
}
static GameDriver withFinalTraversalDelay(ActivityScenario<MainActivity> activity,
long minimumDecisionMillis) {
return create(activity, minimumDecisionMillis, true);
}
private static GameDriver create(ActivityScenario<MainActivity> activity,
long minimumDecisionMillis,
boolean throttleFinalTraversalOnly) {
GameView[] view = new GameView[1];
activity.onActivity(current -> view[0] = current.gameViewForAutomation());
return new GameDriver(InstrumentationRegistry.getInstrumentation(), view[0],
minimumDecisionMillis, throttleFinalTraversalOnly);
}
@Override
public void startLevel(int oneBasedLevelNumber) {
instrumentation.runOnMainSync(
() -> gameView.startLevelForAutomation(oneBasedLevelNumber));
}
@Override
public void setInput(boolean moveLeft, boolean moveRight, boolean jump) {
instrumentation.runOnMainSync(
() -> gameView.setAutomationInput(moveLeft, moveRight, jump));
}
@Override
public GameView.AutomationSnapshot snapshot() {
GameView.AutomationSnapshot[] value = new GameView.AutomationSnapshot[1];
instrumentation.runOnMainSync(() -> value[0] = gameView.automationSnapshot());
lastSnapshot = value[0];
return lastSnapshot;
}
@Override
public void waitForNextDecision(long millis) {
try {
boolean inFinalTraversalWindow = throttleFinalTraversalOnly
&& lastSnapshot != null
&& lastSnapshot.levelNumber() == 10
&& lastSnapshot.deaths() == 3
&& lastSnapshot.playerX() >= 1_200f
&& lastSnapshot.playerX() <= 2_200f;
Thread.sleep(inFinalTraversalWindow
? Math.max(millis, minimumDecisionMillis) : millis);
} catch (InterruptedException interrupted) {
Thread.currentThread().interrupt();
throw new AssertionError("Late-level bot interrupted", interrupted);
}
}
}
}