diff --git a/src/main/java/LadderApplication.java b/src/main/java/LadderApplication.java index 67f73e9..e868d03 100644 --- a/src/main/java/LadderApplication.java +++ b/src/main/java/LadderApplication.java @@ -1,10 +1,14 @@ import controller.LadderController; public class LadderApplication { - public static void main(String[] args) { - LadderController ladderController = LadderController.getInstance(); - ladderController.run(); + public static void main(String[] args) { + try { + LadderController ladderController = LadderController.getInstance(); + ladderController.run(); + } catch (Exception e) { + e.printStackTrace(); + } } } diff --git a/src/main/java/controller/LadderController.java b/src/main/java/controller/LadderController.java index ae66260..43a15e3 100644 --- a/src/main/java/controller/LadderController.java +++ b/src/main/java/controller/LadderController.java @@ -1,19 +1,23 @@ package controller; +import dto.LadderResultDto; import dto.LineDto; -import model.Ladder; -import model.Line; +import model.*; +import view.LadderInputView; import view.LadderOutputView; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class LadderController { - private static final int DEFAULT_LADDER_HEIGHT = 4; - private static final int DEFAULT_LADDER_WIDTH = 4; + + private static final String PRINT_EVERY_RESULT = "all"; private static final LadderController ladderController = new LadderController(); - private final LadderOutputView ladderOutputView = LadderOutputView.getInstance(); + private static final LadderOutputView ladderOutputView = LadderOutputView.getInstance(); + private static final LadderInputView ladderInputView = LadderInputView.getInstance(); private LadderController() { } @@ -23,14 +27,82 @@ public static LadderController getInstance() { } public void run() { - Ladder ladder = new Ladder(DEFAULT_LADDER_WIDTH, DEFAULT_LADDER_HEIGHT); - List lines = ladder.getLines(); + LadderUsers ladderUsers = getLadderUsers(); + String[] resultValues = ladderInputView.getResultValues(); + int height = ladderInputView.getHeight(); + + Ladder ladder = new Ladder(ladderUsers.size(), height); + LadderResultCalculator ladderResultCalculator = new LadderResultCalculator(ladderUsers, resultValues); + printLadderShape(ladder, ladderUsers, resultValues); + + printLadderResultUntilPrintEveryResult(ladder, ladderResultCalculator, ladderUsers); + } + + private LadderUsers getLadderUsers() { + String[] names = ladderInputView.getNames(); + + List ladderUsers = Arrays.stream(names) + .map(LadderUser::new) + .toList(); - ladderOutputView.printResultHeader(); + return new LadderUsers(ladderUsers); + } + + private void printLadderShape(Ladder ladder, LadderUsers ladderUsers, String[] resultValues) { + ladderOutputView.printLadderResultHeader(); + ladderOutputView.printNames(ladderUsers.getNames()); + + List lines = ladder.getLines(); for (Line line : lines) { LineDto lineDto = LineDto.from(line); ladderOutputView.printLine(lineDto); } + + ladderOutputView.printResultValues(resultValues); + } + + private void printLadderResultUntilPrintEveryResult( + Ladder ladder, + LadderResultCalculator ladderResultCalculator, + LadderUsers ladderUsers + ) { + String targetName = ladderInputView.getTargetName(); + + while (shouldPrintSingleResult(targetName)) { + printSingleLadderResult(targetName, ladder, ladderResultCalculator); + targetName = ladderInputView.getTargetName(); + } + + printEveryLadderResult(ladder, ladderResultCalculator, ladderUsers); + } + + private boolean shouldPrintSingleResult(String targetName) { + return !targetName.equals(PRINT_EVERY_RESULT); + } + + private void printSingleLadderResult( + String targetName, + Ladder ladder, + LadderResultCalculator ladderResultCalculator + ) { + String result = ladderResultCalculator.calculate(targetName, ladder); + + ladderOutputView.printLadderResult(result); + } + + private void printEveryLadderResult( + Ladder ladder, + LadderResultCalculator ladderResultCalculator, + LadderUsers ladderUsers + ) { + List ladderResultDtos = new ArrayList<>(); + + for (String name : ladderUsers.getNames()) { + String result = ladderResultCalculator.calculate(name, ladder); + ladderResultDtos.add(new LadderResultDto(name, result)); + } + + ladderOutputView.printLadderResults(ladderResultDtos); } } diff --git a/src/main/java/dto/LadderResultDto.java b/src/main/java/dto/LadderResultDto.java new file mode 100644 index 0000000..3bb8ed8 --- /dev/null +++ b/src/main/java/dto/LadderResultDto.java @@ -0,0 +1,4 @@ +package dto; + +public record LadderResultDto(String name, String resultValue) { +} diff --git a/src/main/java/dto/LineDto.java b/src/main/java/dto/LineDto.java index bcb17b2..323fc95 100644 --- a/src/main/java/dto/LineDto.java +++ b/src/main/java/dto/LineDto.java @@ -1,12 +1,13 @@ package dto; import model.Line; -import model.Link; -import model.LinkStatus; import java.util.List; +import static model.LinkStatus.PRESENT; + public class LineDto { + private final List linkExistCollection; private LineDto(List linkExistCollection) { @@ -15,15 +16,14 @@ private LineDto(List linkExistCollection) { public static LineDto from(Line line) { List linkExistCollection = line.getLinks().stream() - .map(Link::getLinkstatus) - .map(LinkStatus::isPresent) + .map(link -> link.getLinkstatus() == PRESENT) .toList(); return new LineDto(linkExistCollection); } public List getLinkExistCollection() { - return List.copyOf(linkExistCollection); + return linkExistCollection; } } diff --git a/src/main/java/model/DetachedRandomLinksGenerator.java b/src/main/java/model/DetachedRandomLinksGenerator.java new file mode 100644 index 0000000..e10e307 --- /dev/null +++ b/src/main/java/model/DetachedRandomLinksGenerator.java @@ -0,0 +1,98 @@ +package model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import static model.LinkStatus.PRESENT; + +public class DetachedRandomLinksGenerator implements LinksGenerator { + + private static final Random random = new Random(); + + private final int linksSize; + + public DetachedRandomLinksGenerator(int linksSize) { + this.linksSize = linksSize; + } + + public List generate() { + List links = getUndefinedLinks(linksSize); + resolveUndefinedLinks(links); + + return Collections.unmodifiableList(links); + } + + private List getUndefinedLinks(int linksSize) { + List links = new ArrayList<>(); + for (int i = 0; i < linksSize; i++) { + links.add(Link.getUndefinedLink()); + } + + return links; + } + + private void resolveUndefinedLinks(List links) { + int index = getRandomStartIndex(links); + + while (containsUndefined(links)) { + boolean connectDecider = getConnectDecider(); + boolean connectable = isConnectable(index, links); + + links.set(index, Link.getDefinedLink(connectDecider, connectable)); + + index = getNextIndex(index, links); + } + } + + private int getRandomStartIndex(List links) { + return random.nextInt(links.size()); + } + + private boolean containsUndefined(List links) { + return links.stream() + .anyMatch(Link::isUndefined); + } + + private boolean getConnectDecider() { + return random.nextBoolean(); + } + + private boolean isConnectable(int index, List links) { + return isLeftNotPresent(index, links) && isRightNotPresent(index, links); + } + + private boolean isLeftNotPresent(int index, List links) { + if (isFirstIndex(index)) { + return true; + } + + Link leftLink = links.get(index - 1); + + return leftLink.getLinkstatus() != PRESENT; + } + + private boolean isRightNotPresent(int index, List links) { + if (isLastIndex(index, links)) { + return true; + } + + Link rightLink = links.get(index + 1); + + return rightLink.getLinkstatus() != PRESENT; + } + + private boolean isFirstIndex(int index) { + return index == 0; + } + + private boolean isLastIndex(int index, List links) { + return index == links.size() - 1; + } + + private int getNextIndex(int index, List linkStatuses) { + return (index + 1) % linkStatuses.size(); + } + +} diff --git a/src/main/java/model/Ladder.java b/src/main/java/model/Ladder.java index 4d4daea..fcb49be 100644 --- a/src/main/java/model/Ladder.java +++ b/src/main/java/model/Ladder.java @@ -5,19 +5,52 @@ import java.util.List; public class Ladder { + + private static final int MINIMUM_WIDTH = 2; + private static final int MINIMUM_HEIGHT = 1; + private final List lines; public Ladder(int width, int height) { + validateSize(width, height); + + this.lines = createLines(width, height); + } + + public List getLines() { + return lines; + } + + public int getEndPoint(int startPoint) { + int point = startPoint; + + for (Line line : lines) { + point = line.getNextPoint(point); + } + + return point; + } + + private List createLines(int width, int height) { + int linksSize = width - 1; + DetachedRandomLinksGenerator linksGenerator = new DetachedRandomLinksGenerator(linksSize); + List lines = new ArrayList<>(); for (int i = 0; i < height; i++) { - lines.add(new Line(width)); + Line line = new Line(linksGenerator); + lines.add(line); } - this.lines = Collections.unmodifiableList(lines); + return Collections.unmodifiableList(lines); } - public List getLines() { - return lines; + private void validateSize(int width, int height) { + if (width < MINIMUM_WIDTH) { + throw new IllegalArgumentException("사다리의 너비는 " + MINIMUM_WIDTH + "보다 짧을 수 없습니다. 전달된 값: " + width); + } + if (height < MINIMUM_HEIGHT) { + throw new IllegalArgumentException("사다리의 높이는 " + MINIMUM_HEIGHT + "보다 짧을 수 없습니다. 전달된 값: " + height); + } } } diff --git a/src/main/java/model/LadderResultCalculator.java b/src/main/java/model/LadderResultCalculator.java new file mode 100644 index 0000000..08c177a --- /dev/null +++ b/src/main/java/model/LadderResultCalculator.java @@ -0,0 +1,37 @@ +package model; + +import java.util.Arrays; +import java.util.List; + +public class LadderResultCalculator { + + private final LadderUsers ladderUsers; + private final List resultValues; + + public LadderResultCalculator(LadderUsers ladderUsers, String[] resultValues) { + validateSize(ladderUsers, resultValues); + + this.ladderUsers = ladderUsers; + this.resultValues = Arrays.stream(resultValues) + .toList(); + } + + public String calculate(String name, Ladder ladder) { + int index = ladderUsers.findIndexOfUserByName(name) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 이름입니다.")); + int endPoint = ladder.getEndPoint(index); + + return findResultByIndex(endPoint); + } + + private void validateSize(LadderUsers ladderUsers, String[] resultValues) { + if (ladderUsers.size() != resultValues.length) { + throw new IllegalArgumentException("참여자의 수와 실행 결과의 수가 일치하지 않습니다."); + } + } + + private String findResultByIndex(int index) { + return resultValues.get(index); + } + +} diff --git a/src/main/java/model/LadderUser.java b/src/main/java/model/LadderUser.java new file mode 100644 index 0000000..0bd9048 --- /dev/null +++ b/src/main/java/model/LadderUser.java @@ -0,0 +1,56 @@ +package model; + +import java.util.Objects; +import java.util.Set; + +public class LadderUser { + + private static final Set RESERVED_KEYWORD = Set.of("all"); + private static final int MAX_NAME_LENGTH = 5; + + private final String name; + + public LadderUser(String name) { + validateName(name); + + this.name = name; + } + + public String getName() { + return name; + } + + private void validateName(String name) { + if (name.trim().isEmpty()) { + throw new IllegalArgumentException("이름이 비어 있습니다."); + } + if (isReservedKeyword(name)) { + throw new IllegalArgumentException(name + "은 이름으로 사용할 수 없습니다. 사용 불가능한 이름 목록: " + RESERVED_KEYWORD); + } + if (isLongerThanMaxLength(name)) { + throw new IllegalArgumentException("이름은 " + MAX_NAME_LENGTH + "글자를 넘길 수 없습니다."); + } + } + + private boolean isReservedKeyword(String name) { + return RESERVED_KEYWORD.contains(name); + } + + private boolean isLongerThanMaxLength(String name) { + return name.length() > MAX_NAME_LENGTH; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LadderUser that = (LadderUser) o; + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } + +} diff --git a/src/main/java/model/LadderUsers.java b/src/main/java/model/LadderUsers.java new file mode 100644 index 0000000..62567cc --- /dev/null +++ b/src/main/java/model/LadderUsers.java @@ -0,0 +1,56 @@ +package model; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class LadderUsers { + + private final List ladderUsers; + + public LadderUsers(List ladderUsers) { + validateSize(ladderUsers); + validateDuplicate(ladderUsers); + + this.ladderUsers = Collections.unmodifiableList(ladderUsers); + } + + public List getNames() { + return ladderUsers.stream() + .map(LadderUser::getName) + .toList(); + } + + public Optional findIndexOfUserByName(String name) { + Optional findUser = ladderUsers.stream() + .filter(ladderUser -> ladderUser.getName().equals(name)) + .findAny(); + + return findUser.map(ladderUsers::indexOf); + } + + public int size() { + return ladderUsers.size(); + } + + private void validateSize(List ladderUsers) { + if (ladderUsers.size() < 2) { + throw new IllegalArgumentException("참여자는 2명 미만일 수 없습니다."); + } + } + + private void validateDuplicate(List ladderUsers) { + boolean duplicated = ladderUsers.stream() + .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) + .values() + .stream() + .anyMatch(count -> count > 1); + + if (duplicated) { + throw new IllegalArgumentException("이름은 중복될 수 없습니다."); + } + } + +} diff --git a/src/main/java/model/Line.java b/src/main/java/model/Line.java index 579e160..1068296 100644 --- a/src/main/java/model/Line.java +++ b/src/main/java/model/Line.java @@ -1,94 +1,67 @@ package model; -import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Random; import static model.LinkStatus.PRESENT; -import static model.LinkStatus.UNDEFINED; public class Line { - private static final Random random = new Random(); private final List links; - public Line(int width) { - int size = width - 1; - List links = initializeLinks(size); - setupLinks(links); - - this.links = List.copyOf(links); + public Line(LinksGenerator linksGenerator) { + this.links = Collections.unmodifiableList(linksGenerator.generate()); } public List getLinks() { return links; } - private List initializeLinks(int size) { - List links = new ArrayList<>(); - for (int i = 0; i < size; i++) { - links.add(Link.getUndefinedLink()); + public int getNextPoint(int point) { + if (shouldMoveToLeft(point)) { + return point - 1; + } + if (shouldMoveToRight(point)) { + return point + 1; } - return links; + return point; } - private void setupLinks(List links) { - int index = getRandomStartIndex(links); - - while (containsUndefined(links)) { - ConnectDecider connectDecider = ConnectDecider.getRandomConnectDecider(); - boolean connectable = isConnectable(index, links); - - links.set(index, Link.getDefinedLink(connectDecider, connectable)); - - index = getNextIndex(index, links); + private boolean shouldMoveToLeft(int point) { + if (isFirstPoint(point)) { + return false; } - } - private int getRandomStartIndex(List links) { - return random.nextInt(links.size()); + return isLeftLinkPresent(point); } - private boolean containsUndefined(List links) { - return links.stream() - .anyMatch(Link::isUndefined); - } - - private boolean isConnectable(int index, List links) { - if (isFirstIndex(index)) { - return isRightNotPresent(index, links); - } - if (isLastIndex(index, links)) { - return isLeftNotPresent(index, links); + private boolean shouldMoveToRight(int point) { + if (isLastPoint(point)) { + return false; } - return isRightNotPresent(index, links) && isLeftNotPresent(index, links); + return isRightLinkPresent(point); } - private boolean isFirstIndex(int index) { - return index == 0; + private boolean isFirstPoint(int point) { + return point == 0; } - private boolean isLastIndex(int index, List links) { - return index == links.size() - 1; + private boolean isLastPoint(int point) { + return point == links.size(); } - private boolean isRightNotPresent(int index, List links) { - Link rightLink = links.get(index + 1); - - return rightLink.getLinkstatus() != PRESENT; - } - - private boolean isLeftNotPresent(int index, List links) { + private boolean isLeftLinkPresent(int index) { Link leftLink = links.get(index - 1); - return leftLink.getLinkstatus() != PRESENT; + return leftLink.getLinkstatus() == PRESENT; } - private int getNextIndex(int index, List linkStatuses) { - return (index + 1) % linkStatuses.size(); + private boolean isRightLinkPresent(int index) { + Link rightLink = links.get(index); + + return rightLink.getLinkstatus() == PRESENT; } } diff --git a/src/main/java/model/Link.java b/src/main/java/model/Link.java index 2db58d1..61fdc1d 100644 --- a/src/main/java/model/Link.java +++ b/src/main/java/model/Link.java @@ -1,6 +1,7 @@ package model; public class Link { + private final LinkStatus linkstatus; private Link(LinkStatus linkstatus) { @@ -11,8 +12,8 @@ public static Link getUndefinedLink() { return new Link(LinkStatus.UNDEFINED); } - public static Link getDefinedLink(ConnectDecider connectDecider, boolean connectable) { - if (connectDecider.isCanBeConnected() && connectable) { + public static Link getDefinedLink(boolean connectDecider, boolean connectable) { + if (connectDecider && connectable) { return new Link(LinkStatus.PRESENT); } diff --git a/src/main/java/model/LinkStatus.java b/src/main/java/model/LinkStatus.java index 479bf2f..9ed062b 100644 --- a/src/main/java/model/LinkStatus.java +++ b/src/main/java/model/LinkStatus.java @@ -1,10 +1,7 @@ package model; public enum LinkStatus { - UNDEFINED, ABSENT, PRESENT; - public boolean isPresent() { - return this == PRESENT; - } + UNDEFINED, ABSENT, PRESENT; } diff --git a/src/main/java/model/LinksGenerator.java b/src/main/java/model/LinksGenerator.java new file mode 100644 index 0000000..6b85e93 --- /dev/null +++ b/src/main/java/model/LinksGenerator.java @@ -0,0 +1,9 @@ +package model; + +import java.util.List; + +public interface LinksGenerator { + + List generate(); + +} diff --git a/src/main/java/view/LadderInputView.java b/src/main/java/view/LadderInputView.java new file mode 100644 index 0000000..1ff8387 --- /dev/null +++ b/src/main/java/view/LadderInputView.java @@ -0,0 +1,56 @@ +package view; + +import java.util.Scanner; + +public class LadderInputView { + + private static final String INPUT_SEPARATOR = ","; + + private static final LadderInputView ladderInputView = new LadderInputView(); + + private final Scanner scanner = new Scanner(System.in); + + private LadderInputView() { + } + + public static LadderInputView getInstance() { + return ladderInputView; + } + + public String[] getNames() { + System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); + String names = scanner.nextLine(); + + System.out.println(); + + return names.split(INPUT_SEPARATOR); + } + + public String[] getResultValues() { + System.out.println("실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)"); + String resultValues = scanner.nextLine(); + + System.out.println(); + + return resultValues.split(INPUT_SEPARATOR); + } + + public int getHeight() { + System.out.println("최대 사다리 높이는 몇 개인가요?"); + int height = Integer.parseInt(scanner.nextLine()); + + System.out.println(); + + return height; + } + + public String getTargetName() { + System.out.println("결과를 보고 싶은 사람은?"); + String targetName = scanner.nextLine(); + + System.out.println(); + + return targetName; + } + +} diff --git a/src/main/java/view/LadderOutputView.java b/src/main/java/view/LadderOutputView.java index 15f870a..21fba3c 100644 --- a/src/main/java/view/LadderOutputView.java +++ b/src/main/java/view/LadderOutputView.java @@ -1,12 +1,19 @@ package view; +import dto.LadderResultDto; import dto.LineDto; +import java.util.List; + public class LadderOutputView { - private static final String INDENTATION = " "; + + private static final String LADDER_INDENTATION = " "; + private static final String VALUE_INDENTATION = " "; private static final String DASH_COUPLER = "-----"; private static final String BLANK_COUPLER = " "; private static final String PILLAR = "|"; + private static final int MAX_FORMATTED_VALUE_LENGTH = 5; + private static final String WHITE_SPACE = " "; private static final LadderOutputView ladderOutputView = new LadderOutputView(); @@ -17,14 +24,24 @@ public static LadderOutputView getInstance() { return ladderOutputView; } - public void printResultHeader() { - System.out.println("실행결과"); + public void printLadderResultHeader() { + System.out.println("사다리 결과"); System.out.println(); } + public void printNames(List names) { + StringBuilder output = new StringBuilder(VALUE_INDENTATION); + + for (String name : names) { + output.append(formatValue(name)) + .append(WHITE_SPACE); + } + + System.out.println(output); + } + public void printLine(LineDto lineDto) { - StringBuilder output = new StringBuilder() - .append(INDENTATION) + StringBuilder output = new StringBuilder(LADDER_INDENTATION) .append(PILLAR); for (boolean isExist : lineDto.getLinkExistCollection()) { @@ -36,6 +53,37 @@ public void printLine(LineDto lineDto) { System.out.println(output); } + public void printResultValues(String[] resultValues) { + StringBuilder output = new StringBuilder(VALUE_INDENTATION); + + for (String resultValue : resultValues) { + output.append(formatValue(resultValue)) + .append(WHITE_SPACE); + } + + System.out.println(output); + System.out.println(); + } + + public void printLadderResult(String resultValue) { + System.out.println("실행 결과"); + + System.out.println(resultValue); + + System.out.println(); + } + + public void printLadderResults(List ladderResultDtos) { + System.out.println("실행 결과"); + + for (LadderResultDto ladderResultDto : ladderResultDtos) { + String name = ladderResultDto.name(); + String resultValue = ladderResultDto.resultValue(); + + System.out.println(name + " : " + resultValue); + } + } + private String getCoupler(boolean isLinkExist) { if (isLinkExist) { return DASH_COUPLER; @@ -43,4 +91,33 @@ private String getCoupler(boolean isLinkExist) { return BLANK_COUPLER; } + private String formatValue(String value) { + StringBuilder valueFormatter = new StringBuilder(value); + + boolean shouldPrepend = true; + while (isShortThanMaxLength(valueFormatter)) { + appendWhiteSpace(valueFormatter, shouldPrepend); + shouldPrepend = toggle(shouldPrepend); + } + + return valueFormatter.toString(); + } + + private boolean isShortThanMaxLength(StringBuilder value) { + return value.length() < MAX_FORMATTED_VALUE_LENGTH; + } + + private void appendWhiteSpace(StringBuilder stringBuilder, boolean shouldPrepend) { + if (shouldPrepend) { + stringBuilder.insert(0, WHITE_SPACE); + return; + } + + stringBuilder.append(WHITE_SPACE); + } + + private boolean toggle(boolean value) { + return !value; + } + } diff --git a/src/test/java/fixture/LadderResultValuesFixture.java b/src/test/java/fixture/LadderResultValuesFixture.java new file mode 100644 index 0000000..40e2133 --- /dev/null +++ b/src/test/java/fixture/LadderResultValuesFixture.java @@ -0,0 +1,20 @@ +package fixture; + +import java.util.Arrays; + +public enum LadderResultValuesFixture { + + FOUR_RESULTS(new String[]{"result1", "result2", "result3", "result4"}), + FIVE_RESULTS(new String[]{"resultA", "resultB", "resultC", "resultD", "resultE"}); + + private final String[] resultValues; + + LadderResultValuesFixture(String[] resultValues) { + this.resultValues = resultValues; + } + + public String[] getValue() { + return Arrays.copyOf(resultValues, resultValues.length); + } + +} diff --git a/src/test/java/fixture/LadderUserFixture.java b/src/test/java/fixture/LadderUserFixture.java new file mode 100644 index 0000000..d4fdc47 --- /dev/null +++ b/src/test/java/fixture/LadderUserFixture.java @@ -0,0 +1,25 @@ +package fixture; + +import model.LadderUser; + +import java.util.List; + +public enum LadderUserFixture { + + FIVE_USER_COLLECTION(List.of("1", "2", "3", "4", "5")), + NOT_DUPLICATED_USER_COLLECTION(List.of("A", "B", "C", "D", "E")), + DUPLICATED_USER_COLLECTION(List.of("A", "B", "C", "A", "D")); + + private final List ladderUserCollection; + + LadderUserFixture(List names) { + this.ladderUserCollection = names.stream() + .map(LadderUser::new) + .toList(); + } + + public List getValueCollection() { + return ladderUserCollection; + } + +} diff --git a/src/test/java/fixture/LadderUsersFixture.java b/src/test/java/fixture/LadderUsersFixture.java new file mode 100644 index 0000000..9ee2d95 --- /dev/null +++ b/src/test/java/fixture/LadderUsersFixture.java @@ -0,0 +1,25 @@ +package fixture; + +import model.LadderUser; +import model.LadderUsers; + +import java.util.List; + +public enum LadderUsersFixture { + + FOUR_USERS(List.of("user1", "user2", "user3", "user4")), + FIVE_USERS(List.of("userA", "userB", "userC", "userD", "userE")); + + private final List ladderUsers; + + LadderUsersFixture(List userNames) { + this.ladderUsers = userNames.stream() + .map(LadderUser::new) + .toList(); + } + + public LadderUsers getValue() { + return new LadderUsers(ladderUsers); + } + +} diff --git a/src/test/java/model/LadderResultCalculatorTest.java b/src/test/java/model/LadderResultCalculatorTest.java new file mode 100644 index 0000000..d0dda5c --- /dev/null +++ b/src/test/java/model/LadderResultCalculatorTest.java @@ -0,0 +1,27 @@ +package model; + +import fixture.LadderResultValuesFixture; +import fixture.LadderUsersFixture; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LadderResultCalculatorTest { + + private static final LadderUsers FOUR_USERS = LadderUsersFixture.FOUR_USERS.getValue(); + private static final LadderUsers FIVE_USERS = LadderUsersFixture.FIVE_USERS.getValue(); + private static final String[] FOUR_RESULTS = LadderResultValuesFixture.FOUR_RESULTS.getValue(); + private static final String[] FIVE_RESULTS = LadderResultValuesFixture.FIVE_RESULTS.getValue(); + + @Test + @DisplayName("사용자 수와 결과 수가 다르면 예외가 발생한다") + void ifUserAmountDifferentFromResultValuesAmountThanThrowException() { + assertThatThrownBy(() -> new LadderResultCalculator(FOUR_USERS, FIVE_RESULTS)) + .isInstanceOf(IllegalArgumentException.class); + + assertThatThrownBy(() -> new LadderResultCalculator(FIVE_USERS, FOUR_RESULTS)) + .isInstanceOf(IllegalArgumentException.class); + } + +} diff --git a/src/test/java/model/LadderTest.java b/src/test/java/model/LadderTest.java new file mode 100644 index 0000000..0e30806 --- /dev/null +++ b/src/test/java/model/LadderTest.java @@ -0,0 +1,50 @@ +package model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class LadderTest { + + private static final int DEFAULT_WIDTH = 5; + private static final int DEFAULT_HEIGHT = 5; + + @Test + @DisplayName("너비와 높이를 받아 인스턴스를 생성한다") + void createByWidthAndHeight() { + assertThatCode(() -> new Ladder(DEFAULT_WIDTH, DEFAULT_HEIGHT)) + .doesNotThrowAnyException(); + } + + @ParameterizedTest + @DisplayName("너비가 너무 짧다면 예외가 발생한다") + @ValueSource(ints = {Integer.MIN_VALUE, -1, 0, 1}) + void ifWidthTooShortThenThrowException(int illegalWidth) { + assertThatThrownBy(() -> new Ladder(illegalWidth, DEFAULT_HEIGHT)) + .isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @DisplayName("높이가 너무 짧다면 예외가 발생한다") + @ValueSource(ints = {Integer.MIN_VALUE, -1, 0}) + void ifHeightTooShortThenThrowException(int illegalHeight) { + assertThatThrownBy(() -> new Ladder(DEFAULT_WIDTH, illegalHeight)) + .isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @DisplayName("생성자에 전달한 높이와 같은 수의 원소를 지닌 Line 컬렉션을 반환한다") + @ValueSource(ints = {5, 10, 200, 3000}) + void returnLineCollectionWithSameNumberOfElementAsHeight(int height) { + Ladder ladder = new Ladder(DEFAULT_WIDTH, height); + List lines = ladder.getLines(); + + assertThat(lines.size()).isEqualTo(height); + } + +} diff --git a/src/test/java/model/LadderUserTest.java b/src/test/java/model/LadderUserTest.java new file mode 100644 index 0000000..958c98c --- /dev/null +++ b/src/test/java/model/LadderUserTest.java @@ -0,0 +1,56 @@ +package model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LadderUserTest { + + private static final List ILLEGAL_NAMES = List.of("all"); + + @ParameterizedTest + @DisplayName("인스턴스를 생성할 때 전달된 name을 반환한다") + @ValueSource(strings = {"A", "B", "C", "D", "E"}) + void getName(String name) { + LadderUser ladderUser = new LadderUser(name); + String actualResult = ladderUser.getName(); + + assertThat(actualResult).isEqualTo(name); + } + + @Test + @DisplayName("사용할 수 없는 이름을 전달하면 예외가 발생한다") + void ifUseIllegalNameThenThrowException() { + for (String illegalName : ILLEGAL_NAMES) { + assertThatThrownBy(() -> new LadderUser(illegalName)) + .isInstanceOf(IllegalArgumentException.class); + } + } + + @ParameterizedTest + @DisplayName("너무 긴 이름을 전달하면 예외가 발생한다") + @ValueSource(strings = { + "123456", + "1234567", + "abcdefgh" + }) + void ifUseTooLongNameThenThrowException(String illegalName) { + assertThatThrownBy(() -> new LadderUser(illegalName)) + .isInstanceOf(IllegalArgumentException.class); + } + + @ParameterizedTest + @DisplayName("빈 이름을 전달하면 예외가 발생한다") + @ValueSource(strings = {"", " ", " "}) + void ifUseEmptyNameThenThrowException(String emptyName) { + assertThatThrownBy(() -> new LadderUser(emptyName)) + .isInstanceOf(IllegalArgumentException.class); + } + +} diff --git a/src/test/java/model/LadderUsersTest.java b/src/test/java/model/LadderUsersTest.java new file mode 100644 index 0000000..e7d6940 --- /dev/null +++ b/src/test/java/model/LadderUsersTest.java @@ -0,0 +1,70 @@ +package model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static fixture.LadderUserFixture.DUPLICATED_USER_COLLECTION; +import static fixture.LadderUserFixture.FIVE_USER_COLLECTION; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LadderUsersTest { + + private static final String DEFAULT_USER_NAME = "hello"; + + @Test + @DisplayName("사용자가 2명 미만이면 예외가 발생한다") + void ifFewUserThenThrowException() { + List singleLadderUserCollection = createLadderUserCollection(DEFAULT_USER_NAME); + + assertThatThrownBy(() -> new LadderUsers(singleLadderUserCollection)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("사용자 이름이 중복되면 예외가 발생한다") + void ifNameDuplicatedThenThrowException() { + List duplicatedUserCollection = DUPLICATED_USER_COLLECTION.getValueCollection(); + + assertThatThrownBy(() -> new LadderUsers(duplicatedUserCollection)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + @DisplayName("사용자 이름을 담은 컬렉션을 반환한다") + void getNames() { + List fiveLadderUserCollection = FIVE_USER_COLLECTION.getValueCollection(); + LadderUsers ladderUsers = new LadderUsers(fiveLadderUserCollection); + List names = ladderUsers.getNames(); + + int actualSize = names.size(); + int expectedSize = fiveLadderUserCollection.size(); + + assertThat(actualSize).isEqualTo(expectedSize); + } + + @Test + @DisplayName("이름을 통해 인덱스 번호를 반환한다") + void getIndexFromName() { + List ladderUserCollection = createLadderUserCollection("1", "2", "3", "4", "5"); + LadderUsers ladderUsers = new LadderUsers(ladderUserCollection); + + for (LadderUser ladderUser : ladderUserCollection) { + int actualIndex = ladderUsers.findIndexOfUserByName(ladderUser.getName()) + .orElseThrow(); + int expectedIndex = ladderUserCollection.indexOf(ladderUser); + + assertThat(actualIndex).isEqualTo(expectedIndex); + } + } + + private List createLadderUserCollection(String... names) { + return Arrays.stream(names) + .map(LadderUser::new) + .toList(); + } + +} diff --git a/src/test/java/model/LineTest.java b/src/test/java/model/LineTest.java new file mode 100644 index 0000000..8307596 --- /dev/null +++ b/src/test/java/model/LineTest.java @@ -0,0 +1,23 @@ +package model; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +class LineTest { + + private static final int DEFAULT_SIZE = 5; + private static final LinksGenerator DEFAULT_LINKS_GENERATOR = new DetachedRandomLinksGenerator(DEFAULT_SIZE); + + @Test + @DisplayName("내부적으로 지니고 있는 List를 반환한다") + void getLinks() { + Line line = new Line(DEFAULT_LINKS_GENERATOR); + List links = line.getLinks(); + + Assertions.assertThat(links.size()).isEqualTo(DEFAULT_SIZE); + } + +} diff --git a/src/test/java/model/LinkTest.java b/src/test/java/model/LinkTest.java new file mode 100644 index 0000000..88e13e0 --- /dev/null +++ b/src/test/java/model/LinkTest.java @@ -0,0 +1,72 @@ +package model; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static model.LinkStatus.*; +import static org.assertj.core.api.Assertions.assertThat; + +class LinkTest { + + private static final boolean DEFAULT_CONNECT_DECIDER = true; + private static final boolean TRUE_CONNECT_DECIDER = true; + private static final boolean FALSE_CONNECT_DECIDER = false; + private static final boolean DEFAULT_CONNECTABLE = true; + private static final boolean TRUE_CONNECTABLE = true; + private static final boolean FALSE_CONNECTABLE = false; + + @Test + @DisplayName("상태 값이 UNDEFINED인 Link 인스턴스를 생성한다") + void createUndefinedLink() { + Link undefinedLink = Link.getUndefinedLink(); + + assertThat(undefinedLink.getLinkstatus()).isEqualTo(UNDEFINED); + } + + @Test + @DisplayName("상태 값이 UNDEFINED가 아닌 Link 인스턴스를 생성한다") + void createNotUndefinedLink() { + Link definedLink = Link.getDefinedLink(DEFAULT_CONNECT_DECIDER, DEFAULT_CONNECTABLE); + + assertThat(definedLink.getLinkstatus()).isNotEqualTo(UNDEFINED); + } + + @Test + @DisplayName("전달 값이 둘 다 ture라면, 상태 값이 PRESENT인 Link 인스턴스를 생성한다") + void createPresentLink() { + Link link = Link.getDefinedLink(TRUE_CONNECT_DECIDER, TRUE_CONNECTABLE); + + assertThat(link.getLinkstatus()).isEqualTo(PRESENT); + } + + @ParameterizedTest + @DisplayName("ConnectDecider가 false 값을 지닌다면, 상태 값이 ABSENT인 Link 인스턴스를 생성한다") + @ValueSource(booleans = {true, false}) + void createAbsentLinkByConnectDecider(boolean connectable) { + Link link = Link.getDefinedLink(FALSE_CONNECT_DECIDER, connectable); + + assertThat(link.getLinkstatus()).isEqualTo(ABSENT); + } + + @ParameterizedTest + @DisplayName("connectable 값이 false라면, 상태 값이 ABSENT인 Link 인스턴스를 생성한다") + @ValueSource(booleans = {true, false}) + void createAbsentLinkByConnectable(boolean connectDecider) { + Link link = Link.getDefinedLink(connectDecider, FALSE_CONNECTABLE); + + assertThat(link.getLinkstatus()).isEqualTo(ABSENT); + } + + @Test + @DisplayName("LinkStatus 값이 UNDEFINED인지 여부를 반환한다") + void returnIsUndefined() { + Link definedLink = Link.getDefinedLink(DEFAULT_CONNECT_DECIDER, DEFAULT_CONNECTABLE); + Link undefinedLink = Link.getUndefinedLink(); + + assertThat(definedLink.isUndefined()).isFalse(); + assertThat(undefinedLink.isUndefined()).isTrue(); + } + +}