commit 2358585522dc43bc42cace680cd0da3c731dd9b2 Author: Quentin Rouland Date: Wed Jul 16 18:06:02 2025 +0200 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e1c6d30 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.idea/* +build/* +*.iml +out/* +.gradle/* +target/* \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..07b7a81 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..64f73d2 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ + +# Kata Tondeuse + +Simulate the movement of Tondeuses (mowers) on a rectangular Pelouse (lawn), following a set of instructions. + +## Requirements + +- Java 8 or later +- Maven + +## Setup + +Clone this repository +```bash +git clone https://github.com/yourusername/katatondeuse.git +cd katatondeuse +``` + + +## Running the Application + +To run the application: + +```bash +mvn compile exec:java -Dexec.mainClass="com.qrouland.katatondeuse.Main" -Dexec.args="examples/input.txt" +``` + +## Running Tests + +To run all tests : + +```bash +mvn test +``` + + +## License + +This project is licensed under the WTFPL - Do What The F*** You Want To Public License. \ No newline at end of file diff --git a/examples/input.txt b/examples/input.txt new file mode 100644 index 0000000..ce3a66f --- /dev/null +++ b/examples/input.txt @@ -0,0 +1,5 @@ +5 5 +1 2 N +GAGAGAGAA +3 3 E +AADAADADDA \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..13afa31 --- /dev/null +++ b/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + com.qrouland.katatondeuse + kata-tondeuse + 1.0-SNAPSHOT + jar + + Kata Tondeuse + + + + WTFPL - Do What The F*** You Want To Public License + http://www.wtfpl.net/txt/copying/ + + + + + 2.26.0 + 5.5.2 + 1.8 + 1.8 + UTF-8 + 3.1.1 + 3.2.0 + 2.22.2 + 3.14.0 + + + + + org.mockito + mockito-core + ${mockito.version} + test + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.apache.commons + commons-lang3 + ${org.apache.commons.version} + + + + + + + org.apache.maven.plugins + maven-source-plugin + ${source.plugin.version} + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.plugin.version} + + + org.apache.maven.plugins + maven-compiler-plugin + + 16 + 16 + + + + + + \ No newline at end of file diff --git a/src/integration/java/com/qrouland/katatondeuse/MainIntegrationTest.java b/src/integration/java/com/qrouland/katatondeuse/MainIntegrationTest.java new file mode 100644 index 0000000..2ff9da6 --- /dev/null +++ b/src/integration/java/com/qrouland/katatondeuse/MainIntegrationTest.java @@ -0,0 +1,87 @@ +package com.qrouland.katatondeuse; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.*; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; + +public class MainIntegrationTest { + private final PrintStream originalOut = System.out; + private final PrintStream originalErr = System.err; + private ByteArrayOutputStream outContent; + private ByteArrayOutputStream errContent; + + @BeforeEach + public void setUpStreams() { + outContent = new ByteArrayOutputStream(); + errContent = new ByteArrayOutputStream(); + + System.setOut(new PrintStream(outContent)); + System.setErr(new PrintStream(errContent)); + } + + @AfterEach + public void restoreStreams() { + System.setOut(originalOut); + System.setErr(originalErr); + } + + @Test + public void testMain_ValidInputFile() throws IOException { + Path tempFile = Files.createTempFile("test-input", ".txt"); + Files.writeString(tempFile, + "5 5\n" + + "1 2 N\n" + + "GAGAGAGAA\n" + + "3 3 E\n" + + "AADAADADDA\n" + ); + + int code = Main.run(new String[]{tempFile.toString()}); + + String output = outContent.toString(); + + assertEquals(0, code); + assertTrue(output.contains("Pelouse 5 x 5")); + assertTrue(output.contains("Moved")); + + Files.deleteIfExists(tempFile); + } + + @Test + public void testMain_MissingFileArg() { + int code = Main.run(new String[]{}); + + assertEquals(1, code); + String errorOutput = errContent.toString(); + assertTrue(errorOutput.contains("Error: Missing input file argument.")); + assertTrue(errorOutput.contains("Usage: java Main ")); + } + + @Test + public void testMain_NonexistentFile() { + int code = Main.run(new String[]{"nonexistent-file.txt"}); + + assertEquals(1, code); + String errorOutput = errContent.toString(); + assertTrue(errorOutput.contains("File does not exist")); + } + + @Test + public void testMain_withMalformedInputFile() throws IOException { + Path tempFile = Files.createTempFile("malformed-input", ".txt"); + Files.writeString(tempFile, + "invalid pelouse line\n" + ); + + int code = Main.run(new String[]{tempFile.toString()}); + + assertEquals(1, code); + String errorOutput = errContent.toString(); + assertTrue(errorOutput.contains("Invalid Pelouse size format")); + + Files.deleteIfExists(tempFile); + } +} \ No newline at end of file diff --git a/src/main/java/com/qrouland/katatondeuse/Main.java b/src/main/java/com/qrouland/katatondeuse/Main.java new file mode 100644 index 0000000..c3d022c --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/Main.java @@ -0,0 +1,34 @@ +package com.qrouland.katatondeuse; + +import com.qrouland.katatondeuse.controller.TontePelouseController; +import com.qrouland.katatondeuse.parser.FileInputParser; +import com.qrouland.katatondeuse.view.ConsolePelouseView; +import com.qrouland.katatondeuse.view.PelouseView; + +import java.io.File; +import java.io.IOException; + +public class Main { + public static void main(String[] args) { + + int exitCode = run(args); + System.exit(exitCode); + } + + public static int run(String[] args) { + PelouseView view = new ConsolePelouseView(); + try { + File inputFile = FileInputParser.parseInputFile(args); + TontePelouseController controller = new TontePelouseController(inputFile, view); + controller.run(); + } catch (IllegalArgumentException e) { + view.displayError("Error: " + e.getMessage()); + view.displayError("Usage: java Main "); + return 1; + } catch (IOException e) { + view.displayError("Error reading file: " + e.getMessage()); + return 1; + } + return 0; + } +} \ No newline at end of file diff --git a/src/main/java/com/qrouland/katatondeuse/controller/TontePelouseController.java b/src/main/java/com/qrouland/katatondeuse/controller/TontePelouseController.java new file mode 100644 index 0000000..7fa61d0 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/controller/TontePelouseController.java @@ -0,0 +1,72 @@ +package com.qrouland.katatondeuse.controller; + +import com.qrouland.katatondeuse.model.Direction; +import com.qrouland.katatondeuse.model.Pelouse; +import com.qrouland.katatondeuse.model.Tondeuse; +import com.qrouland.katatondeuse.parser.FileInputParser; +import com.qrouland.katatondeuse.view.PelouseView; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import java.io.*; +import java.util.List; + +public class TontePelouseController { + private final File instructionsFile; + private final PelouseView pelouseView; + + public TontePelouseController(File instructionsFile, PelouseView view) { + this.instructionsFile = instructionsFile; + this.pelouseView = view; + } + + /** + * Starts the tonte (mowing) simulation by reading instructions from the input file. + * + * @throws IOException an I/O error occurs. + */ + public void run() throws IOException { + try (BufferedReader reader = new BufferedReader(new FileReader(instructionsFile))) { + Pelouse pelouse = initPelouse(reader); + executeInstructions(reader, pelouse); + } + } + + /** + * Reads and executes the {@link Tondeuse} movement instructions from the reader. + * + * @param reader BufferedReader for reading lines from the instructions file. + * @param pelouse The {@link Pelouse} on which {@link Tondeuse} operate. + * @throws IOException an I/O error occurs. + */ + private void executeInstructions(BufferedReader reader, Pelouse pelouse) throws IOException { + String positionLine; + while ((positionLine = reader.readLine()) != null) { + String instructionsLine = reader.readLine(); + ImmutablePair> instructions = + FileInputParser.parseTondeuseInstructions(positionLine, instructionsLine); + Tondeuse tondeuse = instructions.left; + List directions = instructions.right; + + pelouse.addTondeuse(tondeuse); + pelouseView.displayTondeuseAdd(tondeuse); + + TonteTondeuseController controller = new TonteTondeuseController(tondeuse, pelouse, this.pelouseView); + controller.executeInstructions(directions); + pelouseView.displayPelouseState(pelouse); + } + } + + /** + * Parses the first line of the input to initialize the . + * + * @param reader BufferedReader reading from the instructions file. + * @return The initialized {@link Pelouse}. + * @throws IOException an I/O error occurs. + */ + private Pelouse initPelouse(BufferedReader reader) throws IOException { + String firstLine = reader.readLine(); + Pelouse pelouse = FileInputParser.parsePelouseDefinition(firstLine); + pelouseView.displayPelouseState(pelouse); + return pelouse; + } +} \ No newline at end of file diff --git a/src/main/java/com/qrouland/katatondeuse/controller/TonteTondeuseController.java b/src/main/java/com/qrouland/katatondeuse/controller/TonteTondeuseController.java new file mode 100644 index 0000000..05a0a5a --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/controller/TonteTondeuseController.java @@ -0,0 +1,47 @@ +package com.qrouland.katatondeuse.controller; + + +import com.qrouland.katatondeuse.controller.movement.MovementAhead; +import com.qrouland.katatondeuse.controller.movement.MovementLeft; +import com.qrouland.katatondeuse.controller.movement.MovementStrategy; +import com.qrouland.katatondeuse.controller.movement.MovementRight; +import com.qrouland.katatondeuse.model.*; +import com.qrouland.katatondeuse.view.PelouseView; + +import java.util.List; +import java.util.Map; + +/** + * Controls a {@link Tondeuse} on {@link Pelouse}. + */ +public class TonteTondeuseController { + private final Pelouse pelouse; + private final Tondeuse tondeuse; + private final PelouseView pelouseView; + + private static final Map MOVEMENTS = Map.of( + Direction.D, new MovementRight(), + Direction.G, new MovementLeft(), + Direction.A, new MovementAhead() + ); + + public TonteTondeuseController(Tondeuse tondeuse, Pelouse pelouse, PelouseView view) { + this.pelouse = pelouse; + this.tondeuse = tondeuse; + this.pelouseView = view; + } + + /** + * Executes a list of instructions of movement directions. + * + * @param directions the list of movement directions (D, G, A). + */ + public void executeInstructions(List directions) { + for (Direction direction : directions) { + Position initial_position = tondeuse.getPosition(); + MovementStrategy movement = MOVEMENTS.get(direction); + movement.move(this.pelouse, this.tondeuse); + pelouseView.displayTondeuseMove(initial_position, tondeuse.getPosition(), movement); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementAhead.java b/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementAhead.java new file mode 100644 index 0000000..1378a57 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementAhead.java @@ -0,0 +1,35 @@ +package com.qrouland.katatondeuse.controller.movement; + +import com.qrouland.katatondeuse.model.Pelouse; +import com.qrouland.katatondeuse.model.Orientation; +import com.qrouland.katatondeuse.model.Position; +import com.qrouland.katatondeuse.model.Tondeuse; + +public class MovementAhead implements MovementStrategy { + /** + * Moves the {@link Tondeuse} one step ahead in the direction of its current orientation. + * + * @param pelouse The {@link Pelouse} on which the {@link Tondeuse} operates. + * @param tondeuse The {@link Tondeuse} to be moved. + */ + @Override + public void move(Pelouse pelouse, Tondeuse tondeuse) { + Position current = tondeuse.getPosition(); + int x = current.getX(); + int y = current.getY(); + Orientation orientation = current.getOrientation(); + + switch (orientation) { + case N -> y += 1; + case S -> y -= 1; + case E -> x += 1; + case W -> x -= 1; + } + + if (x >= 0 && x <= pelouse.getMaxX() && y >= 0 && y <= pelouse.getMaxY()) { + tondeuse.setPosition(new Position(x, y, orientation)); + } else { + System.err.println("Move ignored: out of grid bounds."); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementLeft.java b/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementLeft.java new file mode 100644 index 0000000..95a8134 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementLeft.java @@ -0,0 +1,28 @@ +package com.qrouland.katatondeuse.controller.movement; + + +import com.qrouland.katatondeuse.model.Pelouse; +import com.qrouland.katatondeuse.model.Orientation; +import com.qrouland.katatondeuse.model.Position; +import com.qrouland.katatondeuse.model.Tondeuse; + +public class MovementLeft implements MovementStrategy { + /** + * Rotates the {@link Tondeuse} 90 degrees to the left. + * + * @param pelouse The {@link Pelouse} on which the {@link Tondeuse} operates. + * @param tondeuse The {@link Tondeuse} to be moved. + */ + @Override + public void move(Pelouse pelouse, Tondeuse tondeuse) { + Position position = tondeuse.getPosition(); + Orientation new_orientation = switch (position.getOrientation()) { + case N -> Orientation.W; + case W -> Orientation.S; + case S -> Orientation.E; + case E -> Orientation.N; + }; + + tondeuse.setPosition(new Position(position.getX(), position.getY(), new_orientation)); + } +} \ No newline at end of file diff --git a/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementRight.java b/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementRight.java new file mode 100644 index 0000000..538ed22 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementRight.java @@ -0,0 +1,26 @@ +package com.qrouland.katatondeuse.controller.movement; + +import com.qrouland.katatondeuse.model.Pelouse; +import com.qrouland.katatondeuse.model.Orientation; +import com.qrouland.katatondeuse.model.Position; +import com.qrouland.katatondeuse.model.Tondeuse; + +public class MovementRight implements MovementStrategy { + /** + * Rotates the {@link Tondeuse} 90 degrees to the right. + * + * @param pelouse The {@link Pelouse} on which the {@link Tondeuse} operates. + * @param tondeuse The {@link Tondeuse} to be moved. + */ + @Override + public void move(Pelouse pelouse, Tondeuse tondeuse) { + Position position = tondeuse.getPosition(); + Orientation new_orientation = switch (position.getOrientation()) { + case N -> Orientation.E; + case E -> Orientation.S; + case S -> Orientation.W; + case W -> Orientation.N; + }; + tondeuse.setPosition(new Position(position.getX(), position.getY(), new_orientation)); + } +} \ No newline at end of file diff --git a/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementStrategy.java b/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementStrategy.java new file mode 100644 index 0000000..bcf6b80 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/controller/movement/MovementStrategy.java @@ -0,0 +1,14 @@ +package com.qrouland.katatondeuse.controller.movement; + +import com.qrouland.katatondeuse.model.Pelouse; +import com.qrouland.katatondeuse.model.Tondeuse; + +public interface MovementStrategy { + /** + * Executes a movement of a {@link Tondeuse} on the {@link Pelouse}. + * + * @param pelouse The {@link Pelouse} on which the {@link Tondeuse} operates. + * @param tondeuse The {@link Tondeuse} to be moved. + */ + void move(Pelouse pelouse, Tondeuse tondeuse); +} diff --git a/src/main/java/com/qrouland/katatondeuse/model/Direction.java b/src/main/java/com/qrouland/katatondeuse/model/Direction.java new file mode 100644 index 0000000..f5d48d9 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/model/Direction.java @@ -0,0 +1,20 @@ +package com.qrouland.katatondeuse.model; + + +/** + * Three directions for moving a {@link Tondeuse} using a {@link com.qrouland.katatondeuse.controller.movement.MovementStrategy} . + */ +public enum Direction { + D, // Droite (droite) + G, // Gauche (left) + A; // Avance (ahead) + + public static Direction fromChar(char c) { + return switch (c) { + case 'A' -> A; + case 'G' -> G; + case 'D' -> D; + default -> throw new IllegalArgumentException("Invalid direction character: " + c); + }; + } +} diff --git a/src/main/java/com/qrouland/katatondeuse/model/Orientation.java b/src/main/java/com/qrouland/katatondeuse/model/Orientation.java new file mode 100644 index 0000000..6de2bf3 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/model/Orientation.java @@ -0,0 +1,22 @@ +package com.qrouland.katatondeuse.model; + + +/** + * Four cardinal directions for orientations of the {@link Tondeuse}. + */ +public enum Orientation { + N, // North + E, // East + W, // West + S; // South + + public static Orientation fromChar(char c) { + return switch (c) { + case 'N' -> N; + case 'E' -> E; + case 'S' -> S; + case 'W' -> W; + default -> throw new IllegalArgumentException("Invalid orientation character: " + c); + }; + } +} diff --git a/src/main/java/com/qrouland/katatondeuse/model/Pelouse.java b/src/main/java/com/qrouland/katatondeuse/model/Pelouse.java new file mode 100644 index 0000000..4d561f2 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/model/Pelouse.java @@ -0,0 +1,47 @@ +package com.qrouland.katatondeuse.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents a pelouse (lawn) on which {@link Tondeuse} (mowers) operate. + */ +public class Pelouse { + private final int maxX; + private final int maxY; + + private final List tondeuses; + + /** + * Constructs a Pelouse. + * + * @param maxX the max x coordinate. + * @param maxY the max x coordinate. + */ + public Pelouse(int maxX, int maxY) { + this.maxX = maxX; + this.maxY = maxY; + this.tondeuses = new ArrayList<>(); + } + + public int getMaxX() { + return maxX; + } + + public int getMaxY() { + return maxY; + } + + public List getTondeuses() { + return tondeuses; + } + + public void addTondeuse(Tondeuse tondeuse) { + this.tondeuses.add(tondeuse); + } + + @Override + public String toString() { + return String.format("Pelouse{maxX=%d, maxY=%d, tondeuses=%s}", maxX, maxY, tondeuses); + } +} diff --git a/src/main/java/com/qrouland/katatondeuse/model/Position.java b/src/main/java/com/qrouland/katatondeuse/model/Position.java new file mode 100644 index 0000000..5993265 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/model/Position.java @@ -0,0 +1,41 @@ +package com.qrouland.katatondeuse.model; + +/** + * Represents the position of a {@link Tondeuse} on a 2D grid (x,y) and its + * orientation (N, E, S, W). + */ +public class Position { + private final int x; + private final int y; + private final Orientation orientation; + + /** + * Constructs a Position. + * + * @param x the x coordinate. + * @param y the y coordinate. + * @param orientation the orientation (N, E, S, W). + */ + public Position(int x, int y, Orientation orientation) { + this.x = x; + this.y = y; + this.orientation = orientation; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public Orientation getOrientation() { + return orientation; + } + + @Override + public String toString() { + return String.format("%d %d %s", x, y, orientation.toString()); + } +} diff --git a/src/main/java/com/qrouland/katatondeuse/model/Tondeuse.java b/src/main/java/com/qrouland/katatondeuse/model/Tondeuse.java new file mode 100644 index 0000000..cce7d01 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/model/Tondeuse.java @@ -0,0 +1,30 @@ +package com.qrouland.katatondeuse.model; + +/** + * Represents a Tondeuse (mower) with a current {@link Position} on a 2D grid. + */ +public class Tondeuse { + private Position position; + + /** + * Constructs a Tondeuse at the given position. + * + * @param position the initial position of the Tondeuse. + */ + public Tondeuse(Position position) { + this.position = position; + } + + public Position getPosition() { + return position; + } + + public void setPosition(Position position) { + this.position = position; + } + + @Override + public String toString() { + return String.format("Tondeuse { position = %s }", position.toString()); + } +} diff --git a/src/main/java/com/qrouland/katatondeuse/parser/FileInputParser.java b/src/main/java/com/qrouland/katatondeuse/parser/FileInputParser.java new file mode 100644 index 0000000..a171a2a --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/parser/FileInputParser.java @@ -0,0 +1,82 @@ +package com.qrouland.katatondeuse.parser; + +import com.qrouland.katatondeuse.model.*; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class FileInputParser { + /** + * Parse instructions for a {@link Tondeuse} initial position and its movement instructions. + * + * @param positionLine string representing the {@link Tondeuse} initial position. + * @param instructionsLine string representing movement instructions. + * @return An ImmutablePair of {@link Tondeuse} and {@link Direction}. + */ + public static ImmutablePair> parseTondeuseInstructions( + String positionLine, String instructionsLine) { + if (!positionLine.matches("^\\d+ \\d+ [NESW]$")) { + throw new IllegalArgumentException("Invalid tondeuse position format : " + positionLine); + } + + if (!instructionsLine.matches("^[AGD]+$")) { + throw new IllegalArgumentException("Invalid tondeuse instruction format : " + instructionsLine); + } + + String[] parts = positionLine.split(" "); + int x = Integer.parseInt(parts[0]); + int y = Integer.parseInt(parts[1]); + Orientation orientation = Orientation.fromChar(parts[2].charAt(0)); + + List directions = new ArrayList<>(); + for (char c : instructionsLine.toCharArray()) { + directions.add(Direction.fromChar(c)); + } + + Tondeuse tondeuse = new Tondeuse(new Position(x, y, orientation)); + return new ImmutablePair<>(tondeuse, directions); + } + + /** + * Parse a line to instantiate a {@link Pelouse}. + * + * @param line The line of the pelouse definition to parse. + * @return a Pelouse of the correct format. + */ + public static Pelouse parsePelouseDefinition(String line) { + String pattern = "^\\d+ \\d+$"; + + if (!line.matches(pattern)) { + throw new IllegalArgumentException("Invalid Pelouse size format: " + line); + } + + String[] parts = line.split(" "); + int xMax = Integer.parseInt(parts[0]); + int yMax = Integer.parseInt(parts[1]); + + return new Pelouse(xMax, yMax); + } + + /** + * Parses the input arguments to retrieve the input file. + * + * @param args cli args. + * @return a File object representing the input file. + * @throws IllegalArgumentException if no arguments provided or the file does not exist. + */ + public static File parseInputFile(String[] args) { + if (args.length < 1) { + throw new IllegalArgumentException("Missing input file argument."); + } + + File inputFile = new File(args[0]); + + if (!inputFile.exists()) { + throw new IllegalArgumentException("File does not exist: " + args[0]); + } + + return inputFile; + } +} diff --git a/src/main/java/com/qrouland/katatondeuse/view/ConsolePelouseView.java b/src/main/java/com/qrouland/katatondeuse/view/ConsolePelouseView.java new file mode 100644 index 0000000..0aa2f28 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/view/ConsolePelouseView.java @@ -0,0 +1,58 @@ +package com.qrouland.katatondeuse.view; + +import com.qrouland.katatondeuse.controller.movement.MovementStrategy; +import com.qrouland.katatondeuse.model.Pelouse; +import com.qrouland.katatondeuse.model.Position; +import com.qrouland.katatondeuse.model.Tondeuse; + +public class ConsolePelouseView implements PelouseView { + /** + * Displays the current state of the {@link Pelouse}, including the positions of all {@link Tondeuse}. + * + * @param pelouse The {@link Pelouse} to be displayed. + */ + @Override + public void displayPelouseState(Pelouse pelouse) { + System.out.printf("Pelouse %d x %d : ", pelouse.getMaxX(), pelouse.getMaxY()); + for (int i = 0; i < pelouse.getTondeuses().size(); i++) { + Tondeuse tondeuse = pelouse.getTondeuses().get(i); + System.out.print(tondeuse.getPosition()); + if (i < pelouse.getTondeuses().size() - 1) { + System.out.print(" | "); + } + } + System.out.println(); + } + + /** + * Displays a message indicating a new {@link Tondeuse} has been added. + * + * @param tondeuse The {@link Tondeuse} that was added. + */ + @Override + public void displayTondeuseAdd(Tondeuse tondeuse) { + System.out.printf("Added : %s%n", tondeuse.getPosition()); + } + + /** + * Displays a message indicating a {@link Tondeuse} has moved from its origin to its destination. + * + * @param origin The original position of the {@link Tondeuse}. + * @param destination The new position of the {@link Tondeuse}. + * @param movement The {@link MovementStrategy} used for the movement. + */ + @Override + public void displayTondeuseMove(Position origin, Position destination, MovementStrategy movement) { + System.out.printf("Moved : %s to %s using %s%n", origin, destination, movement.getClass().getSimpleName()); + } + + /** + * Displays an error message. + * + * @param message The error message. + */ + @Override + public void displayError(String message) { + System.err.println(message); + } +} diff --git a/src/main/java/com/qrouland/katatondeuse/view/PelouseView.java b/src/main/java/com/qrouland/katatondeuse/view/PelouseView.java new file mode 100644 index 0000000..be95709 --- /dev/null +++ b/src/main/java/com/qrouland/katatondeuse/view/PelouseView.java @@ -0,0 +1,39 @@ +package com.qrouland.katatondeuse.view; + +import com.qrouland.katatondeuse.controller.movement.MovementStrategy; +import com.qrouland.katatondeuse.model.Pelouse; +import com.qrouland.katatondeuse.model.Position; +import com.qrouland.katatondeuse.model.Tondeuse; + +public interface PelouseView { + /** + * Displays the current state of the {@link Pelouse}, including the positions of all {@link Tondeuse}. + * + * @param pelouse The {@link Pelouse} to be displayed. + */ + void displayPelouseState(Pelouse pelouse); + + /** + * Displays a message indicating a new {@link Tondeuse} has been added. + * + * @param tondeuse The {@link Tondeuse} that was added. + */ + void displayTondeuseAdd(Tondeuse tondeuse); + + /** + * Displays a message indicating a {@link Tondeuse} has moved from its origin {@link Position} to its destination {@link Position}. + * + * @param origin The original {@link Position} of the {@link Tondeuse}. + * @param destination The new {@link Position} of the {@link Tondeuse}. + * @param movement The {@link MovementStrategy} used for the movement. + */ + void displayTondeuseMove(Position origin, Position destination, MovementStrategy movement); + + + /** + * Displays an error message. + * + * @param message The error message. + */ + void displayError(String message); +} diff --git a/src/test/java/com/qrouland/katatondeuse/controller/TontePelouseControllerTest.java b/src/test/java/com/qrouland/katatondeuse/controller/TontePelouseControllerTest.java new file mode 100644 index 0000000..2fa6010 --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/controller/TontePelouseControllerTest.java @@ -0,0 +1,62 @@ +package com.qrouland.katatondeuse.controller; + +import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; + +import com.qrouland.katatondeuse.model.Pelouse; +import com.qrouland.katatondeuse.model.Tondeuse; +import com.qrouland.katatondeuse.view.PelouseView; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +import java.io.*; +import java.util.List; + +public class TontePelouseControllerTest { + + private PelouseView pelouseViewMock; + + @BeforeEach + public void setup() { + pelouseViewMock = mock(PelouseView.class); + } + + @Test + public void testRun() throws IOException { + String fileContent = """ + 5 5 + 1 2 N + GAGAGAGAA + 3 3 E + AADAADADDA + """; + + File tempFile = File.createTempFile("testInstructions", ".txt"); + tempFile.deleteOnExit(); + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))) { + writer.write(fileContent); + } + + TontePelouseController controller = new TontePelouseController(tempFile, pelouseViewMock); + controller.run(); + + verify(pelouseViewMock, atLeastOnce()).displayPelouseState(any(Pelouse.class)); + verify(pelouseViewMock, atLeastOnce()).displayTondeuseAdd(any(Tondeuse.class)); + + ArgumentCaptor tondeuseCaptor = ArgumentCaptor.forClass(Tondeuse.class); + verify(pelouseViewMock, atLeastOnce()).displayTondeuseAdd(tondeuseCaptor.capture()); + + List addedTondeuses = tondeuseCaptor.getAllValues(); + assertEquals(2, addedTondeuses.size()); + + assertEquals(1, addedTondeuses.get(0).getPosition().getX()); + assertEquals(3, addedTondeuses.get(0).getPosition().getY()); + assertEquals(com.qrouland.katatondeuse.model.Orientation.N, addedTondeuses.get(0).getPosition().getOrientation()); + + assertEquals(5, addedTondeuses.get(1).getPosition().getX()); + assertEquals(1, addedTondeuses.get(1).getPosition().getY()); + assertEquals(com.qrouland.katatondeuse.model.Orientation.E, addedTondeuses.get(1).getPosition().getOrientation()); + } +} diff --git a/src/test/java/com/qrouland/katatondeuse/controller/TonteTondeuseControllerTest.java b/src/test/java/com/qrouland/katatondeuse/controller/TonteTondeuseControllerTest.java new file mode 100644 index 0000000..df3944a --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/controller/TonteTondeuseControllerTest.java @@ -0,0 +1,41 @@ +package com.qrouland.katatondeuse.controller; + +import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; + +import com.qrouland.katatondeuse.controller.movement.MovementStrategy; +import com.qrouland.katatondeuse.model.*; +import com.qrouland.katatondeuse.view.PelouseView; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class TonteTondeuseControllerTest { + + private Pelouse pelouse; + private Tondeuse tondeuse; + private PelouseView pelouseView; + private TonteTondeuseController controller; + + @BeforeEach + public void setup() { + pelouse = new Pelouse(5, 5); + Position initialPosition = new Position(1, 2, Orientation.N); + tondeuse = new Tondeuse(initialPosition); + pelouseView = mock(PelouseView.class); + controller = new TonteTondeuseController(tondeuse, pelouse, pelouseView); + } + + @Test + public void testExecuteInstructions() { + List instructions = List.of(Direction.A, Direction.D, Direction.G); + + controller.executeInstructions(instructions); + + verify(pelouseView, times(3)) + .displayTondeuseMove(any(Position.class), any(Position.class), any(MovementStrategy.class)); + Position finalPosition = tondeuse.getPosition(); + assertEquals("1 3 N", finalPosition.toString()); + } +} diff --git a/src/test/java/com/qrouland/katatondeuse/controller/movement/MovementAheadTest.java b/src/test/java/com/qrouland/katatondeuse/controller/movement/MovementAheadTest.java new file mode 100644 index 0000000..4cc57fa --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/controller/movement/MovementAheadTest.java @@ -0,0 +1,103 @@ + +package com.qrouland.katatondeuse.controller.movement; + +import com.qrouland.katatondeuse.model.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class MovementAheadTest { + private MovementAhead movementAhead; + private Pelouse pelouse; + + @BeforeEach + void setUp() { + movementAhead = new MovementAhead(); + pelouse = new Pelouse(5, 5); + } + + @Test + void moveNorth() { + Tondeuse tondeuse = new Tondeuse(new Position(2, 2, Orientation.N)); + movementAhead.move(pelouse, tondeuse); + Position pos = tondeuse.getPosition(); + assertEquals(2, pos.getX()); + assertEquals(3, pos.getY()); + assertEquals(Orientation.N, pos.getOrientation()); + } + + @Test + void moveSouth() { + Tondeuse tondeuse = new Tondeuse(new Position(2, 2, Orientation.S)); + movementAhead.move(pelouse, tondeuse); + Position pos = tondeuse.getPosition(); + assertEquals(2, pos.getX()); + assertEquals(1, pos.getY()); + assertEquals(Orientation.S, pos.getOrientation()); + } + + @Test + void moveEast() { + Tondeuse tondeuse = new Tondeuse(new Position(2, 2, Orientation.E)); + movementAhead.move(pelouse, tondeuse); + Position pos = tondeuse.getPosition(); + assertEquals(3, pos.getX()); + assertEquals(2, pos.getY()); + assertEquals(Orientation.E, pos.getOrientation()); + } + + @Test + void moveWest() { + Tondeuse tondeuse = new Tondeuse(new Position(2, 2, Orientation.W)); + movementAhead.move(pelouse, tondeuse); + Position pos = tondeuse.getPosition(); + assertEquals(1, pos.getX()); + assertEquals(2, pos.getY()); + assertEquals(Orientation.W, pos.getOrientation()); + } + + @Test + void move_OutOfBounds_MaxY() { + Tondeuse tondeuse = new Tondeuse(new Position(3, pelouse.getMaxY(), Orientation.N)); + movementAhead.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(3, pos.getX()); + assertEquals(pelouse.getMaxY(), pos.getY()); + assertEquals(Orientation.N, pos.getOrientation()); + } + + @Test + void move_OutOfBounds_MaxX() { + Tondeuse tondeuse = new Tondeuse(new Position(pelouse.getMaxX(), 3, Orientation.E)); + movementAhead.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(pelouse.getMaxX(), pos.getX()); + assertEquals(3, pos.getY()); + assertEquals(Orientation.E, pos.getOrientation()); + } + + @Test + void move_OutOfBounds_MinY() { + Tondeuse tondeuse = new Tondeuse(new Position(3, 0, Orientation.S)); + movementAhead.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(3, pos.getX()); + assertEquals(0, pos.getY()); + assertEquals(Orientation.S, pos.getOrientation()); + } + + @Test + void mov_OutOfBounds_MinX() { + Tondeuse tondeuse = new Tondeuse(new Position(0, 3, Orientation.W)); + movementAhead.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(0, pos.getX()); + assertEquals(3, pos.getY()); + assertEquals(Orientation.W, pos.getOrientation()); + } +} diff --git a/src/test/java/com/qrouland/katatondeuse/controller/movement/MovementLeftTest.java b/src/test/java/com/qrouland/katatondeuse/controller/movement/MovementLeftTest.java new file mode 100644 index 0000000..5386af7 --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/controller/movement/MovementLeftTest.java @@ -0,0 +1,62 @@ +package com.qrouland.katatondeuse.controller.movement; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.qrouland.katatondeuse.model.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class MovementLeftTest { + private MovementLeft movementLeft; + private Pelouse pelouse; + + @BeforeEach + void setUp() { + movementLeft = new MovementLeft(); + pelouse = new Pelouse(5, 5); + } + + @Test + void testTurnLeftFromNorth() { + Tondeuse tondeuse = new Tondeuse(new Position(2, 3, Orientation.N)); + movementLeft.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(2, pos.getX()); + assertEquals(3, pos.getY()); + assertEquals(Orientation.W, pos.getOrientation()); + } + + @Test + void testTurnLeftFromWest() { + Tondeuse tondeuse = new Tondeuse(new Position(2, 3, Orientation.W)); + movementLeft.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(2, pos.getX()); + assertEquals(3, pos.getY()); + assertEquals(Orientation.S, pos.getOrientation()); + } + + @Test + void testTurnLeftFromSouth() { + Tondeuse tondeuse = new Tondeuse(new Position(2, 3, Orientation.S)); + movementLeft.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(2, pos.getX()); + assertEquals(3, pos.getY()); + assertEquals(Orientation.E, pos.getOrientation()); + } + + @Test + void testTurnLeftFromEast() { + Tondeuse tondeuse = new Tondeuse(new Position(2, 3, Orientation.E)); + movementLeft.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(2, pos.getX()); + assertEquals(3, pos.getY()); + assertEquals(Orientation.N, pos.getOrientation()); + } +} diff --git a/src/test/java/com/qrouland/katatondeuse/controller/movement/MovementRightTest.java b/src/test/java/com/qrouland/katatondeuse/controller/movement/MovementRightTest.java new file mode 100644 index 0000000..e7be87d --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/controller/movement/MovementRightTest.java @@ -0,0 +1,62 @@ +package com.qrouland.katatondeuse.controller.movement; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.qrouland.katatondeuse.model.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class MovementRightTest { + private MovementRight movementRight; + private Pelouse pelouse; + + @BeforeEach + void setUp() { + movementRight = new MovementRight(); + pelouse = new Pelouse(5, 5); + } + + @Test + void testTurnRightFromNorth() { + Tondeuse tondeuse = new Tondeuse(new Position(1, 1, Orientation.N)); + movementRight.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(1, pos.getX()); + assertEquals(1, pos.getY()); + assertEquals(Orientation.E, pos.getOrientation()); + } + + @Test + void testTurnRightFromEast() { + Tondeuse tondeuse = new Tondeuse(new Position(1, 1, Orientation.E)); + movementRight.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(1, pos.getX()); + assertEquals(1, pos.getY()); + assertEquals(Orientation.S, pos.getOrientation()); + } + + @Test + void testTurnRightFromSouth() { + Tondeuse tondeuse = new Tondeuse(new Position(1, 1, Orientation.S)); + movementRight.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(1, pos.getX()); + assertEquals(1, pos.getY()); + assertEquals(Orientation.W, pos.getOrientation()); + } + + @Test + void testTurnRightFromWest() { + Tondeuse tondeuse = new Tondeuse(new Position(1, 1, Orientation.W)); + movementRight.move(pelouse, tondeuse); + + Position pos = tondeuse.getPosition(); + assertEquals(1, pos.getX()); + assertEquals(1, pos.getY()); + assertEquals(Orientation.N, pos.getOrientation()); + } +} diff --git a/src/test/java/com/qrouland/katatondeuse/model/DirectionTest.java b/src/test/java/com/qrouland/katatondeuse/model/DirectionTest.java new file mode 100644 index 0000000..0449ef2 --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/model/DirectionTest.java @@ -0,0 +1,28 @@ +package com.qrouland.katatondeuse.model; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DirectionTest { + + @Test + void testFromChar() { + assertEquals(Direction.A, Direction.fromChar('A')); + assertEquals(Direction.G, Direction.fromChar('G')); + assertEquals(Direction.D, Direction.fromChar('D')); + } + + @Test + void testFromChar_invalidCharacter() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> Direction.fromChar('X')); + assertEquals("Invalid direction character: X", exception.getMessage()); + } + + @Test + void testFromChar_lowercaseInput() { + assertThrows(IllegalArgumentException.class, () -> Direction.fromChar('a')); + assertThrows(IllegalArgumentException.class, () -> Direction.fromChar('g')); + assertThrows(IllegalArgumentException.class, () -> Direction.fromChar('d')); + } +} diff --git a/src/test/java/com/qrouland/katatondeuse/model/OrientationTest.java b/src/test/java/com/qrouland/katatondeuse/model/OrientationTest.java new file mode 100644 index 0000000..0fa6e7c --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/model/OrientationTest.java @@ -0,0 +1,27 @@ +package com.qrouland.katatondeuse.model; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class OrientationTest { + + @Test + void testFromChar() { + assertEquals(Orientation.N, Orientation.fromChar('N')); + assertEquals(Orientation.E, Orientation.fromChar('E')); + assertEquals(Orientation.S, Orientation.fromChar('S')); + assertEquals(Orientation.W, Orientation.fromChar('W')); + } + + @Test + void testFromChar_invalidCharacter() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> Orientation.fromChar('X')); + assertEquals("Invalid orientation character: X", exception.getMessage()); + } + + @Test + void testFromChar_lowercaseInput() { + assertThrows(IllegalArgumentException.class, () -> Orientation.fromChar('n')); + } +} \ No newline at end of file diff --git a/src/test/java/com/qrouland/katatondeuse/model/PelouseTest.java b/src/test/java/com/qrouland/katatondeuse/model/PelouseTest.java new file mode 100644 index 0000000..a8522e2 --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/model/PelouseTest.java @@ -0,0 +1,60 @@ +package com.qrouland.katatondeuse.model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class PelouseTest { + + private Pelouse pelouse; + + @BeforeEach + void setUp() { + pelouse = new Pelouse(5, 5); + } + + @Test + void testAddTondeuseAndGetTondeuses() { + Tondeuse tondeuse = new Tondeuse(new Position(1, 2, Orientation.N)); + + pelouse.addTondeuse(tondeuse); + List tondeuses = pelouse.getTondeuses(); + + assertEquals(1, tondeuses.size()); + assertSame(tondeuse, tondeuses.get(0)); + } + + @Test + void testGetTondeuses() { + assertTrue(pelouse.getTondeuses().isEmpty()); + } + + @Test + void testGetMaxX_returnsCorrectValue() { + Pelouse pelouse = new Pelouse(10, 7); + + assertEquals(10, pelouse.getMaxX()); + } + + @Test + void testGetMaxY_returnsCorrectValue() { + Pelouse pelouse = new Pelouse(10, 7); + + assertEquals(7, pelouse.getMaxY()); + } + + @Test + void testToString() { + Tondeuse tondeuse = new Tondeuse(new Position(3, 4, Orientation.E)); + pelouse.addTondeuse(tondeuse); + + String result = pelouse.toString(); + + assertTrue(result.contains("maxX=5")); + assertTrue(result.contains("maxY=5")); + assertTrue(result.contains("Tondeuse { position =")); + } +} diff --git a/src/test/java/com/qrouland/katatondeuse/model/PositionTest.java b/src/test/java/com/qrouland/katatondeuse/model/PositionTest.java new file mode 100644 index 0000000..3860727 --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/model/PositionTest.java @@ -0,0 +1,14 @@ +package com.qrouland.katatondeuse.model; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class PositionTest { + + @Test + void testPositionToString() { + Position position = new Position(0, 0, Orientation.S); + String expected = "0 0 S"; + assertEquals(expected, position.toString()); + } +} diff --git a/src/test/java/com/qrouland/katatondeuse/model/TondeuseTest.java b/src/test/java/com/qrouland/katatondeuse/model/TondeuseTest.java new file mode 100644 index 0000000..60dc154 --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/model/TondeuseTest.java @@ -0,0 +1,41 @@ +package com.qrouland.katatondeuse.model; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class TondeuseTest { + + @Test + void testPositionGetter() { + Position position = new Position(1, 2, Orientation.N); + Tondeuse tondeuse = new Tondeuse(position); + + assertEquals(position, tondeuse.getPosition()); + } + + @Test + void testSetPosition() { + + Position initialPosition = new Position(1, 2, Orientation.N); + Tondeuse tondeuse = new Tondeuse(initialPosition); + + Position newPosition = new Position(3, 4, Orientation.E); + tondeuse.setPosition(newPosition); + + assertEquals(newPosition, tondeuse.getPosition()); + assertEquals(3, tondeuse.getPosition().getX()); + assertEquals(4, tondeuse.getPosition().getY()); + assertEquals(Orientation.E, tondeuse.getPosition().getOrientation()); + } + + @Test + void testTondeuseToString() { + Position position = new Position(3, 4, Orientation.E); + Tondeuse tondeuse = new Tondeuse(position); + + String expected = "Tondeuse { position = 3 4 E }"; + assertEquals(expected, tondeuse.toString()); + } + + +} diff --git a/src/test/java/com/qrouland/katatondeuse/parser/FileInputParserTest.java b/src/test/java/com/qrouland/katatondeuse/parser/FileInputParserTest.java new file mode 100644 index 0000000..98dccef --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/parser/FileInputParserTest.java @@ -0,0 +1,107 @@ +package com.qrouland.katatondeuse; + + +import com.qrouland.katatondeuse.model.Direction; +import com.qrouland.katatondeuse.model.Orientation; +import com.qrouland.katatondeuse.model.Pelouse; +import com.qrouland.katatondeuse.model.Tondeuse; +import com.qrouland.katatondeuse.parser.FileInputParser; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class FileInputParserTest { + + @Test + void testParseTondeuseInstructions_validInput() { + String position = "1 2 N"; + String instructions = "AGAD"; + + ImmutablePair> result = FileInputParser.parseTondeuseInstructions(position, instructions); + + assertNotNull(result); + assertEquals(1, result.left.getPosition().getX()); + assertEquals(2, result.left.getPosition().getY()); + assertEquals(Orientation.N, result.left.getPosition().getOrientation()); + + assertEquals(List.of(Direction.A, Direction.G, Direction.A, Direction.D), result.right); + } + + @Test + void testParseTondeuseInstructions_invalidPositionFormat() { + String invalidPosition = "1 N 2"; + String instructions = "AGAD"; + + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> + FileInputParser.parseTondeuseInstructions(invalidPosition, instructions)); + assertEquals("Invalid tondeuse position format : " + invalidPosition, exception.getMessage()); + } + + @Test + void testParseTondeuseInstructions_invalidInstructionsFormat() { + String position = "1 2 N"; + String invalidInstructions = "AGZRA"; + + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> + FileInputParser.parseTondeuseInstructions(position, invalidInstructions)); + + assertEquals("Invalid tondeuse instruction format : " + invalidInstructions, exception.getMessage()); + } + + + @Test + void testParsePelouseDefinition_ValidInput() { + Pelouse pelouse = FileInputParser.parsePelouseDefinition("5 5"); + + assertNotNull(pelouse); + assertEquals(5, pelouse.getMaxX()); + assertEquals(5, pelouse.getMaxY()); + } + + @Test + void testParsePelouseDefinition_InvalidInput() { + String invalidLine = "5x5"; + + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> + FileInputParser.parsePelouseDefinition(invalidLine)); + + assertTrue(exception.getMessage().contains("Invalid Pelouse size format")); + } + + @Test + void testInputParserFile_ValidInputFile() throws IOException { + File tempFile = File.createTempFile("test", ".txt"); + tempFile.deleteOnExit(); + + String[] args = { tempFile.getAbsolutePath() }; + File result = FileInputParser.parseInputFile(args); + + assertNotNull(result); + assertTrue(result.exists()); + assertEquals(tempFile.getAbsolutePath(), result.getAbsolutePath()); + } + + @Test + void testFileInputParser_MissingArgument() { + String[] args = {}; + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + FileInputParser.parseInputFile(args); + }); + assertEquals("Missing input file argument.", exception.getMessage()); + } + + @Test + void testFileInputParser_NonExistentFile() { + String[] args = { "nonexistentfile.txt" }; + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + FileInputParser.parseInputFile(args); + }); + assertEquals("File does not exist: nonexistentfile.txt", exception.getMessage()); + } + +} diff --git a/src/test/java/com/qrouland/katatondeuse/view/ConsolePelouseViewTest.java b/src/test/java/com/qrouland/katatondeuse/view/ConsolePelouseViewTest.java new file mode 100644 index 0000000..6ecc74f --- /dev/null +++ b/src/test/java/com/qrouland/katatondeuse/view/ConsolePelouseViewTest.java @@ -0,0 +1,81 @@ +package com.qrouland.katatondeuse.view; + +import com.qrouland.katatondeuse.controller.movement.MovementAhead; +import com.qrouland.katatondeuse.model.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.jupiter.api.Assertions.*; + +class ConsolePelouseViewTest { + private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + private final PrintStream originalOut = System.out; + private final PrintStream originalErr = System.err; + + private final ConsolePelouseView view = new ConsolePelouseView(); + + @BeforeEach + void setUpStreams() { + System.setOut(new PrintStream(outContent)); + System.setErr(new PrintStream(errContent)); + } + + @AfterEach + void restoreStreams() { + System.setOut(originalOut); + System.setErr(originalErr); + } + + + @Test + void testDisplayPelouseState() { + Pelouse pelouse = new Pelouse(3, 3); + pelouse.addTondeuse(new Tondeuse(new Position(0, 0, Orientation.S))); + pelouse.addTondeuse(new Tondeuse(new Position(2, 2, Orientation.E))); + + view.displayPelouseState(pelouse); + + String output = outContent.toString().trim(); + assertTrue(output.contains("Pelouse 3 x 3")); + assertTrue(output.contains("0 0 S | 2 2 E")); + } + + @Test + void testDisplayTondeuseAdd() { + Tondeuse tondeuse = new Tondeuse(new Position(2, 3, Orientation.E)); + + view.displayTondeuseAdd(tondeuse); + + String output = outContent.toString().trim(); + assertTrue(output.contains("Added")); + assertTrue(output.contains("2 3 E")); + } + + @Test + void testDisplayTondeuseMove() { + Position origin = new Position(1, 1, Orientation.N); + Position destination = new Position(1, 2, Orientation.N); + MovementAhead movement = new MovementAhead(); + + view.displayTondeuseMove(origin, destination, movement); + + String output = outContent.toString().trim(); + assertTrue(output.contains("Moved")); + assertTrue(output.contains("1 1 N")); + assertTrue(output.contains("1 2 N")); + assertTrue(output.contains("MovementAhead")); + } + + @Test + void testDisplayError() { + view.displayError("Test error message"); + + String output = errContent.toString().trim(); + assertEquals("Test error message", output); + } +}