All Downloads are FREE. Search and download functionalities are using the official Maven repository.

robocode.control.RobotTestBed Maven / Gradle / Ivy

/*
 * Copyright (c) 2001-2023 Mathew A. Nelson and Robocode contributors
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * https://robocode.sourceforge.io/license/epl-v10.html
 */
package robocode.control;


import net.sf.robocode.io.Logger;

import robocode.control.events.*;
import robocode.control.snapshot.IRobotSnapshot;
import robocode.robotinterfaces.IBasicRobot;

import java.io.File;
import java.io.IOException;
import java.util.List;

import static robocode.util.Utils.*;


/**
 * RobotTestBed provides a superclass that can be extended in order to implement JUnit tests
 * for Robocode robots.
 * 

* The user must set the system property robocode.home to the location of the robocode installation, * otherwise we cannot set up the Robocode engine. If robocode.home is not a system property, * we throw a RuntimeException. * * @author Philip Johnson (original) * @author Pavel Savara (contributor) */ public abstract class RobotTestBed extends BattleAdaptor { /** * The Robocode game engine instance used for this test. */ protected static IRobocodeEngine engine; /** * The battlefield specification, which is the default. */ protected final BattlefieldSpecification battleFieldSpec = new BattlefieldSpecification(); /** * The number of errors generated during this battle so far. */ protected int errors = 0; protected StringBuilder errorText = new StringBuilder(); protected Throwable lastError = null; protected EngineErrorsListener engineErrorsListener = new EngineErrorsListener(); protected TestErrorListener testErrorListener = new TestErrorListener(); /** * The number of messages generated during this battle so far. */ protected static int messages = 0; protected static String robotsPath; /** * True to specify that the position during each turn should be printed out. */ public static boolean isDumpingPositions = false; /** * True to specify that each turn should be printed out. */ public static boolean isDumpingTurns = false; /** * True to specify that Robot output should be printed out. */ public static boolean isDumpingOutput = true; /** * True to specify that error messages should be printed out. */ public static boolean isDumpingErrors = true; /** * True to specify that Robot messages should be printed out. */ public static boolean isDumpingMessages = true; /** * Instance of tested robot */ protected R robotObject; public RobotTestBed() { if (engine == null) { beforeInit(); engine = new RobocodeEngine(); afterInit(); } errors = 0; messages = 0; } /** * Must return a fully qualified robot name to be in this battle. *

* You must override this event to specify the robot to battle in this test case. * * @return robot name. */ public abstract String getRobotName(); /** * Must return a fully qualified enemy robot name to be in this battle. *

* You must override this event to specify the robot to battle in this test case. * * @return robot name. */ public String getEnemyName() { return "sample.Target"; } /** * Provides the number of rounds in this battle. Defaults to 1. * Override this to change the number of rounds. * * @return The number of rounds. */ public int getNumRounds() { return 1; } /** * Returns a comma or space separated list like: x1,y1,heading1, x2,y2,heading2, which are the * coordinates and heading of robot #1 and #2. So "0,0,180, 50,80,270" means that robot #1 * has position (0,0) and heading 180, and robot #2 has position (50,80) and heading 270. *

* Override this method to explicitly specify the initial positions. *

* Defaults to null, which means that the initial positions are determined randomly. Since * battles are deterministic by default, the initial positions are randomly chosen but will * always be the same each time you run the test case. * * @return The list of initial positions. */ public String getInitialPositions() { return null; } /** * Provides the number of robots in this battle. * * @param robotList The list of robots. * @return The number of robots in this battle. */ public int getExpectedRobotCount(String robotList) { return robotList.split("[\\s,;]+").length; } /** * Defaults to true, indicating that the battle is deterministic and robots will always start * in the same position each time. *

* Override to support random initialization. * * @return True if the battle will be deterministic. */ public boolean isDeterministic() { return true; } public boolean isEnableRecording() { return false; } public boolean isEnableScreenshots() { return false; } protected void beforeInit() { System.setProperty("EXPERIMENTAL", "true"); System.setProperty("TESTING", "true"); System.setProperty("robocode.options.battle.desiredTPS", "10000"); try { File robotsPathFile = new File("../.sandbox/robots").getCanonicalFile().getAbsoluteFile(); robotsPath = robotsPathFile.getPath(); } catch (IOException e) { e.printStackTrace(Logger.realErr); throw new Error(e); } System.setProperty("ROBOTPATH", robotsPath); if (isEnableScreenshots()) { System.setProperty("PAINTING", "true"); } } protected void afterInit() { if (isEnableScreenshots()) { engine.setVisible(true); } } /** * The setup method run before each test, which sets up the listener on the engine for testing. * Don't override this method; instead, override runSetup to add behavior before the test * battle starts. */ protected void before() { engine.addBattleListener(engineErrorsListener); engine.addBattleListener(testErrorListener); if (isDeterministic()) { RandomFactory.resetDeterministic(0); } errors = 0; errorText = new StringBuilder(); messages = 0; lastError = null; } protected void after() { engine.removeBattleListener(engineErrorsListener); engine.removeBattleListener(testErrorListener); } protected void run(String robotName, String enemyName) { runSetup(); runBattle(robotName + "," + enemyName, getNumRounds(), getInitialPositions()); runTeardown(); final int expectedErrors = getExpectedErrors(); if (lastError != null) { Class errorClass = lastError.getClass(); if (RuntimeException.class.isAssignableFrom(errorClass)) { throw (RuntimeException) lastError; } else if (Error.class.isAssignableFrom(errorClass)) { throw (Error) lastError; } else { throw new Error(lastError); } } if (errors != expectedErrors) { throw new AssertionError( "Number of errors " + errors + " is different than expected " + expectedErrors + "\n" + errorText + "======================================================================"); } } protected void run(String enemyName) { run(getRobotName(), enemyName); } protected void run() { run(getRobotName(), getEnemyName()); } protected int getExpectedErrors() { return 0; } protected void runSetup() { } protected void runTeardown() { } protected void runBattle(String robotList, int numRounds, String initialPositions) { final RobotSpecification[] robotSpecifications = engine.getLocalRepository(robotList); if (getExpectedRobotCount(robotList) > 0) { assertNotNull("Robot were not loaded", robotSpecifications); assertEquals("Robot were not loaded", getExpectedRobotCount(robotList), robotSpecifications.length); engine.runBattle(new BattleSpecification(numRounds, battleFieldSpec, robotSpecifications), initialPositions, true, isEnableRecording()); } } class EngineErrorsListener extends BattleAdaptor { public void onBattleMessage(BattleMessageEvent event) { if (isDumpingMessages) { Logger.realOut.println(event.getMessage()); } messages++; } @Override public void onRoundStarted(RoundStartedEvent event) { List robotObjects = event.getRobotObjects(); if (robotObjects != null && !robotObjects.isEmpty()) { RobotTestBed.this.robotObject = (R) robotObjects.get(0); } } @Override public void onBattleError(BattleErrorEvent event) { String error = event.getError(); if (isDumpingErrors) { Logger.realErr.println(error); } errorText.append("----------err #"); errorText.append(errors); errorText.append("--------------------------------------------------\n"); errorText.append(error); errorText.append("\n"); errors++; } @Override public void onTurnEnded(TurnEndedEvent event) { if (isDumpingTurns) { Logger.realOut.println("turn " + event.getTurnSnapshot().getTurn()); } for (IRobotSnapshot robot : event.getTurnSnapshot().getRobots()) { if (isDumpingPositions) { Logger.realOut.print(robot.getVeryShortName()); Logger.realOut.print(" X:"); Logger.realOut.print(robot.getX()); Logger.realOut.print(" Y:"); Logger.realOut.print(robot.getY()); Logger.realOut.print(" V:"); Logger.realOut.print(robot.getVelocity()); Logger.realOut.println(); } if (isDumpingOutput) { String outputSnapshot = robot.getOutputStreamSnapshot(); if (outputSnapshot != null) { Logger.realOut.print(outputSnapshot); } } } } } class TestErrorListener implements IBattleListener { protected void handleError(Throwable ex) { if (RobotTestBed.this.lastError == null) { RobotTestBed.this.lastError = ex; if (isEnableScreenshots()) { RobotTestBed.engine.takeScreenshot(); } RobotTestBed.engine.abortCurrentBattle(false); } } @Override public void onBattleStarted(BattleStartedEvent event) { try { RobotTestBed.this.onBattleStarted(event); } catch (Error ex) { handleError(ex); } } @Override public void onBattleFinished(BattleFinishedEvent event) { try { RobotTestBed.this.onBattleFinished(event); } catch (Error ex) { handleError(ex); } } @Override public void onBattleCompleted(BattleCompletedEvent event) { try { RobotTestBed.this.onBattleCompleted(event); } catch (Error ex) { handleError(ex); } } @Override public void onBattlePaused(BattlePausedEvent event) { try { RobotTestBed.this.onBattlePaused(event); } catch (Error ex) { handleError(ex); } } @Override public void onBattleResumed(BattleResumedEvent event) { try { RobotTestBed.this.onBattleResumed(event); } catch (Error ex) { handleError(ex); } } @Override public void onRoundStarted(RoundStartedEvent event) { try { RobotTestBed.this.onRoundStarted(event); } catch (Error ex) { handleError(ex); } } @Override public void onRoundEnded(RoundEndedEvent event) { try { RobotTestBed.this.onRoundEnded(event); } catch (Error ex) { handleError(ex); } } @Override public void onTurnStarted(TurnStartedEvent event) { try { RobotTestBed.this.onTurnStarted(event); } catch (Error ex) { handleError(ex); } } @Override public void onTurnEnded(TurnEndedEvent event) { try { if (RobotTestBed.this.lastError == null) { RobotTestBed.this.onTurnEnded(event); } } catch (Error ex) { handleError(ex); } } @Override public void onBattleMessage(BattleMessageEvent event) { try { RobotTestBed.this.onBattleMessage(event); } catch (Error ex) { handleError(ex); } } @Override public void onBattleError(BattleErrorEvent event) { try { RobotTestBed.this.onBattleError(event); Throwable errorInstance = event.getErrorInstance(); if (errorInstance != null) { handleError(errorInstance); } } catch (Error ex) { handleError(ex); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy