Skip to content
Snippets Groups Projects
Commit 46c400ec authored by Nicolai Hollup Brand's avatar Nicolai Hollup Brand :penguin:
Browse files

Merge branch 'feat/filehandling' into 'feat/part-two'

Merge feat/filehandling into feat/part-two

See merge request !10
parents 8239e2b4 43606f6e
No related branches found
No related tags found
3 merge requests!11merge master into part-three,!10Merge feat/filehandling into feat/part-two,!9Feat/part two
Pipeline #230351 passed
Showing
with 1056 additions and 15 deletions
......@@ -15,7 +15,7 @@ import java.util.Objects;
public class Link {
private final String text;
private final String reference;
private final List<Action> actions;
private final List<Action<?>> actions;
/**
* This constructor creates a Link object, which contains information surrounding a linking point in the story.
......@@ -36,7 +36,7 @@ public class Link {
* Adds an action to the list of actions
* @param action, the action to be added to the list
*/
public void addAction(Action action) {
public void addAction(Action<?> action) {
this.actions.add(action);
}
......@@ -60,7 +60,7 @@ public class Link {
* This method retrieves the list of actions attached to the Link object.
* @return The actions of the Link object, given as a List{@code <Action>}.
*/
public List<Action> getActions() {
public List<Action<?>> getActions() {
return this.actions;
}
......@@ -79,6 +79,13 @@ public class Link {
@Override
public String toString() {
return "[" + text + "](" + reference + ")";
StringBuilder sb = new StringBuilder();
sb.append("[").append(text).append("](").append(reference).append(")\n");
for(Action<?> action : actions) {
sb.append("<").append(action.getClass().getSimpleName()).append(">\\").append(action.getActionValue()).append("/\n");
}
return sb.toString();
}
}
......@@ -79,7 +79,7 @@ public class Passage {
sb.append("::").append(title).append("\n")
.append(content).append("\n");
links.forEach(link -> sb.append(link.toString()).append("\n"));
links.forEach(link -> sb.append(link.toString()));
return sb.toString();
}
......
......@@ -4,6 +4,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
/**
......@@ -32,6 +33,7 @@ public class Story {
if (openingPassage == null) throw new IllegalArgumentException("Opening passage cannot be null");
this.openingPassage = openingPassage;
this.passages = new HashMap<>();
addPassage(this.openingPassage);
}
/**
......@@ -103,7 +105,7 @@ public class Story {
/**
* This method retrieves all the passages of a story.
* @return All the pages of the Story as a {@code Collection<Passages>}.
* @return All the passages of the Story as a {@code Collection<Passages>}.
*/
public Collection<Passage> getPassages() {
return this.passages.values();
......@@ -122,8 +124,28 @@ public class Story {
sb.append(this.title).append("\n\n");
sb.append(this.openingPassage.toString()).append("\n");
this.passages.values().forEach(passage -> sb.append(passage.toString()).append("\n"));
this.passages.values().forEach(passage -> {
if(!passage.equals(openingPassage)) sb.append(passage.toString()).append("\n");
});
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Story story)) return false;
if (!Objects.equals(title, story.title)) return false;
if (!Objects.equals(passages, story.passages)) return false;
return Objects.equals(openingPassage, story.openingPassage);
}
@Override
public int hashCode() {
int result = title != null ? title.hashCode() : 0;
result = 31 * result + (passages != null ? passages.hashCode() : 0);
result = 31 * result + (openingPassage != null ? openingPassage.hashCode() : 0);
return result;
}
}
......@@ -3,13 +3,12 @@ package edu.ntnu.idatt2001.group_30.actions;
import edu.ntnu.idatt2001.group_30.Player;
/**
* The functional interface Action provides the method signature for executing an attribute
* The Action interface provides the method signature for executing an attribute
* action on the player.
*
* @author Trym Hamer Gudvangen
*/
@FunctionalInterface
public interface Action {
public interface Action<T> {
/**
* This method changes a given player's attribute:
......@@ -17,4 +16,17 @@ public interface Action {
*/
void execute(Player player);
/**
* This method retrieves the action value of a given action.
* @return Action value, given as an Object.
*/
T getActionValue();
/**
* This method ensures that all action implementations has a way to check if two action objects are equal.
* @param o Object being compared
* @return Boolean representing {@code true} if the actions are equal, otherwise {@code false}
*/
boolean equals(Object o);
}
package edu.ntnu.idatt2001.group_30.actions;
/**
* This class represents a factory for producing Action objects. It, therefore, has methods for instantiating a single
* object from the information provided.
*
* @author Trym Hamer Gudvangen
*/
public class ActionFactory {
/**
* This method takes in an ActionType and the action value in order to create an Action object.
*
* @param actionType The type of action, represented as a ActionType enumeration
* @param actionValue The action value, given as a String.
* @return An action object with the information specified
*/
public static Action<?> getAction(ActionType actionType, String actionValue) throws IllegalArgumentException{
return switch (actionType) {
case GOLD_ACTION -> new GoldAction(Integer.parseInt(actionValue));
case HEALTH_ACTION -> new HealthAction(Integer.parseInt(actionValue));
case INVENTORY_ACTION -> new InventoryAction(actionValue);
case SCORE_ACTION -> new ScoreAction(Integer.parseInt(actionValue));
};
}
}
package edu.ntnu.idatt2001.group_30.actions;
/**
* This enumeration represents the different types of actions that exist: gold, health, inventory, and score.
*
* @author Trym Hamer Gudvangen
*/
public enum ActionType {
GOLD_ACTION,
HEALTH_ACTION,
INVENTORY_ACTION,
SCORE_ACTION
}
......@@ -8,7 +8,7 @@ import java.util.Objects;
*
* @author Trym Hamer Gudvangen
*/
public class GoldAction implements Action {
public class GoldAction implements Action<Integer> {
private final int gold;
......@@ -31,4 +31,26 @@ public class GoldAction implements Action {
Objects.requireNonNull(player);
player.addGold(this.gold);
}
/**
* This method retrieves the gold value.
* @return Gold value, given as an int.
*/
@Override
public Integer getActionValue() {
return gold;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof GoldAction that)) return false;
return gold == that.gold;
}
@Override
public int hashCode() {
return gold;
}
}
......@@ -9,7 +9,7 @@ import java.util.Objects;
*
* @author Trym Hamer Gudvangen
*/
public class HealthAction implements Action {
public class HealthAction implements Action<Integer> {
private final int health;
......@@ -31,4 +31,26 @@ public class HealthAction implements Action {
Objects.requireNonNull(player);
player.addHealth(this.health);
}
/**
* This method retrieves the health value;
* @return Health value, given as an int.
*/
@Override
public Integer getActionValue() {
return health;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof HealthAction that)) return false;
return health == that.health;
}
@Override
public int hashCode() {
return health;
}
}
......@@ -9,7 +9,7 @@ import java.util.Objects;
*
* @author Trym Hamer Gudvangen
*/
public class InventoryAction implements Action {
public class InventoryAction implements Action<String> {
private final String item;
......@@ -31,4 +31,26 @@ public class InventoryAction implements Action {
Objects.requireNonNull(player);
player.addToInventory(this.item);
}
/**
* This method retrieves the item value.
* @return Item value, given as a String.
*/
@Override
public String getActionValue() {
return this.item;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof InventoryAction that)) return false;
return Objects.equals(item, that.item);
}
@Override
public int hashCode() {
return item != null ? item.hashCode() : 0;
}
}
......@@ -9,7 +9,7 @@ import java.util.Objects;
*
* @author Trym Hamer Gudvangen
*/
public class ScoreAction implements Action {
public class ScoreAction implements Action<Integer> {
private final int points;
......@@ -31,4 +31,26 @@ public class ScoreAction implements Action {
Objects.requireNonNull(player);
player.addScore(this.points);
}
/**
* This method retrieves the point value.
* @return Point value, given as an int.
*/
@Override
public Integer getActionValue() {
return this.points;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ScoreAction that)) return false;
return points == that.points;
}
@Override
public int hashCode() {
return points;
}
}
package edu.ntnu.idatt2001.group_30.exceptions;
/**
* An exception that is thrown when Link information a paths file has been corrupted.
*/
public class CorruptFileException extends RuntimeException{
/**
* Constructs a new CorruptFileException with no detail message.
*/
public CorruptFileException() {
}
/**
* Constructs a new CorruptFileException with the specified detail message.
*
* @param message The detail message, given as a String.
*/
public CorruptFileException(String message) {
super(message);
}
/**
* Constructs a new CorruptFileException with the specified detail message and cause.
*
* @param message The detail message, given as a String
* @param cause The cause, given as a Throwable Object.
*/
public CorruptFileException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new CorruptFileException with the specified cause and a detail message of
* {@code cause == null ? null : cause.toString()} (which usually contains the class and detail message of cause).
*
* @param cause The cause, given as a Throwable Object.
*/
public CorruptFileException(Throwable cause) {
super(cause);
}
}
package edu.ntnu.idatt2001.group_30.exceptions;
/**
* An exception that is thrown when Link information a paths file has been corrupted.
*/
public class CorruptLinkException extends RuntimeException{
/**
* Constructs a new CorruptLinkException with no detail message.
*/
public CorruptLinkException() {
}
/**
* Constructs a new CorruptLinkException with the specified detail message.
*
* @param message The detail message, given as a String.
*/
public CorruptLinkException(String message) {
super(message);
}
/**
* Constructs a new CorruptLinkException with the specified detail message and cause.
*
* @param message The detail message, given as a String
* @param cause The cause, given as a Throwable Object.
*/
public CorruptLinkException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new CorruptLinkException with the specified cause and a detail message of
* {@code cause == null ? null : cause.toString()} (which usually contains the class and detail message of cause).
*
* @param cause The cause, given as a Throwable Object.
*/
public CorruptLinkException(Throwable cause) {
super(cause);
}
}
package edu.ntnu.idatt2001.group_30.exceptions;
/**
* An exception that is thrown when an invalid file extension is used.
*/
public class InvalidExtensionException extends IllegalArgumentException{
/**
* Constructs a new InvalidExtensionException with no detail message.
*/
public InvalidExtensionException() {
}
/**
* Constructs a new InvalidExtensionException with the specified detail message.
*
* @param message The detail message, given as a String.
*/
public InvalidExtensionException(String message) {
super(message);
}
/**
* Constructs a new InvalidExtensionException with the specified detail message and cause.
*
* @param message The detail message, given as a String
* @param cause The cause, given as a Throwable Object.
*/
public InvalidExtensionException(String message, Throwable cause) {
super(message, cause);
}
/**
* Constructs a new InvalidExtensionException with the specified cause and a detail message of
* {@code cause == null ? null : cause.toString()} (which usually contains the class and detail message of cause).
*
* @param cause The cause, given as a Throwable Object.
*/
public InvalidExtensionException(Throwable cause) {
super(cause);
}
}
package edu.ntnu.idatt2001.group_30.filehandling;
import edu.ntnu.idatt2001.group_30.exceptions.InvalidExtensionException;
import java.io.File;
import java.nio.file.FileSystems;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class contains general-use methods for handling files such as file creation and name and path validation.
*
* @author Trym Hamer Gudvangen
*/
public class FileHandler {
private static final Pattern VALID_CHAR = Pattern.compile("[^<>:\"/*|?\\\\]*");
private static final Pattern VALID_EXTENSION = Pattern.compile(".*\\.paths$");
private static String defaultPath = "src/main/resources/story-files";
/**
* This method checks whether the given file name is valid.
* @param fileName Name of the given file, given as a String.
* @return {@code true} if the file name is valid.
* @throws IllegalArgumentException This exception is thrown if given file name is blank or has invalid characters.
*/
public static boolean isFileNameValid(String fileName) throws IllegalArgumentException{
if(fileName.isBlank()) throw new IllegalArgumentException("File name cannot be blank");
Matcher matcher = VALID_CHAR.matcher(fileName);
if(!matcher.matches()) throw new IllegalArgumentException("File name contains invalid characters");
return true;
}
/**
* This method checks whether the given file contains .paths as the extension.
* @param fileName Name of the file including extension, given as a String
* @return {@code true} if file name contains .paths, else {@code false}
* @throws InvalidExtensionException This exception is thrown if the file name does not contain .paths at the end
*/
public static boolean isFileExtensionValid(String fileName) throws InvalidExtensionException{
Matcher matcher = VALID_EXTENSION.matcher(fileName);
if(!matcher.matches()) throw new InvalidExtensionException("File name contains invalid characters");
return true;
}
/**
* This method checks if a file exists with a given directory path and whether it contains any information.
* @param file The file to be checked, given as a File object
* @return If the file contains no information, {@code false} is returned. Else, {@code true} is returned
*/
public static boolean fileExists(File file) {
return file.length() > 0;
}
/**
* This method takes a file name. It, then, checks whether the name is valid and if so, it creates a file for it.
* @param fileName Name of the file, given as a String.
* @return The file with the given name, represented using a File object.
* @throws IllegalArgumentException This exception is thrown if the file name is invalid.
*/
public static File createFile(String fileName) throws IllegalArgumentException{
isFileNameValid(fileName);
return new File(getFileSourcePath(fileName));
}
/**
* This method retrieves the file source path of a story file with the file name given.
* @param fileName Name of the desired file, represented as a String
* @return The source path to the file, represented as a String
*/
public static String getFileSourcePath(String fileName){
return FileSystems.getDefault().getPath(defaultPath, fileName) + ".paths";
}
//TODO: test for different OS
/**
* This method changes the default path used to create files. This may, for example, be used for testing purposes.
* @param newPath New default path, given as a String
*/
public static void changeDefaultPath(String newPath) {
defaultPath = newPath;
}
}
package edu.ntnu.idatt2001.group_30.filehandling;
import edu.ntnu.idatt2001.group_30.Link;
import edu.ntnu.idatt2001.group_30.Passage;
import edu.ntnu.idatt2001.group_30.Story;
import edu.ntnu.idatt2001.group_30.actions.Action;
import edu.ntnu.idatt2001.group_30.actions.ActionFactory;
import edu.ntnu.idatt2001.group_30.actions.ActionType;
import edu.ntnu.idatt2001.group_30.exceptions.CorruptFileException;
import edu.ntnu.idatt2001.group_30.exceptions.CorruptLinkException;
import java.io.*;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* This class maintains the storage and retrieval of a Story file. This is done through a Buffered
* writer and reader.
*/
public class StoryFileHandler {
private final Pattern LINK_PATTERN = Pattern.compile("\\[.*]\\(.*\\)");
private final Pattern ACTION_PATTERN = Pattern.compile("<.*>\\\\.*/");
/**
* This method takes a story and writes its contents to a .paths file. The story information is transcribed
* in the given format:
* <pre>
* Story title
*
* ::Opening Passage Title
* Opening Passage Content
* [Link Text](Link Reference)
*
* ::Another Passage Title
* Passage Content
* [Link Text](Link Reference)
* {@code <Action Type>}\Action Value/
* [Link Text](Link Reference)
*
* ...
* </pre>
* @param story The story to be saved, given as a Story object.
* @param fileName The name of the file the story will be saved to, given as a String.
* @throws IOException This exception is thrown if an I/O error occurs with the writer.
*/
public void createStoryFile(Story story, String fileName) throws IOException {
Objects.requireNonNull(story, "Story cannot be null");
Objects.requireNonNull(fileName, "File name cannot be null");
File file = FileHandler.createFile(fileName);
if(FileHandler.fileExists(file)) throw new IllegalArgumentException("You cannot overwrite a pre-existing story file");
try(BufferedWriter storyBufferedWriter = new BufferedWriter(new FileWriter(file))){
storyBufferedWriter.write(story.toString());
}
}
/**
* This method takes a story file and parses it to create a story object.
* @param fileName The name of the story file, given as a String.
* @return The story from the file, given as a Story object.
* @throws IOException This exception is thrown if an I/O error occurs with the reader.
*/
public Story readStoryFromFile(String fileName) throws IOException, InstantiationException {
Objects.requireNonNull(fileName, "File name cannot be null");
File file = new File(FileHandler.getFileSourcePath(fileName));
if(!FileHandler.fileExists(file)) throw new IllegalArgumentException("There is no story file with that name!");
Story story;
try(BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) {
String storyTitle = bufferedReader.readLine();
List<String> passageInfo = new ArrayList<>(List.of(bufferedReader.lines()
.collect(Collectors.joining("\n")).split("\n::")));
if(passageInfo.size() == 1) throw new CorruptFileException(storyTitle);
passageInfo.remove(0);
Passage openingPassage = parseStringToPassage(passageInfo.remove(0));
story = new Story(storyTitle, openingPassage);
for(String passage : passageInfo) {
story.addPassage(parseStringToPassage(passage));
}
}
return story;
}
/**
* This method takes a String containing the information that is vital for a Passage. It, then, parses the
* string into the title, content, and links of the passage. The Links are added to the passage through the
* {@link Passage#addLink(Link)} method.
* @param passageInfo Info of the passage, given as a String.
* @return The passage, given as a Passage object.
*/
private Passage parseStringToPassage(String passageInfo) throws InstantiationException {
String[] splitPassageInfo = passageInfo.split("\n");
Passage passage = new Passage(splitPassageInfo[0], splitPassageInfo[1]);
for(int i = 2; i < splitPassageInfo.length; i++) {
Link link = parseStringToLink(splitPassageInfo[i]);
passage.addLink(link);
while(i + 1 < splitPassageInfo.length && ACTION_PATTERN.matcher(splitPassageInfo[i+1]).matches()){
link.addAction(parseStringToAction(splitPassageInfo[++i]));
}
}
return passage;
}
/**
* This method takes a String containing the information that is vital for a Link. It, then, parses the
* string into the text and reference of the Link.
* @param linkInfo The information of the link, given as a String.
* @return The link, given as a Link object.
*/
private Link parseStringToLink(String linkInfo){
if(!LINK_PATTERN.matcher(linkInfo).matches()) throw new CorruptLinkException(linkInfo);
String text = linkInfo.substring(linkInfo.indexOf("[") + 1, linkInfo.indexOf("]") );
String reference = linkInfo.substring(linkInfo.indexOf("(") + 1, linkInfo.indexOf(")"));
return new Link(text, reference);
}
/**
* This method takes a String containing the information that is vital for an Action. It, then, parses the
* string into the implementation and value.
* @param actionInfo The information of the Action, given as a String.
* @return The action implementation, given as an Action object.
*/
private Action<?> parseStringToAction(String actionInfo) throws InstantiationException {
String className = actionInfo.substring(actionInfo.indexOf("<") + 1, actionInfo.indexOf(">"));
String value = actionInfo.substring(actionInfo.indexOf("\\") + 1, actionInfo.indexOf("/"));
ActionType actionType = extractActionTypeFromInfo(className);
return ActionFactory.getAction(actionType, value);
}
/**
* This method takes in the Action Class info, given as a String. This method checks what ActionType the information
* belongs to.
* @param actionTypeInfo Information of an action's class from a paths File, represented as a String
* @return The type of Action extracted from the String, given as a ActionType enumeration
* @throws InstantiationException This exception is thrown if the action type information is corrupt
*/
private ActionType extractActionTypeFromInfo(String actionTypeInfo) throws InstantiationException{
return switch(actionTypeInfo) {
case "GoldAction" -> ActionType.GOLD_ACTION;
case "HealthAction" -> ActionType.HEALTH_ACTION;
case "InventoryAction" -> ActionType.INVENTORY_ACTION;
case "ScoreAction" -> ActionType.SCORE_ACTION;
default -> throw new InstantiationException("The Action type information is corrupt");
};
}
}
\ No newline at end of file
......@@ -8,6 +8,7 @@ import edu.ntnu.idatt2001.group_30.Player;
*
* @author Trym Hamer Gudvangen
*/
@FunctionalInterface
public interface Goal {
/**
......
......@@ -42,7 +42,7 @@ public class StoryTest {
Passage passage2 = new Passage("Befriend Eilor", "You befriend Eilor");
story.addPassage(passage);
story.addPassage(passage2);
assertEquals(2, story.getPassages().size());
assertEquals(3, story.getPassages().size());
}
@Test
......
package edu.ntnu.idatt2001.group_30.actions;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
class ActionFactoryTest {
@Nested
public class ActionFactory_with_valid_value {
@Test
void can_get_GoldAction(){
ActionType goldAction = ActionType.GOLD_ACTION;
String value = "5";
Action<Integer> expectedAction = new GoldAction(5);
Action<?> actualAction = ActionFactory.getAction(goldAction, value);
Assertions.assertTrue(actualAction instanceof GoldAction);
Assertions.assertEquals(expectedAction, actualAction);
}
@Test
void can_get_HealthAction(){
ActionType healthAction = ActionType.HEALTH_ACTION;
String value = "5";
Action<Integer> expectedAction = new HealthAction(5);
Action<?> actualAction = ActionFactory.getAction(healthAction, value);
Assertions.assertTrue(actualAction instanceof HealthAction);
Assertions.assertEquals(expectedAction, actualAction);
}
@Test
void can_get_InventoryAction(){
ActionType inventoryAction = ActionType.INVENTORY_ACTION;
String value = "Sword";
Action<String> expectedAction = new InventoryAction("Sword");
Action<?> actualAction = ActionFactory.getAction(inventoryAction, value);
Assertions.assertTrue(actualAction instanceof InventoryAction);
Assertions.assertEquals(expectedAction, actualAction);
}
@Test
void can_get_ScoreAction(){
ActionType scoreAction = ActionType.SCORE_ACTION;
String value = "5";
Action<Integer> expectedAction = new ScoreAction(5);
Action<?> actualAction = ActionFactory.getAction(scoreAction, value);
Assertions.assertTrue(actualAction instanceof ScoreAction);
Assertions.assertEquals(expectedAction, actualAction);
}
}
@Nested
public class ActionFactory_with_invalid_value_such_as {
@Test
void null_action_type_throws_NullPointerException() {
Assertions.assertThrows(NullPointerException.class, () -> {
Action<?> action = ActionFactory.getAction(null, "5");
});
}
@Test
void value_throws_NumberFormatException() {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
Action<?> action = ActionFactory.getAction(ActionType.GOLD_ACTION, "Invalid value");
});
}
}
}
\ No newline at end of file
package edu.ntnu.idatt2001.group_30.filehandling;
import edu.ntnu.idatt2001.group_30.exceptions.InvalidExtensionException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.io.File;
import java.nio.file.FileSystems;
import java.util.concurrent.atomic.AtomicReference;
class FileHandlerTest {
@BeforeAll
static void setFileHandlerPath() {
FileHandler.changeDefaultPath("src/test/resources/storytestfiles");
}
@Nested
public class The_FileHandler_makes_sure_a_file_name {
@ParameterizedTest(name = "{index}. File name: {0}")
@ValueSource(strings = {"$123test", "50%Off", "***Story***", "LOTR?", "Winnie the Pooh!",
"LOTR > Hobbit", "The/Hobbit", "[LOTF]", "{LOTR}", "Trym's : Adventure", "Story.paths"})
void does_not_contain_special_characters(String fileName) {
String expectedExceptionMessage = "File name contains invalid characters";
try {
FileHandler.isFileNameValid(fileName);
} catch (IllegalArgumentException e) {
Assertions.assertEquals(expectedExceptionMessage, e.getMessage());
}
}
@ParameterizedTest(name = "{index}. File name: {0}")
@ValueSource(strings = {"", " "})
void is_not_empty_or_blank(String fileName){
String expectedExceptionMessage = "File name cannot be blank";
try {
FileHandler.isFileNameValid(fileName);
} catch (IllegalArgumentException e) {
Assertions.assertEquals(expectedExceptionMessage, e.getMessage());
}
}
@ParameterizedTest(name = "{index}. File name: {0}")
@ValueSource(strings = {"Winnie the Pooh", "L.O.T.R", "The-Bible", "Story123"})
void only_contains_valid_characters(String fileName) {
boolean expectedStatus = true;
boolean actualStatusOfFile = FileHandler.isFileNameValid(fileName);
Assertions.assertEquals(expectedStatus, actualStatusOfFile);
}
}
@Nested
public class The_FileHandler_can_check {
@Test
void if_a_file_exists(){
boolean expectedStatus = true;
File validFile = new File(FileSystems.getDefault()
.getPath("src", "test", "resources", "storytestfiles", "Bones") + ".paths");
boolean actualStatusOfFile = FileHandler.fileExists(validFile);
Assertions.assertEquals(expectedStatus, actualStatusOfFile);
}
@Test
void if_a_file_does_not_exist(){
boolean expectedStatus = false;
File validFile = new File(FileSystems.getDefault()
.getPath("src", "test", "resources", "storytestfiles", "Fairy tale") + ".paths");
boolean actualStatusOfFile = FileHandler.fileExists(validFile);
Assertions.assertEquals(expectedStatus, actualStatusOfFile);
}
@Test
void the_file_source_path(){
String expectedFileSourcePath = "src/test/resources/storytestfiles/story.paths";
String fileName = "story";
String actualFileSourcePath = FileHandler.getFileSourcePath(fileName);
Assertions.assertEquals(expectedFileSourcePath, actualFileSourcePath);
}
@Test
void if_file_extension_is_valid() {
String validPath = "The Hobbit.paths";
boolean isPathValid = FileHandler.isFileExtensionValid(validPath);
Assertions.assertTrue(isPathValid);
}
@ParameterizedTest(name = "{index}. File path: {0}")
@ValueSource(strings = {"Winnie the Pooh.exe", "78924378.doc", "The-Bible.txt", "Story123.csv"})
void if_file_extension_is_invalid(String filePath) {
Assertions.assertThrows(InvalidExtensionException.class, () -> {
FileHandler.isFileExtensionValid(filePath);
});
}
}
@Nested
public class The_FileHandler_can_create {
@ParameterizedTest(name = "{index}. File name: {0}")
@ValueSource(strings = {"Winnie the Pooh", "L.O.T.R", "The-Bible", "Story123"})
void new_files_with_valid_names(String fileName) {
try {
File file = FileHandler.createFile(fileName);
file.createNewFile();
Assertions.assertTrue(file.isFile());
Assertions.assertTrue(file.exists());
file.delete();
} catch (Exception e) {
Assertions.fail(e.getMessage());
}
}
}
}
\ No newline at end of file
package edu.ntnu.idatt2001.group_30.filehandling;
import edu.ntnu.idatt2001.group_30.Link;
import edu.ntnu.idatt2001.group_30.Passage;
import edu.ntnu.idatt2001.group_30.Story;
import edu.ntnu.idatt2001.group_30.actions.*;
import edu.ntnu.idatt2001.group_30.exceptions.CorruptFileException;
import edu.ntnu.idatt2001.group_30.exceptions.CorruptLinkException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.io.*;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class StoryFileHandlerTest {
@BeforeAll
static void setFileHandlerPath() {
FileHandler.changeDefaultPath("src/test/resources/storytestfiles");
}
StoryFileHandler storyFileHandler = new StoryFileHandler();
public File getValidFile(String fileName) {
return FileHandler.createFile(fileName);
}
static Story validStory(){
Story story = new Story("The Hobbit", new Passage("Beginning", "Once upon a time..."));
Passage secondChapter = new Passage("The Great Barrier", "After having completed the arduous...");
story.addPassage(secondChapter);
story.getOpeningPassage().addLink(new Link(secondChapter.getTitle(), secondChapter.getTitle()));
story.getOpeningPassage().getLinks().forEach(link -> link.addAction(new GoldAction(5)));
story.getOpeningPassage().getLinks().get(0).addAction(new ScoreAction(5));
story.getOpeningPassage().getLinks().get(0).addAction(new HealthAction(6));
story.getOpeningPassage().getLinks().get(0).addAction(new InventoryAction("Sword"));
return story;
}
@Nested
public class A_StoryFile_is_valid_if {
@ParameterizedTest(name = "{index}. File name: {0}")
@ValueSource(strings = {"Winnie the Pooh", "L.O.T.R", "The-Bible", "Story123"})
void a_file_has_a_valid_name(String fileName) {
Story story = validStory();
try {
storyFileHandler.createStoryFile(story, fileName);
} catch (Exception e) {
if(!e.getMessage().equals("You cannot overwrite a pre-existing story file")){
System.out.println(e.getMessage());
fail("An exception was thrown when it shouldn't have.");
}
}
File expectedFileCreated = getValidFile(fileName);
Assertions.assertTrue(expectedFileCreated.isFile());
expectedFileCreated.delete();
}
@ParameterizedTest (name = "{index}. File name: {0}")
@ValueSource (strings = {"Winnie the Pooh", "L.O.T.R", "The-Bible", "Story123"})
void files_created_can_be_accessed_and_read(String fileName) {
Story story = validStory();
try{
storyFileHandler.createStoryFile(story, fileName);
}catch (Exception e){
fail("An exception was thrown when it shouldn't have. " + e.getMessage());
}
File expectedFileCreated = getValidFile(fileName);
Assertions.assertTrue(expectedFileCreated.canRead());
expectedFileCreated.delete();
}
@ParameterizedTest (name = "{index}. File name: {0}")
@ValueSource (strings = {"Winnie the Pooh", "L.O.T.R", "The-Bible", "Story123"})
void the_pathing_is_correctly_set(String fileName) {
Story story = validStory();
boolean fileDoesNotExistAtStart = !getValidFile(fileName).exists();
try{
storyFileHandler.createStoryFile(story, fileName);
}catch (Exception e){
fail("An exception was thrown when it shouldn't have.");
}
File expectedFileCreated = getValidFile(fileName);
boolean fileDoesExistAfterWrite = expectedFileCreated.exists();
//Then/Assert
Assertions.assertTrue(fileDoesNotExistAtStart);
Assertions.assertTrue(fileDoesExistAfterWrite);
expectedFileCreated.delete();
}
@Test
void it_cannot_create_new_file_with_preexisting_file_name() {
Story story = validStory();
String fileName = "Bones";
File preexistingFile = getValidFile(fileName);
if(getValidFile(fileName).isFile()) {
Assertions.assertThrows(IllegalArgumentException.class, () -> storyFileHandler.createStoryFile(story, fileName));
}
else fail("The file check for doesn't exist, so this test is invalid");
}
}
@Nested
public class A_StoryFile_properly_writes_a_story_to_new_file_if_it {
@ParameterizedTest (name = "{index}. File name: {0}")
@ValueSource (strings = {"Winnie the Pooh", "L.O.T.R", "The-Bible", "Story123"})
void saves_the_story_title_correctly(String fileName) throws IOException, InstantiationException {
Story story = validStory();
String expectedTitle = story.getTitle();
storyFileHandler.createStoryFile(story, fileName);
Story storyReadFromFile = storyFileHandler.readStoryFromFile(fileName);
String actualTitle = storyReadFromFile.getTitle();
Assertions.assertEquals(expectedTitle, actualTitle);
File file = getValidFile(fileName);
file.delete();
}
@ParameterizedTest (name = "{index}. File name: {0}")
@ValueSource (strings = {"Winnie the Pooh", "L.O.T.R", "The-Bible", "Story123"})
void saves_the_opening_passage_after_title(String fileName) throws IOException, InstantiationException {
Story story = validStory();
Passage expectedOpeningPassage = story.getOpeningPassage();
storyFileHandler.createStoryFile(story, fileName);
Story storyReadFromFile = storyFileHandler.readStoryFromFile(fileName);
Passage actualOpeningPassage = storyReadFromFile.getOpeningPassage();
Assertions.assertEquals(expectedOpeningPassage, actualOpeningPassage);
File file = getValidFile(fileName);
file.delete();
}
@ParameterizedTest (name = "{index}. File name: {0}")
@ValueSource (strings = {"Winnie the Pooh", "L.O.T.R", "The-Bible", "Story123"})
void saves_all_the_links_of_passage_correctly(String fileName) throws IOException, InstantiationException {
Story story = validStory();
List<Link> expectedOpeningPassageLinks = story.getOpeningPassage().getLinks();
storyFileHandler.createStoryFile(story, fileName);
Story storyReadFromFile = storyFileHandler.readStoryFromFile(fileName);
List<Link> actualOpeningPassageLinks = storyReadFromFile.getOpeningPassage().getLinks();
Assertions.assertEquals(expectedOpeningPassageLinks, actualOpeningPassageLinks);
File file = getValidFile(fileName);
file.delete();
}
@ParameterizedTest (name = "{index}. File name: {0}")
@ValueSource (strings = {"Winnie the Pooh", "L.O.T.R", "The-Bible", "Story123"})
void saves_all_the_actions_of_links_correctly(String fileName) throws IOException, InstantiationException {
Story story = validStory();
List<Action<?>> expectedOpeningPassageActions = story.getOpeningPassage().getLinks().get(0).getActions();
storyFileHandler.createStoryFile(story, fileName);
Story storyReadFromFile = storyFileHandler.readStoryFromFile(fileName);
List<Action<?>> actualOpeningPassageActions = storyReadFromFile.getOpeningPassage().getLinks().get(0).getActions();
Assertions.assertEquals(expectedOpeningPassageActions, actualOpeningPassageActions);
File file = getValidFile(fileName);
file.delete();
}
}
@Nested
public class A_StoryFile_properly_reads_a_story_if_it {
@Test
void constructs_a_Story_correctly_when_read() throws IOException, InstantiationException {
Story expectedStory = validStory();
Story actualStory = storyFileHandler.readStoryFromFile("The Hobbit");
assertEquals(expectedStory, actualStory);
}
}
@Nested
public class A_StoryFile_with_invalid_information_such_as {
@Test
void a_null_story_when_creating_new_file_will_throw_NullPointerException(){
Story story = null;
Assertions.assertThrows(NullPointerException.class, () ->{
storyFileHandler.createStoryFile(story, "Null story test");
});
}
@Test
void a_null_file_name_when_creating_new_file_will_throw_NullPointerException(){
Story story = validStory();
Assertions.assertThrows(NullPointerException.class, () ->{
storyFileHandler.createStoryFile(story, null);
});
}
@Test
void a_null_file_name_when_reading_file_will_throw_NullPointerException(){
Assertions.assertThrows(NullPointerException.class, () ->{
Story story = storyFileHandler.readStoryFromFile(null);
});
}
//TODO: change this actually test the link information
@Test
void corrupt_link_information_throws_CorruptLinkException_when_read(){
Story expectedStory = validStory();
Assertions.assertThrows(CorruptLinkException.class, () -> {
Story actualStory = storyFileHandler.readStoryFromFile("Corrupt Link File");
assertNotEquals(expectedStory, actualStory);
});
}
@Test
void file_with_improper_format_throws_CorruptFileException() {
Story expectedStory = validStory();
Assertions.assertThrows(CorruptFileException.class, () ->{
Story actualStory = storyFileHandler.readStoryFromFile("Corrupt .paths Format");
});
}
@Test
void not_existing_throws_IllegalArgumentException() {
Story expectedStory = validStory();
Assertions.assertThrows(IllegalArgumentException.class, () ->{
Story actualStory = storyFileHandler.readStoryFromFile("File that does not exist");
});
}
@Test
void action_class_throws_InstantiationException() {
Story expectedStory = validStory();
Assertions.assertThrows(InstantiationException.class, () -> {
Story actualStory = storyFileHandler.readStoryFromFile("Corrupt Action Class");
});
}
@Test
void corrupt_action_format_throws_CorruptLinkException() {
Story expectedStory = validStory();
Assertions.assertThrows(CorruptLinkException.class, () -> {
Story actualStory = storyFileHandler.readStoryFromFile("Corrupt Action");
});
}
@Test
void valid_action_class_but_invalid_value_throws_IllegalArgumentException() {
Story expectedStory = validStory();
Assertions.assertThrows(IllegalArgumentException.class, () -> {
Story actualStory = storyFileHandler.readStoryFromFile("Corrupt Action Value");
});
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment