From 0c5c940cb23f620f4f94294afc33f7b21095c34d Mon Sep 17 00:00:00 2001 From: "Junyoung Lee (Stitch)" <48052622+lxxjn0@users.noreply.github.com> Date: Tue, 18 Feb 2020 00:30:27 +0900 Subject: [PATCH] =?UTF-8?q?[=EC=8A=A4=ED=8B=B0=EC=B9=98]=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=EC=B0=A8=20=EA=B2=BD=EC=A3=BC=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=EB=AF=B8=EC=85=98=20=EC=BD=94=EB=93=9C=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=A0=9C=EC=B6=9C=ED=95=A9=EB=8B=88=EB=8B=A4.=20(#93)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat : 문자열 덧셈 계산기에서 null과 공백 문자열을 0으로 처리하는 기능 구현 1. null과 공백 문자열을 0으로 처리하는 기능을 calculate 메소드에 구현. 2. null과 공백 문자열을 0으로 처리하는 테스트 구현. * feat : 문자열 덧셈 계산기에서 콤마와 콜론 구분자로 나누어서 더하는 기능 구현 1. 콤마와 콜론 구분자로 나누어서 더하는 기능을 calculate 메소드에 추가. 2. 콤마와 콜론 구분자로 나누어서 더하는 테스트 구현. * feat : 문자열 덧셈 계산기에서 커스텀 구분자로 나누어서 더하는 기능 구현 1. 커스텀 구분자로 나누어서 더하는 기능을 calculate 메소드에 추가. 2. 커스텀 구분자로 나누어서 더하는 테스트 구현. 3. 리팩토링 진행. * feat : 문자열 덧셈 계산기에서 숫자 이외의 값 또는 음수에 대해 RuntimeException throw 구현 1. 음수에 대해 RuntimeException throw을 calculate 메소드에 추가. 2. 숫자 이외의 값 또는 음수에 대해 RuntimeException throw 테스트 구현. 3. 리팩토링 진행. * docs : README.md에 기능 구현 목록 작성 * feat : 자동차의 이름을 쉼표(,)로 나누는 기능 구현 1. StringUtil 클래스에 자동차의 이름을 쉼표(,)로 나누는 메소드 구현. 2. 자동차의 이름을 쉼표(,)로 나누는 테스트 구현. * feat : 자동차 이름의 길이가 5자를 넘지 않는지 확인하는 기능 구현 1. Name 객체에 자동차 이름의 길이가 5자를 넘지 않는지 확인하는 메소드 구현. 2. 자동차 이름의 길이가 5자를 넘지 않는지 확인하는 테스트 구현. * feat : 이동 횟수 입력에 대한 유효성을 검증하는 기능 구현 1. MovementNumber 객체에 이동 횟수 입력이 숫자인지 검증하는 메소드 구현. 2. 이동 횟수 입력이 양수인지 검증하는 메소드 구현. 3. 이동 횟수 입력에 대한 유효성을 검증하는 테스트 구현. * feat : 0에서 9까지의 Random 값을 생성하는 기능 구현 1. RandomGenerator 클래스에 0에서 9까지의 Random 값을 생성하는 메소드 구현. 2. 0에서 9까지의 Random 값을 생성하는 테스트 구현. 3. package 구조 변경. * feat : Random 값에 따라 전진 여부를 결정하는 기능 구현 1. MovementStrategy 클래스에 Random 값에 따라 전진 여부를 결정하는 메소드 구현. 2. Random 값에 따라 전진 여부를 결정하는 테스트 구현. * refactor : MovementStrategy 클래스 삭제 * feat : Random 값에 따라 전진 여부를 결정하는 기능 구현 1. Car 객체에 Random 값에 따라 전진 여부를 결정하는 메소드 구현. 2. Random 값에 따라 전진 여부를 결정하는 테스트 구현. * docs : README.md의 기능 구현 목록 수정 1. 자동차들의 이름 중복에 대한 추가적인 유효성 검사 기능 목록 작성. 2. 자동차의 관리와 이동에 대한 추가적인 기능 목록 작성. * feat : 자동차가 전진하는 경우 위치를 1 증가시키는 기능 구현 1. Car 객체에 자동차가 전진하는 경우 위치를 1 증가시키는 메소드 구현. 2. 자동차가 전진하는 경우 위치를 1 증가시키는 테스트 구현. 3. 테스트 코드 리팩토링. * refactor : Car 객체의 테스트 코드 수정 1. Car 객체의 isMove 메소드의 범위지정자를 private으로 변경함에 따른 테스트 코드 수정. * feat : 자동차의 중복된 이름이 존재하는지 여부를 확인하는 기능 구현 1. Cars 객체에 자동차의 중복된 이름이 존재하는지 여부를 확인하는 메소드 구현. 2. 자동차의 중복된 이름이 존재하는지 여부를 확인하는 테스트 구현. 3. 테스트 코드 리팩토링. 4. 프로덕션 코드의 예외 처리 메시지 추가. * feat : 자동차의 위치를 문자로 변환하는 기능 구현 1. StringUtil 클래스에 자동차의 위치를 문자로 변환하는 메소드 구현. 2. 자동차의 위치를 문자로 변환하는 테스트 구현. * feat : 우승한 위치의 자동차인지 확인하는 기능 구현 1. Car 객체에 우승한 위치의 자동차인지 확인하는 메소드 구현. 2. 우승한 위치의 자동차인지 확인하는 테스트 구현. * docs : README.md에 기능 구현 목록 수정 * feat : 우승한 자동차들을 반환해주는 기능 구현 1. RacingGame 객체에 우승한 자동차들을 반환해주는 메소드 구현. 2. 우승한 자동차들을 반환해주는 테스트 구현. * refactor : stringCalculator 패키지 추가 및 StringCalculator 명명 수정 1. stringCalculator 패키지를 추가하고 해당 패키지 안에 StringCalculator 이동. * refactor : StringCalculator 테스트 코드 리팩토링 1. ParameterizedTest를 활용하여 테스트 코드 리팩토링. * refactor : racingGame 패키지 명명 수정 * feat : 잘못된 위치를 생성하는지 확인하는 기능 구현 1. Position 객체에 잘못된 위치를 생성하는지 확인하는 메소드 구현. 2. 잘못된 위치를 생성하는지 확인하는 테스트 구현. * refactor : Position 객체를 추가함으로 인한 전체 코드 리팩토링 1. Position 객체를 추가함으로 인한 전체 코드 리팩토링. * feat : 입력받은 자동차의 이름을 Cars 객체로 만드는 CarsFactory 구현 1. 입력받은 자동차의 이름을 Cars 객체로 만드는 CarsFactory 클래스와 메소드 구현. 2. 입력받은 자동차의 이름을 Cars 객체로 만드는 테스트 구현. * feat : 자동차 이름이 null 또는 공백인지 확인하는 기능 구현 1. Name 객체에 자동차 이름이 null 또는 공백인지 확인하는 메소드 구현. 2. 자동차 이름이 null 또는 공백인지 확인하는 테스트 구현. * feat : 우승한 자동차의 이름을 이어붙이는 기능 구현 1. 우승한 자동차의 이름을 이어붙이는 메소드 구현. 2. String 배열을 사용한 부분을 List로 변경. 3. RacingGame 객체를 Result로 명명 수정. 4. 전체적인 리팩토링 진행. * feat : 게임을 진행한 후 결과를 반환하는 기능 구현 1. Result 객체에서 게임을 한 번 진행한 후 결과를 반환하는 메소드 구현. 2. 게임을 한 번 진행한 후 결과를 반환하는 테스트 구현. * refactor : 게임을 진행한 후 결과를 반환하는 기능 수정 1. StringUtil 클래스의 메소드를 이름과 위치 상태를 함께 변환하여 반환하도록 리팩토링 진행. * feat : 입력과 출력을 담당할 view 객체 생성 1. 입력을 담당할 InputView 객체와 출력을 담당할 OutputView 객체를 구현. 2. Result 객체의 package 수정 및 내부 코드 리팩토링. * feat : 프로그램 동작을 위한 추가적인 클래스 구현 1. 프로그램의 동작을 위한 Application 클래스 구현. 2. RacingGame을 담당하는 컨트롤러 구현. * refactor : 테스트 코드의 반복을 제거하기 위한 리팩토링 1. 테스트 코드에서의 반복을 제거하기 위한 Car 객체 생성자 추가. * refactor : 전체 코드 컨벤션 체크 및 리팩토링 진행 * refactor : 역할이 적은 RacingGame 객체를 제거 1. 역할이 너무 적은 RacingGame 객체를 제거 후 Application 클래스 수정. * refactor : 1차 피드백 리팩토링 1. Application 클래스의 attemptMove 메소드 책임 재분배. 2. Cars 객체의 attemptMove 메소드 구현. 3. 불필요한 stream 제거. 4. RandomGenerator seed 값 추가. 5. OutputView에 String.format 적용. 6. Pattern 코드의 Compliant한 예제를 참고하여 수정. * refactor : Position의 캐싱 구현 1. PositionCache 클래스를 통해 Position의 객체 중복을 cache 처리. 2. 그에 따른 모든 Position 생성자 코드가 들어간 코드 수정. * feat : 생성된 랜덤한 수를 감싸는 RandomNo 객체 구현 1. 생성된 랜덤한 수의 유효성을 확인하기 위한 RandomNo 객체 구현. 2. 테스트 코드 일부 수정. * feat : RandomNo의 캐싱 구현 1. RandomNo 클래스를 통해 RandomNo의 객체 중복을 cache 처리. 2. 그에 따른 모든 RandomNo 생성자 코드가 들어간 코드 수정. * refactor : Optional 사용과 결과 출력 부분 수정 1. Optional의 잘못된 사용 수정. 2. 결과 출력을 위한 Result를 Map과 같은 형식으로 저장. * refactor : 2차 피드백 리팩토링 1. cache 클래스 제거. * refactor : 전략 패턴 적용 1. MovableStrategy Interface 구현. 2. MovableStrategy의 프로덕션 코드에서 활용할 RandomMovableStrategy 구현. 3. MovableStrategy의 테스트 코드에서 활용할 PlannedMovableStrategy 구현. * refactor : cars 객체 테스트 코드 수정 --- README.md | 65 ++++++++++++++++- .../woowacourse/racingGame/Application.java | 69 +++++++++++++++++++ .../woowacourse/racingGame/domain/Car.java | 55 +++++++++++++++ .../woowacourse/racingGame/domain/Cars.java | 54 +++++++++++++++ .../racingGame/domain/CarsFactory.java | 14 ++++ .../racingGame/domain/MovableStrategy.java | 5 ++ .../racingGame/domain/MovementNumber.java | 31 +++++++++ .../woowacourse/racingGame/domain/Name.java | 46 +++++++++++++ .../domain/PlannedMovableStrategy.java | 14 ++++ .../racingGame/domain/Position.java | 41 +++++++++++ .../woowacourse/racingGame/domain/Power.java | 33 +++++++++ .../racingGame/domain/PowerGenerator.java | 13 ++++ .../domain/RandomMovableStrategy.java | 10 +++ .../woowacourse/racingGame/domain/Result.java | 46 +++++++++++++ .../racingGame/utils/StringUtil.java | 29 ++++++++ .../racingGame/view/InputView.java | 17 +++++ .../racingGame/view/OutputView.java | 34 +++++++++ .../stringCalculator/StringCalculator.java | 54 +++++++++++++++ src/main/java/empty.txt | 0 .../racingGame/domain/CarTest.java | 58 ++++++++++++++++ .../racingGame/domain/CarsFactoryTest.java | 28 ++++++++ .../racingGame/domain/CarsTest.java | 61 ++++++++++++++++ .../racingGame/domain/MovementNumberTest.java | 25 +++++++ .../racingGame/domain/NameTest.java | 32 +++++++++ .../racingGame/domain/PositionTest.java | 39 +++++++++++ .../racingGame/domain/PowerTest.java | 42 +++++++++++ .../racingGame/domain/ResultTest.java | 52 ++++++++++++++ .../racingGame/utils/StringUtilTest.java | 55 +++++++++++++++ .../StringCalculatorTest.java | 68 ++++++++++++++++++ src/test/java/empty.txt | 0 30 files changed, 1089 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/woowacourse/racingGame/Application.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/Car.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/Cars.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/CarsFactory.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/MovableStrategy.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/MovementNumber.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/Name.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/PlannedMovableStrategy.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/Position.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/Power.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/PowerGenerator.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/RandomMovableStrategy.java create mode 100644 src/main/java/com/woowacourse/racingGame/domain/Result.java create mode 100644 src/main/java/com/woowacourse/racingGame/utils/StringUtil.java create mode 100644 src/main/java/com/woowacourse/racingGame/view/InputView.java create mode 100644 src/main/java/com/woowacourse/racingGame/view/OutputView.java create mode 100644 src/main/java/com/woowacourse/stringCalculator/StringCalculator.java delete mode 100644 src/main/java/empty.txt create mode 100644 src/test/java/com/woowacourse/racingGame/domain/CarTest.java create mode 100644 src/test/java/com/woowacourse/racingGame/domain/CarsFactoryTest.java create mode 100644 src/test/java/com/woowacourse/racingGame/domain/CarsTest.java create mode 100644 src/test/java/com/woowacourse/racingGame/domain/MovementNumberTest.java create mode 100644 src/test/java/com/woowacourse/racingGame/domain/NameTest.java create mode 100644 src/test/java/com/woowacourse/racingGame/domain/PositionTest.java create mode 100644 src/test/java/com/woowacourse/racingGame/domain/PowerTest.java create mode 100644 src/test/java/com/woowacourse/racingGame/domain/ResultTest.java create mode 100644 src/test/java/com/woowacourse/racingGame/utils/StringUtilTest.java create mode 100644 src/test/java/com/woowacourse/stringCalculator/StringCalculatorTest.java delete mode 100644 src/test/java/empty.txt diff --git a/README.md b/README.md index 91744e0bd0..ec13cd0a34 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,68 @@ # java-racingcar + 자동차 경주 게임 미션 저장소 +## 기능 요구사항 + +- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. + +- 각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다. + +- 자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다. + +- 사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. + +- 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 - + 멈춘다. + +- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다. + +## 기능 구현 목록 + +- 자동차의 이름을 한 줄로 입력받는 기능 + + - 입력받은 자동차의 이름을 쉼표(,)로 나누는 기능 + + - 이름이 5자를 넘지 않는지 확인하는 기능 + + - 중복된 이름이 존재하는지 여부를 확인하는 기능 + +- 몇 번의 이동을 할 것인지를 입력받는 기능 + + - 이동 횟수 입력에 대한 유효성을 검증하는 기능 + +- 전진하는 조건을 생성하는 기능 + + - 0에서 9까지의 random 값을 생성하는 기능 + + - random 값이 4 이상일 경우 전진하는 기능 + + - random 값이 3 이하인 경우 멈추는 기능 + +- 주어진 횟수에 따라 자동차를 전진하는 기능 + + - 자동차가 전진하는 경우 위치를 1 증가시키는 기능 + +- 여러대의 자동차를 관리하는 기능 + + - 자동차들을 이동시키는 기능 + + - 매번 이동이 끝난 후 자동차의 이름과 이동상황을 출력하는 기능 + +- 우승자를 출력하는 기능 + + - 자동차가 우승한 위치에 있는지 확인하는 기능 + + - 우승한 자동차(들)을 반환해주는 기능 + + - 우승한 자동차(들)을 출력하는 기능 + +## 기여자 + +- [라흐]() + +- [스티치]() + ## 우아한테크코스 코드리뷰 -* [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) \ No newline at end of file + +- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) diff --git a/src/main/java/com/woowacourse/racingGame/Application.java b/src/main/java/com/woowacourse/racingGame/Application.java new file mode 100644 index 0000000000..66ee65a2b7 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/Application.java @@ -0,0 +1,69 @@ +package com.woowacourse.racingGame; + +import java.util.ArrayList; +import java.util.List; + +import com.woowacourse.racingGame.domain.Cars; +import com.woowacourse.racingGame.domain.CarsFactory; +import com.woowacourse.racingGame.domain.MovementNumber; +import com.woowacourse.racingGame.domain.RandomMovableStrategy; +import com.woowacourse.racingGame.domain.Result; +import com.woowacourse.racingGame.view.InputView; +import com.woowacourse.racingGame.view.OutputView; + +public class Application { + private static InputView inputview; + private static OutputView outputView; + + public static void main(String[] args) { + inputview = new InputView(); + outputView = new OutputView(); + + final Cars cars = generateCars(); + final MovementNumber movementNumber = receiveInputMovementNumber(); + + // NOTE : Log처럼 기록을 출력하는 것이 맞는지 여부 물어보기. + final List results = playRacingGame(cars, movementNumber); + + outputView.printRacingGameResult(results); + outputView.printWinners(getFinalResult(results, movementNumber)); + } + + private static Cars generateCars() { + try { + final String carName = inputview.inputCarName(); + return CarsFactory.generate(carName, new RandomMovableStrategy()); + } catch (IllegalArgumentException e) { + outputView.printExceptionMessage(e.getMessage()); + return generateCars(); + } + } + + private static MovementNumber receiveInputMovementNumber() { + try { + final String movementNumber = inputview.inputMovementNumber(); + return new MovementNumber(movementNumber); + } catch (IllegalArgumentException e) { + outputView.printExceptionMessage(e.getMessage()); + return receiveInputMovementNumber(); + } + } + + private static List playRacingGame(final Cars cars, final MovementNumber movementNumber) { + List results = new ArrayList<>(); + + for (int i = 0; i < movementNumber.getMovementNumber(); i++) { + attemptMove(cars); + results.add(new Result(cars)); + } + return results; + } + + private static void attemptMove(final Cars cars) { + cars.attemptMove(); + } + + private static Result getFinalResult(final List results, final MovementNumber movementNumber) { + return results.get(movementNumber.getMovementNumber() - 1); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Car.java b/src/main/java/com/woowacourse/racingGame/domain/Car.java new file mode 100644 index 0000000000..c0dc4a3bf6 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Car.java @@ -0,0 +1,55 @@ +package com.woowacourse.racingGame.domain; + +import java.util.Objects; + +public class Car { + private final MovableStrategy movableStrategy; + private final Name name; + private Position position; + + public Car(Name name, MovableStrategy movableStrategy) { + this.position = Position.ZERO; + this.name = name; + this.movableStrategy = movableStrategy; + } + + Car(Name name, Position position, MovableStrategy movableStrategy) { + this.position = position; + this.name = name; + this.movableStrategy = movableStrategy; + } + + public void attemptMoveThrough() { + if (movableStrategy.isMovable()) { + position = position.increaseByMovingUnit(); + } + } + + // NOTE : 불변객체의 이점? + public boolean isSamePosition(final int winnerPosition) { + return position.getPosition() == winnerPosition; + } + + public String getName() { + return name.getName(); + } + + public int getPosition() { + return position.getPosition(); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Car car = (Car)o; + return name.equals(car.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Cars.java b/src/main/java/com/woowacourse/racingGame/domain/Cars.java new file mode 100644 index 0000000000..b8712b82fc --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Cars.java @@ -0,0 +1,54 @@ +package com.woowacourse.racingGame.domain; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class Cars { + private final List cars; + + public Cars(final List cars) { + checkDuplicate(cars); + this.cars = cars; + } + + private void checkDuplicate(final List cars) { + if (isDuplicateExist(cars)) { + throw new IllegalArgumentException("중복된 이름이 존재합니다."); + } + } + + private boolean isDuplicateExist(final List cars) { + long distinctCarSize = Arrays.stream(cars.toArray()) + .distinct() + .count(); + return cars.size() != distinctCarSize; + } + + public void attemptMove() { + for (Car car : cars) { + car.attemptMoveThrough(); + } + } + + // NOTE : unmodifiable로 처리해주는 것이 의미를 가지는지 리뷰어님께 여쭤보기. + public List getCars() { + return Collections.unmodifiableList(cars); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Cars cars = (Cars)o; + return Objects.equals(this.cars, cars.cars); + } + + @Override + public int hashCode() { + return Objects.hash(cars); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/CarsFactory.java b/src/main/java/com/woowacourse/racingGame/domain/CarsFactory.java new file mode 100644 index 0000000000..eb2053d314 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/CarsFactory.java @@ -0,0 +1,14 @@ +package com.woowacourse.racingGame.domain; + +import java.util.stream.Collectors; + +import com.woowacourse.racingGame.utils.StringUtil; + +public class CarsFactory { + public static Cars generate(final String inputCarName, MovableStrategy movableStrategy) { + return new Cars(StringUtil.split(inputCarName).stream() + .map(Name::new) + .map(name -> new Car(name, movableStrategy)) + .collect(Collectors.toList())); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/MovableStrategy.java b/src/main/java/com/woowacourse/racingGame/domain/MovableStrategy.java new file mode 100644 index 0000000000..3cf453fd97 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/MovableStrategy.java @@ -0,0 +1,5 @@ +package com.woowacourse.racingGame.domain; + +public interface MovableStrategy { + boolean isMovable(); +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/MovementNumber.java b/src/main/java/com/woowacourse/racingGame/domain/MovementNumber.java new file mode 100644 index 0000000000..831ee46055 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/MovementNumber.java @@ -0,0 +1,31 @@ +package com.woowacourse.racingGame.domain; + +public class MovementNumber { + private static final int ZERO = 0; + + private final int movementNumber; + + public MovementNumber(final String inputMovementNumber) { + final int parsedNumber = checkValid(inputMovementNumber); + checkPositive(parsedNumber); + this.movementNumber = parsedNumber; + } + + private int checkValid(final String number) { + try { + return Integer.parseInt(number); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("입력한 이동 횟수가 숫자가 아닙니다."); + } + } + + private void checkPositive(final int parsedNumber) { + if (parsedNumber <= ZERO) { + throw new IllegalArgumentException("입력한 이동 횟수가 0보다 작은 값입니다."); + } + } + + public int getMovementNumber() { + return this.movementNumber; + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Name.java b/src/main/java/com/woowacourse/racingGame/domain/Name.java new file mode 100644 index 0000000000..05020af940 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Name.java @@ -0,0 +1,46 @@ +package com.woowacourse.racingGame.domain; + +import java.util.Objects; + +public class Name { + private static final int MAX_NAME_LENGTH_LIMIT = 5; + + private final String name; + + public Name(final String name) { + checkNullOrEmpty(name); + checkValidLength(name); + this.name = name; + } + + private void checkNullOrEmpty(final String name) { + if (Objects.isNull(name) || name.isEmpty()) { + throw new IllegalArgumentException("null 또는 빈 문자열입니다."); + } + } + + private void checkValidLength(final String name) { + if (name.length() > MAX_NAME_LENGTH_LIMIT) { + throw new IllegalArgumentException("이름은 5자이하만 가능합니다."); + } + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + Name name = (Name)o; + return Objects.equals(this.name, name.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/PlannedMovableStrategy.java b/src/main/java/com/woowacourse/racingGame/domain/PlannedMovableStrategy.java new file mode 100644 index 0000000000..4b349c15cf --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/PlannedMovableStrategy.java @@ -0,0 +1,14 @@ +package com.woowacourse.racingGame.domain; + +public class PlannedMovableStrategy implements MovableStrategy { + private Power power; + + public PlannedMovableStrategy(final Power power) { + this.power = power; + } + + @Override + public boolean isMovable() { + return power.isMovable(); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Position.java b/src/main/java/com/woowacourse/racingGame/domain/Position.java new file mode 100644 index 0000000000..1467408d40 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Position.java @@ -0,0 +1,41 @@ +package com.woowacourse.racingGame.domain; + +import java.util.HashMap; +import java.util.Map; + +public class Position { + private static final int INIT_POSITION = 0; + private static final int MOVING_UNIT = 1; + private static final Map cache = new HashMap<>(); + public static final Position ZERO = new Position(INIT_POSITION); + + private final int position; + + public Position(final int position) { + checkValid(position); + this.position = position; + } + + private void checkValid(final int position) { + if (position < INIT_POSITION) { + throw new IllegalArgumentException("위치가 음수가 될 수 없습니다."); + } + } + + public static Position valueOf(final int position) { + if (cache.containsKey(position)) { + return cache.get(position); + } + Position newPosition = new Position(position); + cache.put(position, newPosition); + return newPosition; + } + + public Position increaseByMovingUnit() { + return Position.valueOf(position + MOVING_UNIT); + } + + public int getPosition() { + return position; + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Power.java b/src/main/java/com/woowacourse/racingGame/domain/Power.java new file mode 100644 index 0000000000..a44327bd74 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Power.java @@ -0,0 +1,33 @@ +package com.woowacourse.racingGame.domain; + +import java.util.HashMap; +import java.util.Map; + +public class Power { + private static final Map cache = new HashMap<>(); + private static final int MOVABLE_LOWER_BOUND = 4; + public static final int RANDOM_LOWER_BOUND = 0; + public static final int RANDOM_UPPER_BOUND = 9; + + private final int randomNumber; + + public Power(final int randomNumber) { + if (randomNumber < RANDOM_LOWER_BOUND || randomNumber > RANDOM_UPPER_BOUND) { + throw new IllegalArgumentException("유효하지 않은 랜덤 숫자입니다."); + } + this.randomNumber = randomNumber; + } + + public static Power valueOf(final int randomNumber) { + if (cache.containsKey(randomNumber)) { + return cache.get(randomNumber); + } + Power newPower = new Power(randomNumber); + cache.put(randomNumber, newPower); + return newPower; + } + + public boolean isMovable() { + return randomNumber >= MOVABLE_LOWER_BOUND; + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/PowerGenerator.java b/src/main/java/com/woowacourse/racingGame/domain/PowerGenerator.java new file mode 100644 index 0000000000..77c9a560b6 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/PowerGenerator.java @@ -0,0 +1,13 @@ +package com.woowacourse.racingGame.domain; + +import java.util.Random; + +public class PowerGenerator { + private static final int RANDOM_UPPER_BOUND = 10; + + public static Power generateRandomNumber() { + Random random = new Random(); + random.setSeed(System.nanoTime()); + return Power.valueOf(random.nextInt(RANDOM_UPPER_BOUND)); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/RandomMovableStrategy.java b/src/main/java/com/woowacourse/racingGame/domain/RandomMovableStrategy.java new file mode 100644 index 0000000000..2ad9f2cda0 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/RandomMovableStrategy.java @@ -0,0 +1,10 @@ +package com.woowacourse.racingGame.domain; + +public class RandomMovableStrategy implements MovableStrategy { + + @Override + public boolean isMovable() { + Power power = PowerGenerator.generateRandomNumber(); + return power.isMovable(); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/domain/Result.java b/src/main/java/com/woowacourse/racingGame/domain/Result.java new file mode 100644 index 0000000000..21103fdd81 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/domain/Result.java @@ -0,0 +1,46 @@ +package com.woowacourse.racingGame.domain; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class Result { + public static final int INIT_POSITION = 0; + + private final Cars cars; + private final Map racingCarStatus; + + public Result(final Cars cars) { + this.cars = cars; + racingCarStatus = generateRacingCarStatus(); + } + + private Map generateRacingCarStatus() { + return cars.getCars() + .stream() + .collect(Collectors.toMap(Car::getName, Car::getPosition, (a, b) -> b)); + } + + public List getWinners() { + final int maxPosition = getMaxPosition(); + return cars.getCars().stream() + .filter(car -> isWinner(car, maxPosition)) + .map(Car::getName) + .collect(Collectors.toList()); + } + + private int getMaxPosition() { + return cars.getCars().stream() + .map(Car::getPosition) + .max(Integer::compareTo) + .orElse(INIT_POSITION); + } + + private boolean isWinner(final Car car, final int maxPosition) { + return car.isSamePosition(maxPosition); + } + + public Map getRacingCarStatus() { + return racingCarStatus; + } +} diff --git a/src/main/java/com/woowacourse/racingGame/utils/StringUtil.java b/src/main/java/com/woowacourse/racingGame/utils/StringUtil.java new file mode 100644 index 0000000000..a267f7b670 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/utils/StringUtil.java @@ -0,0 +1,29 @@ +package com.woowacourse.racingGame.utils; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +// NOTE : 이건 View에 필요한 작업 처리의 보조? View와 같이 있어야하지 않을까? +public class StringUtil { + private static final String DELIMITER = ","; + private static final String DASH = "-"; + public static final String JOIN_DELIMITER = ", "; + + public static List split(final String inputCarName) { + return Arrays.stream(inputCarName.split(DELIMITER)) + .map(String::trim) + .collect(Collectors.toList()); + } + + public static String convertIntoDashBy(final int position) { + return Stream.generate(() -> DASH) + .limit(position) + .collect(Collectors.joining()); + } + + public static String joinNameOf(final List winners) { + return String.join(JOIN_DELIMITER, winners); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/view/InputView.java b/src/main/java/com/woowacourse/racingGame/view/InputView.java new file mode 100644 index 0000000000..488cb439e2 --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/view/InputView.java @@ -0,0 +1,17 @@ +package com.woowacourse.racingGame.view; + +import java.util.Scanner; + +public class InputView { + private Scanner scanner = new Scanner(System.in); + + public String inputCarName() { + System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."); + return scanner.nextLine(); + } + + public String inputMovementNumber() { + System.out.println("시도할 회수는 몇회인가요?"); + return scanner.nextLine(); + } +} diff --git a/src/main/java/com/woowacourse/racingGame/view/OutputView.java b/src/main/java/com/woowacourse/racingGame/view/OutputView.java new file mode 100644 index 0000000000..bfcee31d0e --- /dev/null +++ b/src/main/java/com/woowacourse/racingGame/view/OutputView.java @@ -0,0 +1,34 @@ +package com.woowacourse.racingGame.view; + +import java.util.List; +import java.util.Map; + +import com.woowacourse.racingGame.domain.Result; +import com.woowacourse.racingGame.utils.StringUtil; + +public class OutputView { + public void printExceptionMessage(final String exceptionMessage) { + System.out.println(exceptionMessage); + } + + public void printRacingGameResult(final List results) { + for (Result result : results) { + printRacingCarStatus(result); + } + } + + private void printRacingCarStatus(final Result result) { + final Map racingCarStatus = result.getRacingCarStatus(); + + racingCarStatus.forEach((name, position) -> + System.out.println(name + + " : " + + StringUtil.convertIntoDashBy(position))); + System.out.println(); + } + + public void printWinners(final Result finalResult) { + final String winners = StringUtil.joinNameOf(finalResult.getWinners()); + System.out.println(String.format("%s가 최종 우승했습니다.", winners)); + } +} diff --git a/src/main/java/com/woowacourse/stringCalculator/StringCalculator.java b/src/main/java/com/woowacourse/stringCalculator/StringCalculator.java new file mode 100644 index 0000000000..431e73f6b6 --- /dev/null +++ b/src/main/java/com/woowacourse/stringCalculator/StringCalculator.java @@ -0,0 +1,54 @@ +package com.woowacourse.stringCalculator; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class StringCalculator { + private static final int ZERO = 0; + private static final String DELIMITERS = "[:,]"; + private static final Pattern CUSTOM_REGEX = Pattern.compile("//(.)\n(.*)"); + + // TODO : 실행하는 main문이 없는 경우, private 생성자를 만들어 제한을 둬야 함. + + private StringCalculator() { + } + + public static int calculate(final String value) { + if (isNullOrEmpty(value)) { + return 0; + } + return calculateSum(splitByDelimiter(value)); + } + + private static int calculateSum(String[] values) { + int calculateResult = ZERO; + + for (String number : values) { + final int parsedNumber = Integer.parseInt(number); + + checkNegative(parsedNumber); + calculateResult += parsedNumber; + } + return calculateResult; + } + + private static void checkNegative(final int parsedNumber) { + if (parsedNumber < ZERO) { + throw new RuntimeException(); + } + } + + private static boolean isNullOrEmpty(final String value) { + return value == null || value.isEmpty(); + } + + private static String[] splitByDelimiter(final String value) { + Matcher matcher = CUSTOM_REGEX.matcher(value); + + if (matcher.find()) { + final String customDelimiter = matcher.group(1); + return matcher.group(2).split(customDelimiter); + } + return value.split(DELIMITERS); + } +} diff --git a/src/main/java/empty.txt b/src/main/java/empty.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/test/java/com/woowacourse/racingGame/domain/CarTest.java b/src/test/java/com/woowacourse/racingGame/domain/CarTest.java new file mode 100644 index 0000000000..4540d689b8 --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/CarTest.java @@ -0,0 +1,58 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class CarTest { + private static final int MOVING_UNIT = 1; + + private MovableStrategy movableStrategy; + + @Test + void attemptMoveThrough_랜덤_값이_3_이하() { + final int stoppedRandomNumber = 3; + movableStrategy = new PlannedMovableStrategy(new Power(stoppedRandomNumber)); + final Car car = new Car(new Name("test"), Position.valueOf(4), movableStrategy); + car.attemptMoveThrough(); + + final int actual = car.getPosition(); + + final int expected = 4; + + assertThat(actual).isEqualTo(expected); + } + + @Test + void attemptMoveThrough_랜덤_값이_4_이상() { + final int movedRandomNumber = 7; + final MovableStrategy movableStrategy = new PlannedMovableStrategy(new Power(movedRandomNumber)); + final Car car = new Car(new Name("test"), Position.valueOf(4), movableStrategy); + car.attemptMoveThrough(); + + final int actual = car.getPosition(); + + final int expected = 4 + MOVING_UNIT; + + assertThat(actual).isEqualTo(expected); + } + + @Test + void isSamePosition_우승한_위치의_자동차() { + final int winnerPosition = 7; + final MovableStrategy movableStrategy = new RandomMovableStrategy(); + final Car car = new Car(new Name("test"), Position.valueOf(7), movableStrategy); + + assertThat(car.isSamePosition(winnerPosition)).isTrue(); + } + + @Test + void isSamePosition_우승하지_못한_위치의_자동차() { + final int winnerPosition = 7; + final MovableStrategy movableStrategy = new RandomMovableStrategy(); + final Car car = new Car(new Name("test"), Position.valueOf(6), movableStrategy); + + assertThat(car.isSamePosition(winnerPosition)).isFalse(); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/CarsFactoryTest.java b/src/test/java/com/woowacourse/racingGame/domain/CarsFactoryTest.java new file mode 100644 index 0000000000..dcb246d24b --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/CarsFactoryTest.java @@ -0,0 +1,28 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class CarsFactoryTest { + @Test + void generate_Cars_생성() { + final String inputCarName = "car1,car2,car3,car4"; + final MovableStrategy movableStrategy = new RandomMovableStrategy(); + final List cars = Arrays.asList( + new Car(new Name("car1"), movableStrategy), + new Car(new Name("car2"), movableStrategy), + new Car(new Name("car3"), movableStrategy), + new Car(new Name("car4"), movableStrategy)); + + final Cars actual = CarsFactory.generate(inputCarName, movableStrategy); + + final Cars expect = new Cars(cars); + + assertThat(actual).isEqualTo(expect); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/CarsTest.java b/src/test/java/com/woowacourse/racingGame/domain/CarsTest.java new file mode 100644 index 0000000000..e7324c749b --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/CarsTest.java @@ -0,0 +1,61 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class CarsTest { + private MovableStrategy movableStrategy; + + @Test + void checkDuplicate_유효한_이름() { + movableStrategy = new RandomMovableStrategy(); + List nonDuplicatedCars = Arrays.asList( + new Car(new Name("a"), movableStrategy), + new Car(new Name("bb"), movableStrategy), + new Car(new Name("ccc"), movableStrategy)); + Cars cars = new Cars(nonDuplicatedCars); + + final List actual = cars.getCars(); + + assertThat(actual).isEqualTo(nonDuplicatedCars); + } + + @Test + void checkDuplicate_중복된_이름_존재() { + movableStrategy = new RandomMovableStrategy(); + List duplicatedCars = Arrays.asList( + new Car(new Name("a"), movableStrategy), + new Car(new Name("bb"), movableStrategy), + new Car(new Name("a"), movableStrategy)); + + assertThatThrownBy(() -> new Cars(duplicatedCars)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("중복된 이름이 존재합니다."); + } + + @Test + void attemptMove_전부_이동() { + //given + movableStrategy = new PlannedMovableStrategy(new Power(9)); + List plannedCars = Arrays.asList( + new Car(new Name("a"), new Position(1), movableStrategy), + new Car(new Name("bb"), new Position(2), movableStrategy), + new Car(new Name("ccc"), new Position(3), movableStrategy)); + Cars cars = new Cars(plannedCars); + cars.attemptMove(); + + List actual = cars.getCars().stream() + .map(Car::getPosition) + .collect(Collectors.toList()); + + List expected = Arrays.asList(2, 3, 4); + + assertThat(actual).isEqualTo(expected); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/MovementNumberTest.java b/src/test/java/com/woowacourse/racingGame/domain/MovementNumberTest.java new file mode 100644 index 0000000000..8fb0cbac68 --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/MovementNumberTest.java @@ -0,0 +1,25 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class MovementNumberTest { + @Test + void checkValid_이동_횟수가_숫자가_아닌_경우() { + final String notMovementNumber = "abc"; + + assertThatThrownBy(() -> new MovementNumber(notMovementNumber)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("입력한 이동 횟수가 숫자가 아닙니다."); + } + + @Test + void checkPositive_이동_횟수가_음수() { + final String invalidMovementNumber = "-1"; + + assertThatThrownBy(() -> new MovementNumber(invalidMovementNumber)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/NameTest.java b/src/test/java/com/woowacourse/racingGame/domain/NameTest.java new file mode 100644 index 0000000000..7ce8a538df --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/NameTest.java @@ -0,0 +1,32 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class NameTest { + @Test + void checkNullOrEmpty_null_또는_빈_문자열() { + final String nullName = null; + + assertThatThrownBy(() -> new Name(nullName)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("null 또는 빈 문자열입니다."); + + final String emptyName = ""; + + assertThatThrownBy(() -> new Name(emptyName)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("null 또는 빈 문자열입니다."); + } + + @Test + void checkValidLength_자동차_이름의_길이가_5를_초과() { + final String invalidLengthName = "abcdef"; + + assertThatThrownBy(() -> new Name(invalidLengthName)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("이름은 5자이하만 가능합니다."); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/PositionTest.java b/src/test/java/com/woowacourse/racingGame/domain/PositionTest.java new file mode 100644 index 0000000000..8a546ed5fb --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/PositionTest.java @@ -0,0 +1,39 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class PositionTest { + public static final int MOVING_UNIT = 1; + + @Test + void checkValid_위치가_음수() { + final int negativeNumber = -1; + + assertThatThrownBy(() -> Position.valueOf(negativeNumber)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void increaseByMovingUnit_위치가_Moving_Unit만큼_증가() { + int initPosition = 4; + Position position = Position.valueOf(initPosition); + position = position.increaseByMovingUnit(); + + final int actual = position.getPosition(); + + final int expected = initPosition + MOVING_UNIT; + + assertThat(actual).isEqualTo(expected); + } + + @Test + void valueOf_캐싱된_위치() { + Position position1 = Position.valueOf(1); + Position position2 = Position.valueOf(1); + + assertThat(position1).isEqualTo(position2); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/PowerTest.java b/src/test/java/com/woowacourse/racingGame/domain/PowerTest.java new file mode 100644 index 0000000000..92614effbe --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/PowerTest.java @@ -0,0 +1,42 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +@SuppressWarnings("NonAsciiCharacters") +class PowerTest { + @ParameterizedTest + @CsvSource(value = {"-1", "10"}) + void RandomNo_유효한_범위밖(final int invalidValue) { + assertThatThrownBy(() -> Power.valueOf(invalidValue)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("유효하지 않은 랜덤 숫자입니다."); + } + + @Test + void isMovable_이동_가능() { + final int movableNumber = 4; + Power power = Power.valueOf(movableNumber); + + assertThat(power.isMovable()).isTrue(); + } + + @Test + void isMovable_이동_불가능() { + final int immovableNumber = 3; + Power power = Power.valueOf(immovableNumber); + + assertThat(power.isMovable()).isFalse(); + } + + @Test + void valueOf_캐싱된_범위() { + final Power power1 = Power.valueOf(3); + final Power power2 = Power.valueOf(3); + + assertThat(power1).isEqualTo(power2); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/domain/ResultTest.java b/src/test/java/com/woowacourse/racingGame/domain/ResultTest.java new file mode 100644 index 0000000000..98787e574b --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/domain/ResultTest.java @@ -0,0 +1,52 @@ +package com.woowacourse.racingGame.domain; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class ResultTest { + private final MovableStrategy movableStrategy = new RandomMovableStrategy(); + + @Test + void getRacingCarStatus_게임을_진행한_자동차() { + final List racingCar = Arrays.asList( + new Car(new Name("car1"), Position.valueOf(3), movableStrategy), + new Car(new Name("car2"), Position.valueOf(2), movableStrategy), + new Car(new Name("car3"), Position.valueOf(1), movableStrategy)); + final Result result = new Result(new Cars(racingCar)); + + final Map actual = result.getRacingCarStatus(); + + final Map expected = new HashMap() {{ + put("car1", 3); + put("car2", 2); + put("car3", 1); + }}; + + assertThat(actual).isEqualTo(expected); + } + + @Test + void getWinners_우승한_자동차() { + int winnerPosition = 7; + List inGameCars = Arrays.asList( + new Car(new Name("car1"), Position.valueOf(winnerPosition - 1), movableStrategy), + new Car(new Name("car2"), Position.valueOf(winnerPosition), movableStrategy), + new Car(new Name("car3"), Position.valueOf(winnerPosition - 2), movableStrategy), + new Car(new Name("car4"), Position.valueOf(winnerPosition), movableStrategy)); + Cars cars = new Cars(inGameCars); + Result result = new Result(cars); + + List actual = result.getWinners(); + + List expected = Arrays.asList("car2", "car4"); + + assertThat(actual).isEqualTo(expected); + } +} diff --git a/src/test/java/com/woowacourse/racingGame/utils/StringUtilTest.java b/src/test/java/com/woowacourse/racingGame/utils/StringUtilTest.java new file mode 100644 index 0000000000..8ee882ca10 --- /dev/null +++ b/src/test/java/com/woowacourse/racingGame/utils/StringUtilTest.java @@ -0,0 +1,55 @@ +package com.woowacourse.racingGame.utils; + +import static org.assertj.core.api.Assertions.*; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +public class StringUtilTest { + @Test + void split_쉼표로_구분되어_입력된_자동차_이름() { + final String inputCarName = "a,b,c,d"; + + List actual = StringUtil.split(inputCarName); + + List expected = Arrays.asList("a", "b", "c", "d"); + + assertThat(actual).isEqualTo(expected); + } + + @Test + void split_공백이_앞뒤로_존재하는_자동차_이름() { + final String inputCarName = "a, b, c, d "; + + List actual = StringUtil.split(inputCarName); + + List expected = Arrays.asList("a", "b", "c", "d"); + + assertThat(actual).isEqualTo(expected); + } + + @Test + void convertIntoDashBy_게임을_진행한_자동차의_상태() { + final int position = 4; + + final String actual = StringUtil.convertIntoDashBy(position); + + final String expected = "----"; + + assertThat(actual).isEqualTo(expected); + } + + @Test + void joinNameOf_우승한_자동차_이름들() { + final List winners = Arrays.asList("toni", "alt"); + + final String actual = StringUtil.joinNameOf(winners); + + final String expected = "toni, alt"; + + assertThat(actual).isEqualTo(expected); + } +} diff --git a/src/test/java/com/woowacourse/stringCalculator/StringCalculatorTest.java b/src/test/java/com/woowacourse/stringCalculator/StringCalculatorTest.java new file mode 100644 index 0000000000..4a6f8d209d --- /dev/null +++ b/src/test/java/com/woowacourse/stringCalculator/StringCalculatorTest.java @@ -0,0 +1,68 @@ +package com.woowacourse.stringCalculator; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +@SuppressWarnings("NonAsciiCharacters") +public class StringCalculatorTest { + @Test + void calculate_null_또는_빈문자() { + final String nullValue = null; + + assertThat(StringCalculator.calculate(nullValue)).isEqualTo(0); + + final String emptyValue = ""; + + assertThat(StringCalculator.calculate(emptyValue)).isEqualTo(0); + } + + @ParameterizedTest + @CsvSource(value = {"1,2|3", "1:2|3", "1,2:3|6"}, delimiter = '|') + void calculate_쉼표_콜론_구분자로_숫자합_계산(final String value, final int expected) { + final int actual = StringCalculator.calculate(value); + + assertThat(actual).isEqualTo(expected); + } + + // NOTE : @CsvSource로 value들을 넘기려고 할 때 \ 문자를 어떻게 넘길 수 있을까? + @Test + void calculate_커스텀_구분자로_숫자합_계산() { + final String value = "//;\n1;2;3"; + + final int actual = StringCalculator.calculate(value); + + final int expected = 6; + + assertThat(actual).isEqualTo(expected); + } + + // NOTE : 위의 @CsvSource와 같은 고민 + @Test + void calculate_숫자_이외의_값() { + final String invalidValue = "1:2,a"; + + assertThatThrownBy(() -> StringCalculator.calculate(invalidValue)) + .isInstanceOf(RuntimeException.class); + + final String invalidValue2 = "//;\n1;2;a"; + + assertThatThrownBy(() -> StringCalculator.calculate(invalidValue2)) + .isInstanceOf(RuntimeException.class); + } + + @Test + void calculate_음수() { + final String invalidValue = "1:2,-2"; + + assertThatThrownBy(() -> StringCalculator.calculate(invalidValue)) + .isInstanceOf(RuntimeException.class); + + final String invalidValue2 = "//;\n1;2;-2"; + + assertThatThrownBy(() -> StringCalculator.calculate(invalidValue2)) + .isInstanceOf(RuntimeException.class); + } +} diff --git a/src/test/java/empty.txt b/src/test/java/empty.txt deleted file mode 100644 index e69de29bb2..0000000000