minecraft-src/net/minecraft/gametest/framework/GameTestRunner.java
2025-07-04 03:45:38 +03:00

242 lines
8.9 KiB
Java

package net.minecraft.gametest.framework;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.mojang.logging.LogUtils;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSet;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import net.minecraft.Util;
import net.minecraft.core.Holder;
import net.minecraft.network.protocol.game.DebugPackets;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
public class GameTestRunner {
public static final int DEFAULT_TESTS_PER_ROW = 8;
private static final Logger LOGGER = LogUtils.getLogger();
final ServerLevel level;
private final GameTestTicker testTicker;
private final List<GameTestInfo> allTestInfos;
private ImmutableList<GameTestBatch> batches;
final List<GameTestBatchListener> batchListeners = Lists.<GameTestBatchListener>newArrayList();
private final List<GameTestInfo> scheduledForRerun = Lists.<GameTestInfo>newArrayList();
private final GameTestRunner.GameTestBatcher testBatcher;
private boolean stopped = true;
@Nullable
private Holder<TestEnvironmentDefinition> currentEnvironment;
private final GameTestRunner.StructureSpawner existingStructureSpawner;
private final GameTestRunner.StructureSpawner newStructureSpawner;
final boolean haltOnError;
protected GameTestRunner(
GameTestRunner.GameTestBatcher testBatcher,
Collection<GameTestBatch> batches,
ServerLevel level,
GameTestTicker testTicker,
GameTestRunner.StructureSpawner existingStructureSpawner,
GameTestRunner.StructureSpawner newStructureSpawner,
boolean haltOnError
) {
this.level = level;
this.testTicker = testTicker;
this.testBatcher = testBatcher;
this.existingStructureSpawner = existingStructureSpawner;
this.newStructureSpawner = newStructureSpawner;
this.batches = ImmutableList.copyOf(batches);
this.haltOnError = haltOnError;
this.allTestInfos = (List<GameTestInfo>)this.batches.stream().flatMap(gameTestBatch -> gameTestBatch.gameTestInfos().stream()).collect(Util.toMutableList());
testTicker.setRunner(this);
this.allTestInfos.forEach(gameTestInfo -> gameTestInfo.addListener(new ReportGameListener()));
}
public List<GameTestInfo> getTestInfos() {
return this.allTestInfos;
}
public void start() {
this.stopped = false;
this.runBatch(0);
}
public void stop() {
this.stopped = true;
if (this.currentEnvironment != null) {
this.endCurrentEnvironment();
}
}
public void rerunTest(GameTestInfo test) {
GameTestInfo gameTestInfo = test.copyReset();
test.getListeners().forEach(gameTestListener -> gameTestListener.testAddedForRerun(test, gameTestInfo, this));
this.allTestInfos.add(gameTestInfo);
this.scheduledForRerun.add(gameTestInfo);
if (this.stopped) {
this.runScheduledRerunTests();
}
}
void runBatch(int index) {
if (index >= this.batches.size()) {
this.endCurrentEnvironment();
this.runScheduledRerunTests();
} else {
final GameTestBatch gameTestBatch = (GameTestBatch)this.batches.get(index);
this.existingStructureSpawner.onBatchStart(this.level);
this.newStructureSpawner.onBatchStart(this.level);
Collection<GameTestInfo> collection = this.createStructuresForBatch(gameTestBatch.gameTestInfos());
LOGGER.info(
"Running test environment '{}' batch {} ({} tests)...", gameTestBatch.environment().getRegisteredName(), gameTestBatch.index(), collection.size()
);
if (this.currentEnvironment != gameTestBatch.environment()) {
this.endCurrentEnvironment();
this.currentEnvironment = gameTestBatch.environment();
this.currentEnvironment.value().setup(this.level);
}
this.batchListeners.forEach(gameTestBatchListener -> gameTestBatchListener.testBatchStarting(gameTestBatch));
final MultipleTestTracker multipleTestTracker = new MultipleTestTracker();
collection.forEach(multipleTestTracker::addTestToTrack);
multipleTestTracker.addListener(new GameTestListener() {
private void testCompleted() {
if (multipleTestTracker.isDone()) {
GameTestRunner.this.batchListeners.forEach(gameTestBatchListener -> gameTestBatchListener.testBatchFinished(gameTestBatch));
LongSet longSet = new LongArraySet(GameTestRunner.this.level.getForceLoadedChunks());
longSet.forEach(l -> GameTestRunner.this.level.setChunkForced(ChunkPos.getX(l), ChunkPos.getZ(l), false));
GameTestRunner.this.runBatch(index + 1);
}
}
@Override
public void testStructureLoaded(GameTestInfo testInfo) {
}
@Override
public void testPassed(GameTestInfo test, GameTestRunner runner) {
this.testCompleted();
}
@Override
public void testFailed(GameTestInfo test, GameTestRunner runner) {
if (GameTestRunner.this.haltOnError) {
GameTestRunner.this.endCurrentEnvironment();
LongSet longSet = new LongArraySet(GameTestRunner.this.level.getForceLoadedChunks());
longSet.forEach(l -> GameTestRunner.this.level.setChunkForced(ChunkPos.getX(l), ChunkPos.getZ(l), false));
GameTestTicker.SINGLETON.clear();
} else {
this.testCompleted();
}
}
@Override
public void testAddedForRerun(GameTestInfo oldTest, GameTestInfo newTest, GameTestRunner runner) {
}
});
collection.forEach(this.testTicker::add);
}
}
void endCurrentEnvironment() {
if (this.currentEnvironment != null) {
this.currentEnvironment.value().teardown(this.level);
this.currentEnvironment = null;
}
}
private void runScheduledRerunTests() {
if (!this.scheduledForRerun.isEmpty()) {
LOGGER.info(
"Starting re-run of tests: {}", this.scheduledForRerun.stream().map(gameTestInfo -> gameTestInfo.id().toString()).collect(Collectors.joining(", "))
);
this.batches = ImmutableList.copyOf(this.testBatcher.batch(this.scheduledForRerun));
this.scheduledForRerun.clear();
this.stopped = false;
this.runBatch(0);
} else {
this.batches = ImmutableList.of();
this.stopped = true;
}
}
public void addListener(GameTestBatchListener listener) {
this.batchListeners.add(listener);
}
private Collection<GameTestInfo> createStructuresForBatch(Collection<GameTestInfo> batch) {
return batch.stream().map(this::spawn).flatMap(Optional::stream).toList();
}
private Optional<GameTestInfo> spawn(GameTestInfo test) {
return test.getTestBlockPos() == null ? this.newStructureSpawner.spawnStructure(test) : this.existingStructureSpawner.spawnStructure(test);
}
public static void clearMarkers(ServerLevel serverLevel) {
DebugPackets.sendGameTestClearPacket(serverLevel);
}
public static class Builder {
private final ServerLevel level;
private final GameTestTicker testTicker = GameTestTicker.SINGLETON;
private GameTestRunner.GameTestBatcher batcher = GameTestBatchFactory.fromGameTestInfo();
private GameTestRunner.StructureSpawner existingStructureSpawner = GameTestRunner.StructureSpawner.IN_PLACE;
private GameTestRunner.StructureSpawner newStructureSpawner = GameTestRunner.StructureSpawner.NOT_SET;
private final Collection<GameTestBatch> batches;
private boolean haltOnError = false;
private Builder(Collection<GameTestBatch> batches, ServerLevel level) {
this.batches = batches;
this.level = level;
}
public static GameTestRunner.Builder fromBatches(Collection<GameTestBatch> batches, ServerLevel level) {
return new GameTestRunner.Builder(batches, level);
}
public static GameTestRunner.Builder fromInfo(Collection<GameTestInfo> infos, ServerLevel level) {
return fromBatches(GameTestBatchFactory.fromGameTestInfo().batch(infos), level);
}
public GameTestRunner.Builder haltOnError(boolean haltOnError) {
this.haltOnError = haltOnError;
return this;
}
public GameTestRunner.Builder newStructureSpawner(GameTestRunner.StructureSpawner newStructureSpawner) {
this.newStructureSpawner = newStructureSpawner;
return this;
}
public GameTestRunner.Builder existingStructureSpawner(StructureGridSpawner existingStructureSpawner) {
this.existingStructureSpawner = existingStructureSpawner;
return this;
}
public GameTestRunner.Builder batcher(GameTestRunner.GameTestBatcher batcher) {
this.batcher = batcher;
return this;
}
public GameTestRunner build() {
return new GameTestRunner(this.batcher, this.batches, this.level, this.testTicker, this.existingStructureSpawner, this.newStructureSpawner, this.haltOnError);
}
}
public interface GameTestBatcher {
Collection<GameTestBatch> batch(Collection<GameTestInfo> collection);
}
public interface StructureSpawner {
GameTestRunner.StructureSpawner IN_PLACE = gameTestInfo -> Optional.of(gameTestInfo.prepareTestStructure().startExecution(1));
GameTestRunner.StructureSpawner NOT_SET = gameTestInfo -> Optional.empty();
Optional<GameTestInfo> spawnStructure(GameTestInfo gameTestInfo);
default void onBatchStart(ServerLevel level) {
}
}
}