diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml new file mode 100644 index 00000000..596391e5 --- /dev/null +++ b/.github/workflows/github-actions.yml @@ -0,0 +1,22 @@ +name: GitHub Actions Demo +on: [push] +jobs: + Explore-GitHub-Actions: + runs-on: ubuntu-latest + steps: + - name: Environment + run: | + echo "Event - ${{ github.event_name }}" + echo "Runner ${{ runner.os }}" + echo "The name of your branch - ${{ github.repository }}/${{ github.ref }}" + - name: checkout + uses: actions/checkout@v2 + - name: Install JDK + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'temurin' + - name: Run deployment test using maven + run: .github/workflows/run-deployment-test.sh + - name: After run status + run: echo "🍏 This job's status is ${{ job.status }}." \ No newline at end of file diff --git a/.github/workflows/run-deployment-test.sh b/.github/workflows/run-deployment-test.sh new file mode 100755 index 00000000..32e3eb71 --- /dev/null +++ b/.github/workflows/run-deployment-test.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +# clean build dir +rm -rf target + + +curl -sL https://www-eu.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.zip -o maven.zip +sudo apt-get update +sudo apt-get -y install unzip +unzip -d /usr/share maven.zip +rm maven.zip + +mvnlnk=/usr/bin/mvn +if [ ! -L $(mvnlnk) ]; then + sudo rm $(mvnlnk) +fi + +sudo ln -s /usr/share/apache-maven-3.6.3/bin/mvn /usr/bin/mvn +echo "M2_HOME=/usr/share/apache-maven-3.6.3" | sudo tee -a /etc/environment + +# source code test +mvn clean +mvn install +mvn compile +mvn test + +# package into jar +mvn package +# test jar output +java -jar NoelLim/Duke.jar < ./src/test/resources/linux-test/input.txt > ./src/test/resources/linux-test/actual.txt + +cmp --print-chars ./src/test/resources/linux-test/actual.txt ./src/test/resources/linux-test/expected.txt +if [ $? -eq 0 ] +then + echo "Test result: PASSED" + exit 0 +else + cat ./src/test/resources/linux-test/actual.txt + cat ./src/test/resources/linux-test/expected.txt + echo "Test result: FAILED" + exit 1 +fi \ No newline at end of file diff --git a/.gitignore b/.gitignore index f69985ef..fd3a024a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,16 +2,29 @@ /.idea/ /out/ /*.iml +/lib # Gradle build files /.gradle/ /build/ src/main/resources/docs/ +# Maven build files +/NoelLim +/target + # MacOS custom attributes files created by Finder .DS_Store *.iml bin/ +# Generated files +/src/main/resources/* +!/src/main/resources/data/* + +# Test /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT +/src/test/resources/generated/* +/src/test/resources/test-output/actual.txt +/src/test/resources/linux-test \ No newline at end of file diff --git a/README.md b/README.md index 8715d4d9..7fde1006 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,61 @@ -# Duke project template - -This is a project template for a greenfield Java project. It's named after the Java mascot _Duke_. Given below are instructions on how to use it. - -## Setting up in Intellij - -Prerequisites: JDK 11, update Intellij to the most recent version. - -1. Open Intellij (if you are not in the welcome screen, click `File` > `Close Project` to close the existing project first) -1. Open the project into Intellij as follows: - 1. Click `Open`. - 1. Select the project directory, and click `OK`. - 1. If there are any further prompts, accept the defaults. -1. Configure the project to use **JDK 11** (not other versions) as explained in [here](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk).
- In the same dialog, set the **Project language level** field to the `SDK default` option. -3. After that, locate the `src/main/java/Duke.java` file, right-click it, and choose `Run Duke.main()` (if the code editor is showing compile errors, try restarting the IDE). If the setup is correct, you should see something like the below as the output: - ``` - Hello from - ____ _ - | _ \ _ _| | _____ - | | | | | | | |/ / _ \ - | |_| | |_| | < __/ - |____/ \__,_|_|\_\___| - ``` +![](./docs/logo.png) + +# Duke +A cli-chatbot task manager. + +# Requirements + +For production you will need JDK 11 support and maven for build. Source code is tested on Linux/Catalina. + +# Run + +Clone the repo. + +## Test + +### With Local Github Action +```act``` test + +### With Maven + +```mvn test``` + +### Build + +In root of repository, + +```mvn package``` package \ +```./NoelLim/Duke.jar``` generated jar is single executable fatty \ +```java -jar ./NoelLim/Duke.jar``` run jar + +# Command List + +`[*]` - unknown command + +`todo [description]` add ToDo \ +`deadline [description] /by [date:dateOption]` add Deadline \ +`event [description] /at [from:dateOption]-[to:dateOption]` add Event + +- dateOption formats + - Input year, month and day : "20201231" + - Input year, month, day, hour, minutes : "20201231 23:59" + +`delete [id]` delete task by id \ +`done [id]` set task as done \ +`undone [id]` set task as not done + +`see [id]` see task + +`list` list tasks \ +`find [keyword]` query tasks with keyword in description + +`stats:all` summary of tasks + +`projection [days]` projection for next days \ +`projection:yet [days]` projection for next days (only show incomplete tasks) + +`scan:duplicates` check for tasks with same description + +`bye` exit input loop \ +`save` save tasks to file + diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 00000000..a5eb3c13 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 8077118e..00000000 --- a/docs/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# User Guide - -## Features - -### Feature-ABC - -Description of the feature. - -### Feature-XYZ - -Description of the feature. - -## Usage - -### `Keyword` - Describe action - -Describe the action and its outcome. - -Example of usage: - -`keyword (optional arguments)` - -Expected outcome: - -Description of the outcome. - -``` -expected output -``` diff --git a/docs/TODO.md b/docs/TODO.md new file mode 100644 index 00000000..b36dc3b2 --- /dev/null +++ b/docs/TODO.md @@ -0,0 +1,85 @@ +for development tracking of project + +# Routine Task +- [ ] progressive documentation + +# Feature Level 0 + +- [x] entry message +- [x] exit message +- [x] exits gently + +# Feature Level 1 + +## Test +- user input loop + - [x] user should input as textCommand and app will echo back full text + - [x] exit loop on textCommand "bye" + + +# Feature Level 2 + +## New Feature +- [x] Create abstract Class Task + - [x] Create ToDo sub class of Task + - [x] Aggregate class TaskManager of Task objects +- textCommand + - [x] default textCommand: create task with textCommand as task description + - [x] add textCommand `list` to list all tasks + +# Feature Level 3 + +## New Feature + +- textCommands + - [x] `done [taskId]` - flag task as done + +# Feature Level 4 + +## New Feature + + +- [x] Create Deadline extends Task + - With deadline +- [x] Create Event extends Task + - With start and end time +- Command + - [x] `$ todo [task_description]` - add a ToDo \ + - [x] `$ deadline [task_description] /by [dateString]` - add a Deadline \ + - [x] `$ event [task_description] /at [fromDateString]-[toDateString]` - add an event \ + - [x] `*` - unknown request + +## Feature Level 5 + +No feature changes (error handling) + +# [Feature Level 6] + +## New Feature + +- [x] `$ delete [task_id]` - delete a task \ + + +# [Feature Level 7] + +## New Feature + +- app will load task resource, if any + - [x] load on start + - [x] `save` - save tasks + +# [Feature Level 8] + +## New Feature + +- [x] change dateStrings to localDateTime + + +# [Feature Level 9] + +## New Feature + +- [x] find by description +- [x] projection for next x days +- [x] view task summary +- [x] check for duplicates \ No newline at end of file diff --git a/docs/logo.png b/docs/logo.png new file mode 100644 index 00000000..6e8e43b3 Binary files /dev/null and b/docs/logo.png differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..0fa6a68f --- /dev/null +++ b/pom.xml @@ -0,0 +1,106 @@ + + + 4.0.0 + + duke + N- + 1.0-SNAPSHOT + + + NoelLim + 11 + 11 + + + ${buildPath} + ${basedir}/src/main/java + ${basedir}/src/test/java + ${basedir}/${buildPath}/classes + ${basedir}/${buildPath}/test-classes + Duke + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + + maven-jar-plugin + 3.2.0 + + + default-jar + none + + + + ${buildPath} + + + true + lib/ + duke.Main + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.1 + + + copy-dependencies + package + + copy-dependencies + + + ${buildPath}/lib + false + false + true + + + + + + maven-assembly-plugin + + + package + + single + + + + + false + + + duke.Main + + + + jar-with-dependencies + + + + + + + + com.google.code.gson + gson + 2.8.8 + + + org.junit.jupiter + junit-jupiter + 5.8.0 + test + + + \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334..00000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/duke/FileResourceManager.java b/src/main/java/duke/FileResourceManager.java new file mode 100644 index 00000000..210b45e1 --- /dev/null +++ b/src/main/java/duke/FileResourceManager.java @@ -0,0 +1,158 @@ +package duke; + +import static duke.dukeutility.parser.PathParser.stringToPath; +import static duke.dukeutility.validator.TextCommandValidator.isParentDirectoryValid; + +import java.io.FileWriter; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.stream.JsonWriter; + +import duke.command.Command; +import duke.command.CommandJsonResponse; +import duke.command.commandfactory.ImportCommandFactory; +import duke.command.errorcommand.CommandExecutionError; +import duke.command.systemcommand.CommandExportTasksToFile; + +public class FileResourceManager { + + private final ImportCommandFactory importCommandFactory = new ImportCommandFactory(); + + private String exportPathString; + private String importPathString; + + /** + * Handles data imports and exports. + * + * @param exportPathString export path + * @param importPathString import path + */ + public FileResourceManager(String exportPathString, String importPathString) { + this.setExportPathString(exportPathString); + this.setImportPathString(importPathString); + + } + + private FileResourceManager() { + } + + + private ImportCommandFactory getImportCommandFactory() { + return this.importCommandFactory; + } + + private String getImportPathString() { + return this.importPathString; + } + + private void setImportPathString(String importPathString) { + this.importPathString = importPathString; + } + + public Path getImportPath() { + return stringToPath(this.getImportPathString()); + } + + private String getExportPathString() { + return this.exportPathString; + } + + private void setExportPathString(String exportPathString) { + this.exportPathString = exportPathString; + } + + public Path getExportPath() { + return stringToPath(this.getExportPathString()); + } + + + /** + * Save tasks in taskManager to this fileResourceManager's export path. + * + * @param taskManager task manager + * @return + */ + public Command executeSave(TaskManager taskManager) { + JsonArray tasksJson = new JsonArray(); + Path exportPath; + JsonWriter jw; + + // get the data to write to + try { + tasksJson = taskManager.getAllAsJson(); + } catch (Exception e) { + return new CommandExecutionError(e, "Obtaining tasks collection from task manager failed " + tasksJson); + } + // try to get the export path + try { + exportPath = this.getExportPath(); + if (exportPath == null) { + throw new Exception("Export path validation failed."); + } + Files.createDirectories(exportPath.getParent()); + if (!isParentDirectoryValid(exportPath)) { + throw new Exception("Export path validation failed."); + } + } catch (Exception e) { + return new CommandExecutionError(e, e.getMessage()); + } + // create a Json writer of export path. + try { + jw = new JsonWriter(new FileWriter(exportPath.toString(), false)); + } catch (Exception e) { + return new CommandExecutionError(e, "JsonWriter initialization failed."); + } + return new CommandExportTasksToFile(tasksJson, exportPath, jw); + } + + /** + * Helper class + * Import tasks from this import path. + * + * @return + */ + public CommandJsonResponse executeExtractTasksFromFile() { + Path path = this.getImportPath(); + return this.getImportCommandFactory().executeExtractTasksFromFile(path); + } + + /** + * Helper class + * Import tasks to task manager + * + * @param tasks json array of tasks + * @param taskManager task manager to import to. + */ + public void importTasks(JsonArray tasks, TaskManager taskManager) { + if (tasks == null || tasks.isEmpty()) { + return; + } + for (JsonElement element : tasks) { + if (element.isJsonObject()) { + JsonObject jsonTask = element.getAsJsonObject(); + this.getImportCommandFactory().executeImportJsonTask(jsonTask, taskManager); + } + } + } + + /** + * Extract from import path, transform to json and load into task manager. + * Display activities to user also. + * + * @param taskManager task manager to import to + * @param ui ui display + * @throws Exception + */ + public void etlTasks(TaskManager taskManager, Ui ui) throws Exception { + Path path = this.getImportPath(); + ui.printInitialLoadTaskAttempt(path); + CommandJsonResponse reading = this.executeExtractTasksFromFile(); + ui.printCommandResponse(reading); + JsonArray tasksFromFile = (JsonArray) reading.getJsonArg(); + this.importTasks(tasksFromFile, taskManager); + } +} diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java new file mode 100644 index 00000000..34e542ac --- /dev/null +++ b/src/main/java/duke/Main.java @@ -0,0 +1,39 @@ +package duke; + +import static duke.dukeutility.config.DukeIo.getDefaultTasksExportPathString; +import static duke.dukeutility.config.DukeIo.getDefaultTasksImportPathString; + +import java.io.PrintStream; + +public class Main { + /** + * @see Main#run(PrintStream, TaskManager, FileResourceManager) + */ + public static void main(String[] args) throws Exception { + Main.run(System.out, new TaskManager(), + new FileResourceManager(getDefaultTasksExportPathString(), getDefaultTasksImportPathString())); + } + + /** + * Helper to entry class. This is the default routine for the program. + * Allows custom PrintStream, TaskManager and FileResourceManager for test phase. + * + * @param out out stream. + * @param taskManager task manager to handle tasks. + * @param frm file resource manager + * @throws Exception + */ + public static void run(PrintStream out, TaskManager taskManager, FileResourceManager frm) throws Exception { + // create a Ui to handle cli I/O. + Ui ui = new Ui(out); + + ui.printEntryMessage(); + // loads tasks + frm.etlTasks(taskManager, ui); + // commence request response cycle + ui.runTextCommandLoop(taskManager, frm); + + ui.printTerminateMessage(); + } + +} diff --git a/src/main/java/duke/TaskManager.java b/src/main/java/duke/TaskManager.java new file mode 100644 index 00000000..392eb288 --- /dev/null +++ b/src/main/java/duke/TaskManager.java @@ -0,0 +1,202 @@ +package duke; + + +import static duke.dukeutility.parser.TaskToJsonParser.parseDeadlineAsJson; +import static duke.dukeutility.parser.TaskToJsonParser.parseEventAsJson; +import static duke.dukeutility.parser.TaskToJsonParser.parseToDoAsJson; +import static duke.dukeutility.validator.StringValidator.isSubstring; +import static duke.task.TaskComparator.isTaskWithinNextDays; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; + +import com.google.gson.JsonArray; + +import duke.task.TaskComparator; +import duke.task.aggregator.TaskList; +import duke.task.model.Deadline; +import duke.task.model.Event; +import duke.task.model.Task; +import duke.task.model.ToDo; + +interface TaskFilter { + Boolean filter(Task t); +} + +public class TaskManager { + // tasks is a "controlled" component. It is a storage for defined properties, i.e tasks ids are set by TaskManager. + private final TaskList tasks = new TaskList(); + private int serialNo = 0; + + + private int rollSerialNo() { + while (this.tasks.containsKey(this.serialNo)) { + this.serialNo++; + } + return this.getSerialNo(); + } + + private int getSerialNo() { + return this.serialNo; + } + + /** + * Add a todo to this task manager. + * + * @param taskDescription + * @return + */ + public ToDo addNewToDo(String taskDescription) { + ToDo task = new ToDo(taskDescription, this.rollSerialNo(), false); + this.tasks.addTask(task); + return task; + } + + /** + * Add a deadline to this task manager. + * + * @param taskDescription + * @param deadlineString + * @return + */ + public Deadline addNewDeadline(String taskDescription, LocalDateTime deadlineString) { + Deadline deadLine = new Deadline(taskDescription, deadlineString, this.rollSerialNo(), false); + this.tasks.addTask(deadLine); + return deadLine; + } + + /** + * Add a event to this task manager. + * + * @param taskDescription + * @param from + * @param to + * @return + */ + public Event addNewEvent(String taskDescription, LocalDateTime from, LocalDateTime to) { + Event event = new Event(taskDescription, from, to, this.rollSerialNo(), false); + this.tasks.addTask(event); + return event; + } + + + /** + * add task to collection + * + * @param toDo + * @return + */ + public Task importTask(Task task) { + this.tasks.addTask(task); + return task; + } + + public Integer getSize() { + return this.tasks.getSize(); + } + + public ArrayList getAllAsArray() { + return this.tasks.getAllAsArray(); + } + + + public ArrayList getTasksWithId(Integer id) { + ArrayList taskList = new ArrayList<>(); + taskList.add(this.tasks.getTaskById(id)); + return taskList; + } + + public ArrayList getTasksForNextDaysAll(Integer period) { + return this.getTasksFiltered((t) -> isTaskWithinNextDays(t, period)); + } + + public ArrayList getTasksForNextDaysNotDone(Integer period) { + return this.getTasksFiltered((t) -> isTaskWithinNextDays(t, period) && !t.isDone()); + } + + public ArrayList getTasksWithKeywordInDescription(String keyword) { + return this.getTasksFiltered((t) -> isSubstring(t.getTaskDescription().toLowerCase(), keyword.toLowerCase())); + } + + public ArrayList getTasksFiltered(TaskFilter cb) { + // filter must have a predicate. + assert (cb != null); + ArrayList all = this.tasks.getAllAsArray(); + ArrayList filtering = new ArrayList<>(); + for (Task t : all) { + if (cb.filter(t)) { + filtering.add(t); + } + } + filtering.sort(TaskComparator::compareTaskDate); + return filtering; + } + + + /** + * Sort tasks into map by description. Return entries with more than one tasks. + * + * @return + */ + public HashMap> getDuplicateDescriptionsAsArray() { + + HashMap> result = new HashMap<>(); + this.tasks.getContainer().forEach((thisId, task) -> { + String desc = task.getTaskDescription(); + ArrayList tasks = result.get(desc); + if (tasks == null) { + tasks = new ArrayList<>(); + } + tasks.add(task); + result.put(desc, tasks); + }); + + result.entrySet().removeIf(entry -> !(entry.getValue().size() > 1)); + + return result; + } + + + public Task getTaskById(Integer taskId) { + return this.tasks.getTaskById(taskId); + } + + public Task getTaskByIdAndSetDoneStatus(Integer taskId, Boolean done) { + Task target = this.getTaskById(taskId); + target.setDoneStatus(done); + return target; + } + + public Task getTaskByIdAndSetCompleted(Integer taskId) { + return this.getTaskByIdAndSetDoneStatus(taskId, true); + } + + public Task getTaskByIdAndSetIncomplete(Integer taskId) { + return this.getTaskByIdAndSetDoneStatus(taskId, false); + } + + public Task deleteTaskById(Integer taskId) { + return this.tasks.removeTaskById(taskId); + } + + public JsonArray getAllAsJson() { + ArrayList tasks = this.getAllAsArray(); + JsonArray tasksJson = new JsonArray(); + for (Task task : tasks) { + if (task instanceof Event) { + tasksJson.add(parseEventAsJson((Event) task)); + } else if (task instanceof Deadline) { + tasksJson.add(parseDeadlineAsJson((Deadline) task)); + } else if (task instanceof ToDo) { + tasksJson.add(parseToDoAsJson((ToDo) task)); + } + } + return tasksJson; + } + + public Boolean containsTaskId(Integer taskId) { + return this.tasks.containsKey(taskId); + } + +} diff --git a/src/main/java/duke/Ui.java b/src/main/java/duke/Ui.java new file mode 100644 index 00000000..4cccc5ed --- /dev/null +++ b/src/main/java/duke/Ui.java @@ -0,0 +1,155 @@ +package duke; + + +import java.io.PrintStream; +import java.nio.file.Path; +import java.util.Scanner; + +import duke.command.Command; +import duke.command.commandfactory.UiCommandFactory; +import duke.dukeutility.enums.ResponseType; + + +/** + * Handles terminal display. + */ +public class Ui { + private final UiCommandFactory uiCommandFactory = new UiCommandFactory(); + private PrintStream out; + private Boolean isLoop = true; + + private Ui() { + } + // Getters and Setters + + public Ui(PrintStream ps) { + this.setPrintStream(ps); + } + + private PrintStream getPrintStream() { + return this.out; + } + + public void setPrintStream(PrintStream ps) { + this.out = ps; + } + + private Boolean isLoop() { + return this.isLoop; + } + + private void setIsLoop(Boolean p) { + this.isLoop = p; + } + + private UiCommandFactory getUiCommandFactory() { + return this.uiCommandFactory; + } + + + // Outputs + + + public void printEntryMessage() { + String logo = " _ _ _ " + System.lineSeparator() + + "| | | | | | " + System.lineSeparator() + + "| |_ __ _ ___ | | __ _ __ ___ __ _ ___ | |_ ___ _ __ " + System.lineSeparator() + + "| __| / _` | / __| | |/ / | '_ ` _ \\ / _` | / __| | __| / _ \\ | '__|" + System.lineSeparator() + + "| |_ | (_| | \\__ \\ | < | | | | | | | (_| | \\__ \\ | |_ | __/ | | " + System.lineSeparator() + + " \\__| \\__,_| |___/ |_|\\_\\ |_| |_| |_| \\__,_| |___/ \\__| \\___| |_| " + System.lineSeparator(); + this.getPrintStream().print("Hello from" + System.lineSeparator() + logo); + } + + public void printInitialLoadTaskAttempt(Path path) { + if (path == null) { + this.getPrintStream().print("Import path empty. " + System.lineSeparator()); + } else { + this.getPrintStream().print("Attempting to import tasks from " + path + "." + System.lineSeparator()); + } + } + + public void printBeginInputLoop() { + this.getPrintStream().print("How can i help you? (See README.md for usage)" + System.lineSeparator()); + } + + + public void printTerminateMessage() { + this.getPrintStream().print("See you again!" + System.lineSeparator()); + } + + private void printEndOfResponse() { + this.getPrintStream().print("\t\t\t\t\t\t\t\t -" + System.lineSeparator()); + } + + + private String getExitLoopMessage() { + return "ok bye" + System.lineSeparator(); + } + + protected void printCommandResponse(Command c) throws Exception { + assert (c != null); + ResponseType rt = c.getResponseType(); + + String output; + + switch (rt) { + case EXIT_LOOP: + output = (this.getExitLoopMessage()); + break; + case TASK_LIST_ALL: + case TASK_LIST_FIND: + case TASK_LIST_ONE: + case TASK_PROJECTION_ALL: + case TASK_PROJECTION_NOT_DONE: + case TASK_STATS_ALL: + case SCAN_DUPLICATE_DESCRIPTION: + case TASK_UPDATE_COMPLETE: + case TASK_UPDATE_INCOMPLETE: + case TASK_NOT_FOUND: + case TASK_DELETE_TASK: + case TASK_CREATE_TODO: + case TASK_CREATE_DEADLINE: + case TASK_CREATE_EVENT: + case ERROR_COMMAND_EXECUTION: + case ERROR_REQUEST_UNKNOWN: + case ERROR_REQUEST_INVALID_SYNTAX: + case ERROR_REQUEST_INVALID_PARAMETERS: + case ERROR_INVALID_READ_FILE_PATH: + case FILE_SAVED: + case FILE_READ: + output = c.getResponse(); + break; + default: + throw new Exception("Unhandled response type [" + rt + "]."); + } + this.getPrintStream().print(output); + } + + /** + * cli session with request-response cycle. + *

+ * Text commands are separated by line breaks. Parsing and execution is delegated to + * After execution, command response will be displayed based on the type of the command returned. + * It is recommended highly to pass in non-null arguments in production environment. + * + * @param taskManager is required if commands wants to manipulate a task collection. + * @param frm is required if commands execute file operations. + * @throws Exception + * @see UiCommandFactory#executeTextCommand() + */ + public void runTextCommandLoop(TaskManager taskManager, FileResourceManager frm) throws Exception { + this.printBeginInputLoop(); + String textCommand; + Scanner in = new Scanner(System.in); + do { + textCommand = in.nextLine(); + Command command = this.getUiCommandFactory().executeTextCommand(textCommand, taskManager, frm); + assert (command != null); + this.setIsLoop(!command.getResponseType().equals(ResponseType.EXIT_LOOP)); + this.printCommandResponse(command); + this.printEndOfResponse(); + } while (this.isLoop()); + } + + +} diff --git a/src/main/java/duke/command/Command.java b/src/main/java/duke/command/Command.java new file mode 100644 index 00000000..8cb89902 --- /dev/null +++ b/src/main/java/duke/command/Command.java @@ -0,0 +1,43 @@ +package duke.command; + +import java.util.List; + +import duke.dukeutility.enums.ResponseType; + + +/** + * Abstract Class for commands. + * Commands executes core behaviors and resultant value(s) are stored for future use. + */ +public abstract class Command { + /* Array of args to record parameters and results for task creation. */ + private List args; + /* Response type for users to identify the behavior that was executed in the command */ + private ResponseType responseType; + + protected Command(ResponseType rt, List args) { + this.setResponseType(rt); + this.setArgs(args); + } + + private Command() { + } + + public ResponseType getResponseType() { + return responseType; + } + + protected void setResponseType(ResponseType rt) { + responseType = rt; + } + + public List getArgs() { + return this.args; + } + + protected void setArgs(List inputArgs) { + this.args = inputArgs; + } + + public abstract String getResponse(); +} diff --git a/src/main/java/duke/command/CommandJsonResponse.java b/src/main/java/duke/command/CommandJsonResponse.java new file mode 100644 index 00000000..6541ba7a --- /dev/null +++ b/src/main/java/duke/command/CommandJsonResponse.java @@ -0,0 +1,25 @@ +package duke.command; + +import java.util.List; + +import com.google.gson.JsonElement; + +import duke.dukeutility.enums.ResponseType; + + +public abstract class CommandJsonResponse extends Command { + private JsonElement jsonArg; + + protected CommandJsonResponse(ResponseType rt, List args, JsonElement jsonArg) { + super(rt, args); + this.setJsonArg(jsonArg); + } + + public JsonElement getJsonArg() { + return this.jsonArg; + } + + private void setJsonArg(JsonElement arg) { + this.jsonArg = arg; + } +} diff --git a/src/main/java/duke/command/commandfactory/CommandFactory.java b/src/main/java/duke/command/commandfactory/CommandFactory.java new file mode 100644 index 00000000..919cc92a --- /dev/null +++ b/src/main/java/duke/command/commandfactory/CommandFactory.java @@ -0,0 +1,7 @@ +package duke.command.commandfactory; + +/** + * @see duke.command.Command + */ +public abstract class CommandFactory { +} diff --git a/src/main/java/duke/command/commandfactory/ImportCommandFactory.java b/src/main/java/duke/command/commandfactory/ImportCommandFactory.java new file mode 100644 index 00000000..d41ab292 --- /dev/null +++ b/src/main/java/duke/command/commandfactory/ImportCommandFactory.java @@ -0,0 +1,64 @@ +package duke.command.commandfactory; + +import static duke.dukeutility.parser.JsonTaskToObjectParser.jsonTaskToPojo; + +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.google.gson.JsonObject; + +import duke.TaskManager; +import duke.command.Command; +import duke.command.CommandJsonResponse; +import duke.command.errorcommand.CommandExecutionError; +import duke.command.errorcommand.CommandReadFileError; +import duke.command.errorcommand.CommandUnknownRequest; +import duke.command.systemcommand.CommandReadTasks; +import duke.command.taskcommand.taskimport.CommandImportDeadline; +import duke.command.taskcommand.taskimport.CommandImportEvent; +import duke.command.taskcommand.taskimport.CommandImportToDo; +import duke.task.model.Deadline; +import duke.task.model.Event; +import duke.task.model.Task; +import duke.task.model.ToDo; + +public class ImportCommandFactory extends CommandFactory { + + + /** + * Extract tasks from file. + * + * @param path file of saved tasks + * @return command + */ + public CommandJsonResponse executeExtractTasksFromFile(Path path) { + Reader reader; + try { + reader = Files.newBufferedReader(path, + StandardCharsets.UTF_8); + } catch (Exception e) { + return new CommandReadFileError("Invalid file read path. " + e); + } + return new CommandReadTasks(reader, path); + } + + public Command executeImportJsonTask(JsonObject jsonObj, TaskManager taskManager) { + Task task; + try { + task = jsonTaskToPojo(jsonObj); + } catch (Exception e) { + return new CommandExecutionError(e, "Error transforming json to java object " + jsonObj.toString()); + } + if (task instanceof Event) { + return new CommandImportEvent((Event) task, taskManager); + } else if (task instanceof Deadline) { + return new CommandImportDeadline((Deadline) task, taskManager); + } else if (task instanceof ToDo) { + return new CommandImportToDo((ToDo) task, taskManager); + } + return new CommandUnknownRequest("Unrecognised Task type."); + } + +} diff --git a/src/main/java/duke/command/commandfactory/UiCommandFactory.java b/src/main/java/duke/command/commandfactory/UiCommandFactory.java new file mode 100644 index 00000000..d6c8fa8e --- /dev/null +++ b/src/main/java/duke/command/commandfactory/UiCommandFactory.java @@ -0,0 +1,306 @@ +package duke.command.commandfactory; + +import static duke.dukeutility.definition.CommandPromptsAndOptions.ADD_DEADLINE_DEADLINE_DELIMITER; +import static duke.dukeutility.definition.CommandPromptsAndOptions.ADD_EVENT_SCHEDULE_DELIMITER; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_ADD_DEADLINE; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_ADD_EVENT; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_ADD_TODO; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_DELETE_TASK; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_FIND_BY_KEYWORD_DESCRIPTION; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_LIST_ONE; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_PROJECTION_ALL; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_PROJECTION_NOT_DONE; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_UPDATE_DONE; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_UPDATE_NOT_DONE; +import static duke.dukeutility.parser.DateParser.parseStringAsLocalDateTime; +import static duke.dukeutility.validator.TextCommandValidator.isRequestAddDeadline; +import static duke.dukeutility.validator.TextCommandValidator.isRequestAddEvent; +import static duke.dukeutility.validator.TextCommandValidator.isRequestAddToDo; +import static duke.dukeutility.validator.TextCommandValidator.isRequestDeleteTask; +import static duke.dukeutility.validator.TextCommandValidator.isRequestExitLoop; +import static duke.dukeutility.validator.TextCommandValidator.isRequestFind; +import static duke.dukeutility.validator.TextCommandValidator.isRequestList; +import static duke.dukeutility.validator.TextCommandValidator.isRequestMarkTaskAsDone; +import static duke.dukeutility.validator.TextCommandValidator.isRequestMarkTaskAsIncomplete; +import static duke.dukeutility.validator.TextCommandValidator.isRequestProjectionAll; +import static duke.dukeutility.validator.TextCommandValidator.isRequestProjectionNotDone; +import static duke.dukeutility.validator.TextCommandValidator.isRequestSave; +import static duke.dukeutility.validator.TextCommandValidator.isRequestScanDuplicates; +import static duke.dukeutility.validator.TextCommandValidator.isRequestSee; +import static duke.dukeutility.validator.TextCommandValidator.isRequestStatisticsAll; + +import java.time.LocalDateTime; + +import duke.FileResourceManager; +import duke.TaskManager; +import duke.command.Command; +import duke.command.errorcommand.CommandExecutionError; +import duke.command.errorcommand.CommandInvalidRequestParameters; +import duke.command.errorcommand.CommandInvalidTextCommandSyntax; +import duke.command.errorcommand.CommandTaskNotFound; +import duke.command.errorcommand.CommandUnknownRequest; +import duke.command.systemcommand.CommandExitLoop; +import duke.command.taskcommand.taskadd.CommandAddNewDeadline; +import duke.command.taskcommand.taskadd.CommandAddNewEvent; +import duke.command.taskcommand.taskadd.CommandAddNewToDo; +import duke.command.taskcommand.taskquery.CommandListAll; +import duke.command.taskcommand.taskquery.CommandListOne; +import duke.command.taskcommand.taskquery.CommandListTasksWithKeyword; +import duke.command.taskcommand.taskquery.CommandProjectionAll; +import duke.command.taskcommand.taskquery.CommandProjectionNotDone; +import duke.command.taskcommand.taskquery.CommandScanDuplicateDescriptions; +import duke.command.taskcommand.taskquery.CommandStatsAll; +import duke.command.taskcommand.taskupdate.CommandDeleteTask; +import duke.command.taskcommand.taskupdate.CommandMarkTaskAsDone; +import duke.command.taskcommand.taskupdate.CommandMarkTaskAsIncomplete; +import duke.dukeexception.DukeParseDateTimeException; + + +public class UiCommandFactory extends CommandFactory { + + public Command executeTextCommand(String text, TaskManager taskManager, FileResourceManager frm) { + assert (text != null); + try { + if (isRequestExitLoop(text)) { + return this.executeCommandExitLoop(); + } else if (isRequestList(text)) { + return new CommandListAll(taskManager); + } else if (isRequestMarkTaskAsDone(text)) { + return this.executeCommandMarkTaskComplete(text, taskManager); + } else if (isRequestMarkTaskAsIncomplete(text)) { + return this.executeCommandMarkTaskIncomplete(text, taskManager); + } else if (isRequestAddToDo(text)) { + return this.executeCommandAddToDo(text, taskManager); + } else if (isRequestAddDeadline(text)) { + return this.executeCommandAddDeadline(text, taskManager); + } else if (isRequestAddEvent(text)) { + return this.executeCommandAddEvent(text, taskManager); + } else if (isRequestDeleteTask(text)) { + return this.executeCommandDeleteTask(text, taskManager); + } else if (isRequestSave(text)) { + return frm.executeSave(taskManager); + } else if (isRequestSee(text)) { + return this.executeSeeTask(text, taskManager); + } else if (isRequestFind(text)) { + return this.executeCommandFindByKeywordInDescription(text, taskManager); + } else if (isRequestProjectionAll(text)) { + return this.executeCommandProjectionAll(text, taskManager); + } else if (isRequestProjectionNotDone(text)) { + return this.executeCommandProjectionNotDone(text, taskManager); + } else if (isRequestStatisticsAll(text)) { + return new CommandStatsAll(taskManager); + } else if (isRequestScanDuplicates(text)) { + return new CommandScanDuplicateDescriptions(taskManager); + } else { + return new CommandUnknownRequest(text); + } + } catch (Exception e) { + return new CommandExecutionError(e, "command execution @ cli"); + } + } + + private Command executeCommandExitLoop() { + return new CommandExitLoop(); + } + + private Command executeCommandAddToDo(String text, TaskManager taskManager) { + int minDescLength = 0; + String taskDescription = text.replaceFirst(PROMPT_ADD_TODO, ""); + if (taskDescription.length() <= minDescLength) { + return new CommandInvalidRequestParameters("ToDo description is too short"); + } + return new CommandAddNewToDo(taskManager, taskDescription); + } + + private Command executeCommandAddDeadline(String text, TaskManager taskManager) { + String argLine; + String[] argList; + String taskDescription; + LocalDateTime deadline; + try { + argLine = text.replaceFirst(PROMPT_ADD_DEADLINE, ""); + String addDlDlDelimiter = ADD_DEADLINE_DEADLINE_DELIMITER; + argList = argLine.split(addDlDlDelimiter); + int argsCount = 2; + if (argList.length != argsCount) { + String msg = + "Expected " + argsCount + " arguments delimited by \"" + addDlDlDelimiter + "\""; + return new CommandInvalidTextCommandSyntax(msg); + } + try { + taskDescription = argList[0]; + deadline = parseStringAsLocalDateTime(argList[1]); + } catch (DukeParseDateTimeException e) { + return new CommandInvalidRequestParameters(e.getMessage()); + } + } catch (Exception e) { + return new CommandInvalidTextCommandSyntax(e.getMessage()); + + } + return new CommandAddNewDeadline(taskManager, taskDescription, deadline); + } + + private Command executeCommandAddEvent(String text, TaskManager taskManager) { + + String argLine; + String[] argList; + String[] scheduleOptionList; + String taskDescription; + LocalDateTime from; + LocalDateTime to; + try { + argLine = text.replaceFirst(PROMPT_ADD_EVENT, ""); + argList = argLine.split(ADD_EVENT_SCHEDULE_DELIMITER); + if (argList.length != 2) { + return new CommandInvalidTextCommandSyntax("Request line for adding event does not conform to syntax."); + } + taskDescription = argList[0]; + scheduleOptionList = argList[1].split("-"); + if (scheduleOptionList.length != 2) { + return new CommandInvalidTextCommandSyntax("Request line for adding event does not conform to syntax."); + } + try { + from = parseStringAsLocalDateTime(scheduleOptionList[0]); + to = parseStringAsLocalDateTime(scheduleOptionList[1]); + } catch (DukeParseDateTimeException e) { + return new CommandInvalidRequestParameters(e.getMessage()); + } + } catch (Exception e) { + return new CommandInvalidRequestParameters(e.toString()); + } + return new CommandAddNewEvent(taskManager, taskDescription, from, to); + } + + + private Command executeCommandMarkTaskComplete(String text, TaskManager taskManager) { + String argLine; + String[] argList; + Integer taskId; + try { + argLine = text.replaceFirst(PROMPT_UPDATE_DONE, ""); + argList = argLine.split(" "); + if (argList.length != 1) { + return new CommandInvalidTextCommandSyntax("Invalid syntax."); + } + taskId = Integer.parseInt(argList[0]); + if (!taskManager.containsTaskId(taskId)) { + return new CommandTaskNotFound(argList[0]); + } + } catch (Exception e) { + return new CommandInvalidRequestParameters(e.toString()); + } + return new CommandMarkTaskAsDone(taskManager, taskId); + } + + private Command executeCommandMarkTaskIncomplete(String text, TaskManager taskManager) { + String argLine; + String[] argList; + Integer taskId; + try { + argLine = text.replaceFirst(PROMPT_UPDATE_NOT_DONE, ""); + argList = argLine.split(" "); + if (argList.length != 1) { + return new CommandInvalidTextCommandSyntax("Invalid syntax."); + } + taskId = Integer.parseInt(argList[0]); + if (!taskManager.containsTaskId(taskId)) { + return new CommandTaskNotFound(argList[0]); + } + } catch (Exception e) { + return new CommandInvalidRequestParameters(e.toString()); + } + return new CommandMarkTaskAsIncomplete(taskManager, taskId); + } + + private Command executeSeeTask(String text, TaskManager taskManager) { + String argLine; + String[] argList; + Integer taskId; + try { + argLine = text.replaceFirst(PROMPT_LIST_ONE, ""); + argList = argLine.split(" "); + if (argList.length != 1) { + return new CommandInvalidTextCommandSyntax("Invalid syntax."); + } + taskId = Integer.parseInt(argList[0]); + if (!taskManager.containsTaskId(taskId)) { + return new CommandTaskNotFound(argList[0]); + } + } catch (Exception e) { + return new CommandInvalidRequestParameters(e.toString()); + } + return new CommandListOne(taskManager, taskId); + } + + private Command executeCommandDeleteTask(String text, TaskManager taskManager) { + String argLine; + String[] argList; + Integer taskId; + try { + argLine = text.replaceFirst(PROMPT_DELETE_TASK, ""); + argList = argLine.split(" "); + if (argList.length != 1) { + return new CommandInvalidTextCommandSyntax("Invalid syntax."); + } + taskId = Integer.parseInt(argList[0]); + if (!taskManager.containsTaskId(taskId)) { + return new CommandTaskNotFound(argList[0]); + } + } catch (Exception e) { + return new CommandInvalidRequestParameters(e.toString()); + } + return new CommandDeleteTask(taskManager, taskId); + } + + + private Command executeCommandFindByKeywordInDescription(String text, TaskManager taskManager) { + String argLine; + String[] argList; + String keyword; + try { + argLine = text.replaceFirst(PROMPT_FIND_BY_KEYWORD_DESCRIPTION, ""); + argList = argLine.split(" "); + if (argList.length != 1) { + return new CommandInvalidTextCommandSyntax("Invalid syntax. Keyword should not have spacing."); + } + keyword = argList[0]; + } catch (Exception e) { + return new CommandInvalidRequestParameters(e.toString()); + } + return new CommandListTasksWithKeyword(taskManager, keyword); + } + + private Command executeCommandProjectionAll(String text, TaskManager taskManager) { + String argLine; + String[] argList; + int period; + try { + argLine = text.replaceFirst(PROMPT_PROJECTION_ALL, ""); + argList = argLine.split(" "); + if (argList.length != 1) { + return new CommandInvalidTextCommandSyntax("Invalid syntax."); + } + period = Integer.parseInt(argList[0]); + } catch (Exception e) { + return new CommandInvalidRequestParameters(e.toString()); + } + return new CommandProjectionAll(taskManager, period); + } + + private Command executeCommandProjectionNotDone(String text, TaskManager taskManager) { + String argLine; + String[] argList; + int period; + try { + argLine = text.replaceFirst(PROMPT_PROJECTION_NOT_DONE, ""); + argList = argLine.split(" "); + if (argList.length != 1) { + return new CommandInvalidTextCommandSyntax("Invalid syntax."); + } + period = Integer.parseInt(argList[0]); + } catch (Exception e) { + return new CommandInvalidRequestParameters(e.toString()); + } + return new CommandProjectionNotDone(taskManager, period); + } +} diff --git a/src/main/java/duke/command/errorcommand/CommandExecutionError.java b/src/main/java/duke/command/errorcommand/CommandExecutionError.java new file mode 100644 index 00000000..409ec7c5 --- /dev/null +++ b/src/main/java/duke/command/errorcommand/CommandExecutionError.java @@ -0,0 +1,16 @@ +package duke.command.errorcommand; + +import java.util.List; + +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandExecutionError extends Command { + public CommandExecutionError(Exception e, String when) { + super(ResponseType.ERROR_COMMAND_EXECUTION, List.of("Error during execution of command ", e.toString())); + } + + public String getResponse() { + return this.getArgs().get(1) + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/command/errorcommand/CommandInvalidRequestParameters.java b/src/main/java/duke/command/errorcommand/CommandInvalidRequestParameters.java new file mode 100644 index 00000000..693a36ce --- /dev/null +++ b/src/main/java/duke/command/errorcommand/CommandInvalidRequestParameters.java @@ -0,0 +1,17 @@ +package duke.command.errorcommand; + +import java.util.List; + +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandInvalidRequestParameters extends Command { + public CommandInvalidRequestParameters(String message) { + super(ResponseType.ERROR_REQUEST_INVALID_PARAMETERS, List.of("Invalid request parameters", message)); + } + + + public String getResponse() { + return "Invalid parameters: " + this.getArgs().get(1) + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/command/errorcommand/CommandInvalidTextCommandSyntax.java b/src/main/java/duke/command/errorcommand/CommandInvalidTextCommandSyntax.java new file mode 100644 index 00000000..8536e021 --- /dev/null +++ b/src/main/java/duke/command/errorcommand/CommandInvalidTextCommandSyntax.java @@ -0,0 +1,16 @@ +package duke.command.errorcommand; + +import java.util.List; + +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandInvalidTextCommandSyntax extends Command { + public CommandInvalidTextCommandSyntax(String message) { + super(ResponseType.ERROR_REQUEST_INVALID_SYNTAX, List.of("Invalid syntax", message)); + } + + public String getResponse() { + return this.getArgs().get(1) + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/command/errorcommand/CommandReadFileError.java b/src/main/java/duke/command/errorcommand/CommandReadFileError.java new file mode 100644 index 00000000..a976b17f --- /dev/null +++ b/src/main/java/duke/command/errorcommand/CommandReadFileError.java @@ -0,0 +1,17 @@ +package duke.command.errorcommand; + +import java.util.List; + +import duke.command.CommandJsonResponse; +import duke.dukeutility.enums.ResponseType; + +public class CommandReadFileError extends CommandJsonResponse { + public CommandReadFileError(String message) { + super(ResponseType.ERROR_INVALID_READ_FILE_PATH, List.of(message), null); + } + + @Override + public String getResponse() { + return "Read path not found/invalid. " + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/command/errorcommand/CommandTaskNotFound.java b/src/main/java/duke/command/errorcommand/CommandTaskNotFound.java new file mode 100644 index 00000000..104dbb02 --- /dev/null +++ b/src/main/java/duke/command/errorcommand/CommandTaskNotFound.java @@ -0,0 +1,17 @@ +package duke.command.errorcommand; + +import java.util.List; + +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandTaskNotFound extends Command { + public CommandTaskNotFound(String text) { + super(ResponseType.TASK_NOT_FOUND, List.of("Task Not Found ", text)); + } + + public String getResponse() { + return "Task Not Found: " + this.getArgs().get(1) + System.lineSeparator(); + } +} + diff --git a/src/main/java/duke/command/errorcommand/CommandUnknownRequest.java b/src/main/java/duke/command/errorcommand/CommandUnknownRequest.java new file mode 100644 index 00000000..592ce121 --- /dev/null +++ b/src/main/java/duke/command/errorcommand/CommandUnknownRequest.java @@ -0,0 +1,16 @@ +package duke.command.errorcommand; + +import java.util.List; + +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandUnknownRequest extends Command { + public CommandUnknownRequest(String text) { + super(ResponseType.ERROR_REQUEST_UNKNOWN, List.of("?", text)); + } + + public String getResponse() { + return "Unknown command. . ." + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/command/systemcommand/CommandExitLoop.java b/src/main/java/duke/command/systemcommand/CommandExitLoop.java new file mode 100644 index 00000000..e688c674 --- /dev/null +++ b/src/main/java/duke/command/systemcommand/CommandExitLoop.java @@ -0,0 +1,19 @@ +package duke.command.systemcommand; + +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_EXIT_LOOP; + +import java.util.List; + +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandExitLoop extends Command { + public CommandExitLoop() { + super(ResponseType.EXIT_LOOP, List.of(PROMPT_EXIT_LOOP)); + } + + @Override + public String getResponse() { + return null; + } +} diff --git a/src/main/java/duke/command/systemcommand/CommandExportTasksToFile.java b/src/main/java/duke/command/systemcommand/CommandExportTasksToFile.java new file mode 100644 index 00000000..8aaae2a8 --- /dev/null +++ b/src/main/java/duke/command/systemcommand/CommandExportTasksToFile.java @@ -0,0 +1,55 @@ +package duke.command.systemcommand; + +import static duke.dukeutility.enums.ResponseType.FILE_SAVED; +import static duke.dukeutility.enums.ResponseType.FILE_SAVED_IO_ERROR; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.stream.JsonWriter; + +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandExportTasksToFile extends Command { + /** + * Export tasks to file as JSON. Will return response type based on the execution. + * + * @param tasks task objects to write + * @param jw writer + */ + public CommandExportTasksToFile(JsonArray tasks, Path writePath, JsonWriter jw) { + super(ResponseType.EXPORT_IN_PROGRESS, List.of("export", writePath.toString())); + + new Gson().toJson(tasks, jw); + try { + jw.close(); + this.setResponseType(FILE_SAVED); + } catch (IOException err) { + this.setResponseType(FILE_SAVED_IO_ERROR); + + } + + + } + + public String getResponse() { + String reply; + ResponseType rt = this.getResponseType(); + switch (rt) { + case FILE_SAVED: + reply = "Saved task to file: "; + break; + case FILE_SAVED_IO_ERROR: + reply = "File save error . . . "; + break; + default: + reply = "Save did not complete to"; + + } + return reply + this.getArgs().get(1) + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/command/systemcommand/CommandReadTasks.java b/src/main/java/duke/command/systemcommand/CommandReadTasks.java new file mode 100644 index 00000000..f5c9f4c5 --- /dev/null +++ b/src/main/java/duke/command/systemcommand/CommandReadTasks.java @@ -0,0 +1,27 @@ +package duke.command.systemcommand; + +import java.io.Reader; +import java.nio.file.Path; +import java.util.List; + +import com.google.gson.JsonParser; + +import duke.command.CommandJsonResponse; +import duke.dukeutility.enums.ResponseType; + +public class CommandReadTasks extends CommandJsonResponse { + /** + * get tasks from reader and parse as json + * + * @param reader reader + * @param path path + */ + public CommandReadTasks(Reader reader, Path path) { + super(ResponseType.FILE_READ, List.of("json", "tasks", path.toString()), + JsonParser.parseReader(reader).getAsJsonArray()); + } + + public String getResponse() { + return "Success reading file " + this.getArgs().get(0) + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskadd/CommandAddNewDeadline.java b/src/main/java/duke/command/taskcommand/taskadd/CommandAddNewDeadline.java new file mode 100644 index 00000000..f10fe175 --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskadd/CommandAddNewDeadline.java @@ -0,0 +1,33 @@ +package duke.command.taskcommand.taskadd; + +import java.time.LocalDateTime; +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; +import duke.task.model.Deadline; + +public class CommandAddNewDeadline extends Command { + /** + * Task manager will add a new deadline to its collection with provided parameters + * + * @param tm task manager + * @param desc of deadline + * @param deadline of deadline + */ + public CommandAddNewDeadline(TaskManager tm, String desc, LocalDateTime dl) { + super(ResponseType.TASK_CREATING_IN_PROGRESS, + null); + + Deadline deadline = tm.addNewDeadline(desc, dl); + this.setArgs(List.of("create", deadline.getTaskId().toString(), deadline.getTaskDescription())); + this.setResponseType(ResponseType.TASK_CREATE_DEADLINE); + } + + public String getResponse() { + String id = this.getArgs().get(1); + String desc = this.getArgs().get(2); + return "Added Deadline [id #" + id + "]: " + desc + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskadd/CommandAddNewEvent.java b/src/main/java/duke/command/taskcommand/taskadd/CommandAddNewEvent.java new file mode 100644 index 00000000..9ffcca78 --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskadd/CommandAddNewEvent.java @@ -0,0 +1,29 @@ +package duke.command.taskcommand.taskadd; + +import java.time.LocalDateTime; +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; +import duke.task.model.Event; + +public class CommandAddNewEvent extends Command { + + public CommandAddNewEvent(TaskManager tm, String desc, LocalDateTime from, LocalDateTime to) { + super(ResponseType.TASK_CREATING_IN_PROGRESS, + null); + + Event ev = tm.addNewEvent(desc, from, to); + + this.setArgs(List.of("create", ev.getTaskId().toString(), ev.getTaskDescription())); + this.setResponseType(ResponseType.TASK_CREATE_EVENT); + } + + public String getResponse() { + String id = this.getArgs().get(1); + String desc = this.getArgs().get(2); + return "Added Event [id #" + id + "]: " + desc + System.lineSeparator(); + } + +} diff --git a/src/main/java/duke/command/taskcommand/taskadd/CommandAddNewToDo.java b/src/main/java/duke/command/taskcommand/taskadd/CommandAddNewToDo.java new file mode 100644 index 00000000..3966c1cb --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskadd/CommandAddNewToDo.java @@ -0,0 +1,35 @@ +package duke.command.taskcommand.taskadd; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; +import duke.task.model.ToDo; + + +public class CommandAddNewToDo extends Command { + + /** + * Task manager will add a new todo to its collection with provided parameters + * + * @param tm task manager + * @param desc of todo + */ + + public CommandAddNewToDo(TaskManager tm, String desc) { + super(ResponseType.TASK_CREATING_IN_PROGRESS, + null); + ToDo todo = tm.addNewToDo(desc); + this.setArgs(List.of("create", todo.getTaskId().toString(), todo.getTaskDescription())); + this.setResponseType(ResponseType.TASK_CREATE_TODO); + } + + public String getResponse() { + + String id = this.getArgs().get(1); + String desc = this.getArgs().get(2); + + return "Added To Do [id #" + id + "]: " + desc + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskimport/CommandImportDeadline.java b/src/main/java/duke/command/taskcommand/taskimport/CommandImportDeadline.java new file mode 100644 index 00000000..0558f0ef --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskimport/CommandImportDeadline.java @@ -0,0 +1,26 @@ +package duke.command.taskcommand.taskimport; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; +import duke.task.model.Deadline; + + +public class CommandImportDeadline extends Command { + /** + * Import a deadline to a task manager + * + * @param deadline deadline + * @param taskMgr task manager + */ + public CommandImportDeadline(Deadline deadline, TaskManager taskMgr) { + super(ResponseType.TASK_IMPORT_DEADLINE, + List.of("create", deadline.getTaskDescription(), taskMgr.importTask(deadline).getTaskDescription())); + } + + public String getResponse() { + return null; + } +} diff --git a/src/main/java/duke/command/taskcommand/taskimport/CommandImportEvent.java b/src/main/java/duke/command/taskcommand/taskimport/CommandImportEvent.java new file mode 100644 index 00000000..3c31bf5b --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskimport/CommandImportEvent.java @@ -0,0 +1,26 @@ +package duke.command.taskcommand.taskimport; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; +import duke.task.model.Event; + + +public class CommandImportEvent extends Command { + /** + * Import a event to a task manager + * + * @param event event + * @param taskMgr task manager + */ + public CommandImportEvent(Event event, TaskManager taskMgr) { + super(ResponseType.TASK_IMPORT_EVENT, + List.of("create", event.getTaskDescription(), taskMgr.importTask(event).getTaskDescription())); + } + + public String getResponse() { + return null; + } +} diff --git a/src/main/java/duke/command/taskcommand/taskimport/CommandImportToDo.java b/src/main/java/duke/command/taskcommand/taskimport/CommandImportToDo.java new file mode 100644 index 00000000..8d68ac56 --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskimport/CommandImportToDo.java @@ -0,0 +1,26 @@ +package duke.command.taskcommand.taskimport; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; +import duke.task.model.ToDo; + + +public class CommandImportToDo extends Command { + /** + * Import a todo to a task manager + * + * @param toDo todo + * @param taskMgr task manager + */ + public CommandImportToDo(ToDo toDo, TaskManager taskMgr) { + super(ResponseType.TASK_IMPORT_TODO, + List.of("create", toDo.getTaskDescription(), taskMgr.importTask(toDo).getTaskDescription())); + } + + public String getResponse() { + return null; + } +} diff --git a/src/main/java/duke/command/taskcommand/taskquery/CommandListAll.java b/src/main/java/duke/command/taskcommand/taskquery/CommandListAll.java new file mode 100644 index 00000000..c1cc3f31 --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskquery/CommandListAll.java @@ -0,0 +1,19 @@ +package duke.command.taskcommand.taskquery; + +import static duke.dukeutility.prettify.Prettify.prettifyTaskMgr; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandListAll extends Command { + public CommandListAll(TaskManager taskMgr) { + super(ResponseType.TASK_LIST_ALL, List.of("list", prettifyTaskMgr(taskMgr))); + } + + public String getResponse() { + return this.getArgs().get(1); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskquery/CommandListOne.java b/src/main/java/duke/command/taskcommand/taskquery/CommandListOne.java new file mode 100644 index 00000000..658f3528 --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskquery/CommandListOne.java @@ -0,0 +1,20 @@ +package duke.command.taskcommand.taskquery; + +import static duke.dukeutility.prettify.Prettify.prettifyTasks; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandListOne extends Command { + public CommandListOne(TaskManager taskMgr, Integer taskId) { + super(ResponseType.TASK_LIST_FIND, + List.of("list", prettifyTasks(taskMgr.getTasksWithId(taskId)), taskId.toString())); + } + + public String getResponse() { + return "See: " + this.getArgs().get(2) + System.lineSeparator() + this.getArgs().get(1); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskquery/CommandListTasksWithKeyword.java b/src/main/java/duke/command/taskcommand/taskquery/CommandListTasksWithKeyword.java new file mode 100644 index 00000000..f44af857 --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskquery/CommandListTasksWithKeyword.java @@ -0,0 +1,21 @@ +package duke.command.taskcommand.taskquery; + +import static duke.dukeutility.prettify.Prettify.prettifyTasks; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandListTasksWithKeyword extends Command { + public CommandListTasksWithKeyword(TaskManager taskMgr, String keyword) { + super(ResponseType.TASK_LIST_FIND, + List.of("list", prettifyTasks(taskMgr.getTasksWithKeywordInDescription(keyword)), keyword)); + } + + public String getResponse() { + return "Query keyword in description: " + this.getArgs().get(2) + System.lineSeparator() + + this.getArgs().get(1); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskquery/CommandProjectionAll.java b/src/main/java/duke/command/taskcommand/taskquery/CommandProjectionAll.java new file mode 100644 index 00000000..139490f7 --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskquery/CommandProjectionAll.java @@ -0,0 +1,21 @@ +package duke.command.taskcommand.taskquery; + +import static duke.dukeutility.prettify.Prettify.prettifyTasks; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandProjectionAll extends Command { + public CommandProjectionAll(TaskManager taskMgr, Integer period) { + super(ResponseType.TASK_PROJECTION_ALL, + List.of("list", prettifyTasks(taskMgr.getTasksForNextDaysAll(period)), period.toString())); + } + + public String getResponse() { + return "All tasks for the next " + this.getArgs().get(2) + " days: " + System.lineSeparator() + + this.getArgs().get(1); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskquery/CommandProjectionNotDone.java b/src/main/java/duke/command/taskcommand/taskquery/CommandProjectionNotDone.java new file mode 100644 index 00000000..67aaafb6 --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskquery/CommandProjectionNotDone.java @@ -0,0 +1,21 @@ +package duke.command.taskcommand.taskquery; + +import static duke.dukeutility.prettify.Prettify.prettifyTasks; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandProjectionNotDone extends Command { + public CommandProjectionNotDone(TaskManager taskMgr, Integer period) { + super(ResponseType.TASK_PROJECTION_NOT_DONE, + List.of("list", prettifyTasks(taskMgr.getTasksForNextDaysNotDone(period)), period.toString())); + } + + public String getResponse() { + return "Incomplete tasks for the next " + this.getArgs().get(2) + " days: " + System.lineSeparator() + + this.getArgs().get(1); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskquery/CommandScanDuplicateDescriptions.java b/src/main/java/duke/command/taskcommand/taskquery/CommandScanDuplicateDescriptions.java new file mode 100644 index 00000000..b6d3fd67 --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskquery/CommandScanDuplicateDescriptions.java @@ -0,0 +1,21 @@ +package duke.command.taskcommand.taskquery; + +import static duke.dukeutility.prettify.Prettify.getScanDuplicatesDescription; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandScanDuplicateDescriptions extends Command { + public CommandScanDuplicateDescriptions(TaskManager taskMgr) { + super(ResponseType.SCAN_DUPLICATE_DESCRIPTION, + List.of("stat", getScanDuplicatesDescription(taskMgr.getDuplicateDescriptionsAsArray()))); + } + + public String getResponse() { + return "Duplicates \"[Description]\":[...(id,type)] " + System.lineSeparator() + + this.getArgs().get(1); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskquery/CommandStatsAll.java b/src/main/java/duke/command/taskcommand/taskquery/CommandStatsAll.java new file mode 100644 index 00000000..70c3a95b --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskquery/CommandStatsAll.java @@ -0,0 +1,19 @@ +package duke.command.taskcommand.taskquery; + +import static duke.dukeutility.prettify.Prettify.getStatisticsAll; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandStatsAll extends Command { + public CommandStatsAll(TaskManager taskMgr) { + super(ResponseType.TASK_STATS_ALL, List.of("stat", getStatisticsAll(taskMgr.getAllAsArray()))); + } + + public String getResponse() { + return "Task Summary " + System.lineSeparator() + this.getArgs().get(1); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskupdate/CommandDeleteTask.java b/src/main/java/duke/command/taskcommand/taskupdate/CommandDeleteTask.java new file mode 100644 index 00000000..1f9888b6 --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskupdate/CommandDeleteTask.java @@ -0,0 +1,25 @@ +package duke.command.taskcommand.taskupdate; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + + +public class CommandDeleteTask extends Command { + /** + * Delete a task by id of task manager + * + * @param taskMgr task manager to delete task from. + * @param id of task + */ + public CommandDeleteTask(TaskManager taskMgr, Integer id) { + super(ResponseType.TASK_DELETE_TASK, + List.of("delete", id.toString(), taskMgr.deleteTaskById(id).toString())); + } + + public String getResponse() { + return "Task Deleted: #" + this.getArgs().get(1) + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskupdate/CommandMarkTaskAsDone.java b/src/main/java/duke/command/taskcommand/taskupdate/CommandMarkTaskAsDone.java new file mode 100644 index 00000000..eabb409b --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskupdate/CommandMarkTaskAsDone.java @@ -0,0 +1,24 @@ +package duke.command.taskcommand.taskupdate; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandMarkTaskAsDone extends Command { + /** + * get task by id in task manager and set task as done + * + * @param taskMgr task manager + * @param taskId id of task + */ + public CommandMarkTaskAsDone(TaskManager taskMgr, Integer taskId) { + super(ResponseType.TASK_UPDATE_COMPLETE, + List.of("update", "done", "#" + taskMgr.getTaskByIdAndSetCompleted(taskId).getTaskId().toString())); + } + + public String getResponse() { + return String.join(" ", this.getArgs()) + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/command/taskcommand/taskupdate/CommandMarkTaskAsIncomplete.java b/src/main/java/duke/command/taskcommand/taskupdate/CommandMarkTaskAsIncomplete.java new file mode 100644 index 00000000..12300a89 --- /dev/null +++ b/src/main/java/duke/command/taskcommand/taskupdate/CommandMarkTaskAsIncomplete.java @@ -0,0 +1,24 @@ +package duke.command.taskcommand.taskupdate; + +import java.util.List; + +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; + +public class CommandMarkTaskAsIncomplete extends Command { + /** + * get task by id in task manager and set task as done + * + * @param taskMgr task manager + * @param taskId id of task + */ + public CommandMarkTaskAsIncomplete(TaskManager taskMgr, Integer taskId) { + super(ResponseType.TASK_UPDATE_INCOMPLETE, + List.of("update", "not done", "#" + taskMgr.getTaskByIdAndSetIncomplete(taskId).getTaskId().toString())); + } + + public String getResponse() { + return String.join(" ", this.getArgs()) + System.lineSeparator(); + } +} diff --git a/src/main/java/duke/dukeexception/DukeException.java b/src/main/java/duke/dukeexception/DukeException.java new file mode 100644 index 00000000..c1908d40 --- /dev/null +++ b/src/main/java/duke/dukeexception/DukeException.java @@ -0,0 +1,7 @@ +package duke.dukeexception; + +public abstract class DukeException extends Exception { + protected DukeException(String message) { + super(message); + } +} diff --git a/src/main/java/duke/dukeexception/DukeInvalidSyntaxException.java b/src/main/java/duke/dukeexception/DukeInvalidSyntaxException.java new file mode 100644 index 00000000..27de10be --- /dev/null +++ b/src/main/java/duke/dukeexception/DukeInvalidSyntaxException.java @@ -0,0 +1,7 @@ +package duke.dukeexception; + +public class DukeInvalidSyntaxException extends DukeException { + public DukeInvalidSyntaxException(String message) { + super(message); + } +} diff --git a/src/main/java/duke/dukeexception/DukeParseDateTimeException.java b/src/main/java/duke/dukeexception/DukeParseDateTimeException.java new file mode 100644 index 00000000..92a7412e --- /dev/null +++ b/src/main/java/duke/dukeexception/DukeParseDateTimeException.java @@ -0,0 +1,7 @@ +package duke.dukeexception; + +public class DukeParseDateTimeException extends DukeException { + public DukeParseDateTimeException(String message) { + super(message); + } +} diff --git a/src/main/java/duke/dukeutility/Helper.java b/src/main/java/duke/dukeutility/Helper.java new file mode 100644 index 00000000..bf3f0b2b --- /dev/null +++ b/src/main/java/duke/dukeutility/Helper.java @@ -0,0 +1,18 @@ +package duke.dukeutility; + +public class Helper { + + /** + * Helper to combine array of strings. + * + * @param commands + * @return + */ + public static String buildString(String... commands) { + StringBuilder commandBuilder = new StringBuilder(); + for (String c : commands) { + commandBuilder.append(c); + } + return commandBuilder.toString(); + } +} diff --git a/src/main/java/duke/dukeutility/config/DukeIo.java b/src/main/java/duke/dukeutility/config/DukeIo.java new file mode 100644 index 00000000..696fe213 --- /dev/null +++ b/src/main/java/duke/dukeutility/config/DukeIo.java @@ -0,0 +1,28 @@ +package duke.dukeutility.config; + + +import java.io.File; + +public class DukeIo { + public static final String RESOURCE_PATH = System.getProperty("user.home") + File.separator + "duke"; + + private static String pathStringDefaultTasksExportJsonPath = null; + private static String pathStringDefaultTasksImportPath = null; + + public static String getDefaultTasksImportPathString() { + if (DukeIo.pathStringDefaultTasksImportPath == null) { + DukeIo.pathStringDefaultTasksImportPath = RESOURCE_PATH + File.separator + "tasks.json"; + } + return DukeIo.pathStringDefaultTasksImportPath; + + } + + public static String getDefaultTasksExportPathString() { + if (DukeIo.pathStringDefaultTasksExportJsonPath == null) { + DukeIo.pathStringDefaultTasksExportJsonPath = RESOURCE_PATH + File.separator + "tasks.json"; + } + return DukeIo.pathStringDefaultTasksExportJsonPath; + } +} + + diff --git a/src/main/java/duke/dukeutility/definition/CommandPromptsAndOptions.java b/src/main/java/duke/dukeutility/definition/CommandPromptsAndOptions.java new file mode 100644 index 00000000..3b68447c --- /dev/null +++ b/src/main/java/duke/dukeutility/definition/CommandPromptsAndOptions.java @@ -0,0 +1,28 @@ +package duke.dukeutility.definition; + +public class CommandPromptsAndOptions { + public static final String PROMPT_ADD_TODO = "todo "; + + public static final String PROMPT_ADD_DEADLINE = "deadline "; + public static final String ADD_DEADLINE_DEADLINE_DELIMITER = " /by "; + + public static final String PROMPT_ADD_EVENT = "event "; + public static final String ADD_EVENT_SCHEDULE_DELIMITER = " /at "; + + public static final String PROMPT_LIST = "list"; + public static final String PROMPT_STATISTICS_ALL = "stats:all"; + public static final String PROMPT_LIST_ONE = "see "; + public static final String PROMPT_PROJECTION_ALL = "projection "; + public static final String PROMPT_PROJECTION_NOT_DONE = "projection:yet "; + public static final String PROMPT_UPDATE_DONE = "done "; + public static final String PROMPT_UPDATE_NOT_DONE = "undone "; + + public static final String PROMPT_EXIT_LOOP = "bye"; + + public static final String PROMPT_DELETE_TASK = "delete "; + + public static final String PROMPT_SAVE = "save"; + + public static final String PROMPT_FIND_BY_KEYWORD_DESCRIPTION = "find "; + public static final String PROMPT_SCAN_DUPLICATE_DESCRIPTION = "scan:duplicates"; +} diff --git a/src/main/java/duke/dukeutility/definition/TaskField.java b/src/main/java/duke/dukeutility/definition/TaskField.java new file mode 100644 index 00000000..3c5acbaa --- /dev/null +++ b/src/main/java/duke/dukeutility/definition/TaskField.java @@ -0,0 +1,11 @@ +package duke.dukeutility.definition; + +public class TaskField { + public static final String TASK_FIELD_TYPE = "type"; + public static final String TASK_FIELD_TASK_ID = "taskId"; + public static final String TASK_FIELD_DESCRIPTION = "description"; + public static final String TASK_FIELD_DEADLINE = "deadline"; + public static final String TASK_FIELD_FROM = "from"; + public static final String TASK_FIELD_TO = "to"; + public static final String TASK_FIELD_DONE_STATUS = "done"; +} diff --git a/src/main/java/duke/dukeutility/enums/JsonTaskType.java b/src/main/java/duke/dukeutility/enums/JsonTaskType.java new file mode 100644 index 00000000..c6a79568 --- /dev/null +++ b/src/main/java/duke/dukeutility/enums/JsonTaskType.java @@ -0,0 +1,5 @@ +package duke.dukeutility.enums; + +public enum JsonTaskType { + ToDo, Deadline, Event +} diff --git a/src/main/java/duke/dukeutility/enums/ResponseType.java b/src/main/java/duke/dukeutility/enums/ResponseType.java new file mode 100644 index 00000000..40769897 --- /dev/null +++ b/src/main/java/duke/dukeutility/enums/ResponseType.java @@ -0,0 +1,36 @@ +package duke.dukeutility.enums; + +/** + * @see duke.command.Command + */ +public enum ResponseType { + ECHO, + EXIT_LOOP, + ERROR_COMMAND_EXECUTION, + ERROR_REQUEST_INVALID_PARAMETERS, + ERROR_REQUEST_INVALID_SYNTAX, + ERROR_REQUEST_UNKNOWN, + ERROR_INVALID_READ_FILE_PATH, + EXPORT_IN_PROGRESS, + FILE_SAVED, + FILE_SAVED_IO_ERROR, + FILE_READ, + SCAN_DUPLICATE_DESCRIPTION, + TASK_CREATING_IN_PROGRESS, + TASK_CREATE_TODO, + TASK_CREATE_DEADLINE, + TASK_CREATE_EVENT, + TASK_DELETE_TASK, + TASK_LIST_ALL, + TASK_LIST_FIND, + TASK_LIST_ONE, + TASK_STATS_ALL, + TASK_PROJECTION_ALL, + TASK_PROJECTION_NOT_DONE, + TASK_IMPORT_TODO, + TASK_IMPORT_EVENT, + TASK_IMPORT_DEADLINE, + TASK_UPDATE_COMPLETE, + TASK_UPDATE_INCOMPLETE, + TASK_NOT_FOUND, +} diff --git a/src/main/java/duke/dukeutility/parser/DateParser.java b/src/main/java/duke/dukeutility/parser/DateParser.java new file mode 100644 index 00000000..3d70a9b0 --- /dev/null +++ b/src/main/java/duke/dukeutility/parser/DateParser.java @@ -0,0 +1,54 @@ +package duke.dukeutility.parser; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + +import duke.dukeexception.DukeParseDateTimeException; + +public class DateParser { + private static final String transitiveJsonAndTextPattern = "yyyy-MM-dd-HH-mm-ss"; + private static final List patterns = + List.of("yyyyMMdd", "yyyyMMdd HH:mm", DateParser.transitiveJsonAndTextPattern); + + public static String parseLocalDateTimeAsString(LocalDateTime ldt) { + return DateTimeFormatter.ofPattern(DateParser.transitiveJsonAndTextPattern).format(ldt); + } + + /** + * format string as LocalDateTime + * + * @param dateTimeString + * @return date in LocalDateTime format. + * @throws DukeParseDateTimeException Formatting failed + */ + public static LocalDateTime parseStringAsLocalDateTime(String dateTimeString) throws DukeParseDateTimeException { + + LocalDateTime ldt = null; + for (String pattern : DateParser.patterns) { + try { + ldt = LocalDateTime.parse(dateTimeString, DateTimeFormatter.ofPattern(pattern)); + } catch (Exception e) { + } + } + + try { + ldt = parseStringAsLocalDate(dateTimeString).atTime(0, 0); + } catch (Exception e) { + } + if (ldt == null) { + throw new DukeParseDateTimeException( + "Date should be of the following format: " + String.join(", ", patterns)); + } + return ldt; + } + + public static LocalDate parseStringAsLocalDate(String dateString) { + return LocalDate.parse(dateString, DateTimeFormatter.ofPattern("yyyyMMdd")); + } + + public static String prettifyLocalDateTime(LocalDateTime ldt) { + return ldt.toString().replace("T", " "); + } +} diff --git a/src/main/java/duke/dukeutility/parser/JsonTaskToObjectParser.java b/src/main/java/duke/dukeutility/parser/JsonTaskToObjectParser.java new file mode 100644 index 00000000..18de346e --- /dev/null +++ b/src/main/java/duke/dukeutility/parser/JsonTaskToObjectParser.java @@ -0,0 +1,84 @@ +package duke.dukeutility.parser; + +import static duke.dukeutility.definition.TaskField.TASK_FIELD_DEADLINE; +import static duke.dukeutility.definition.TaskField.TASK_FIELD_DESCRIPTION; +import static duke.dukeutility.definition.TaskField.TASK_FIELD_DONE_STATUS; +import static duke.dukeutility.definition.TaskField.TASK_FIELD_FROM; +import static duke.dukeutility.definition.TaskField.TASK_FIELD_TASK_ID; +import static duke.dukeutility.definition.TaskField.TASK_FIELD_TO; +import static duke.dukeutility.parser.DateParser.parseStringAsLocalDateTime; +import static duke.dukeutility.validator.JsonObjectValidator.isJsonTypeDeadline; +import static duke.dukeutility.validator.JsonObjectValidator.isJsonTypeEvent; +import static duke.dukeutility.validator.JsonObjectValidator.isJsonTypeToDo; +import static duke.dukeutility.validator.JsonObjectValidator.isNotNullJsonPropertyTaskType; + +import java.time.LocalDateTime; + +import com.google.gson.JsonObject; + +import duke.task.model.Deadline; +import duke.task.model.Event; +import duke.task.model.Task; +import duke.task.model.ToDo; + +public class JsonTaskToObjectParser extends Parser { + private JsonTaskToObjectParser() { + } + + /** + * Converts a JSON formatted task to POJO. + * + * @param jsonObj task + * @return task object + * @throws Exception if not recognised as a task + */ + public static Task jsonTaskToPojo(JsonObject jsonObj) throws Exception { + if (!isNotNullJsonPropertyTaskType(jsonObj)) { + throw new Exception("No task type"); + } else if (isJsonTypeToDo(jsonObj)) { + Integer taskId = getJsonPropertyTaskId(jsonObj); + String taskDescription = getJsonPropertyTaskDescription(jsonObj); + Boolean done = getJsonPropertyDoneStatus(jsonObj); + return new ToDo(taskDescription, taskId, done); + } else if (isJsonTypeDeadline(jsonObj)) { + Integer taskId = getJsonPropertyTaskId(jsonObj); + String taskDescription = getJsonPropertyTaskDescription(jsonObj); + Boolean done = getJsonPropertyDoneStatus(jsonObj); + LocalDateTime deadline = getJsonPropertyDeadline(jsonObj); + return new Deadline(taskDescription, deadline, taskId, done); + } else if (isJsonTypeEvent(jsonObj)) { + Integer taskId = getJsonPropertyTaskId(jsonObj); + String taskDescription = getJsonPropertyTaskDescription(jsonObj); + Boolean done = getJsonPropertyDoneStatus(jsonObj); + LocalDateTime from = getJsonPropertyFrom(jsonObj); + LocalDateTime to = getJsonPropertyTo(jsonObj); + return new Event(taskDescription, from, to, taskId, done); + } + throw new Exception("Json object not recognised as a task Object"); + } + + + public static Boolean getJsonPropertyDoneStatus(JsonObject jsonObj) { + return jsonObj.get(TASK_FIELD_DONE_STATUS).getAsBoolean(); + } + + public static String getJsonPropertyTaskDescription(JsonObject jsonObj) { + return jsonObj.get(TASK_FIELD_DESCRIPTION).getAsString(); + } + + public static LocalDateTime getJsonPropertyDeadline(JsonObject jsonObj) throws Exception { + return parseStringAsLocalDateTime(jsonObj.get(TASK_FIELD_DEADLINE).getAsString()); + } + + public static LocalDateTime getJsonPropertyFrom(JsonObject jsonObj) throws Exception { + return parseStringAsLocalDateTime(jsonObj.get(TASK_FIELD_FROM).getAsString()); + } + + public static LocalDateTime getJsonPropertyTo(JsonObject jsonObj) throws Exception { + return parseStringAsLocalDateTime(jsonObj.get(TASK_FIELD_TO).getAsString()); + } + + public static Integer getJsonPropertyTaskId(JsonObject jsonObj) { + return jsonObj.get(TASK_FIELD_TASK_ID).getAsInt(); + } +} diff --git a/src/main/java/duke/dukeutility/parser/Parser.java b/src/main/java/duke/dukeutility/parser/Parser.java new file mode 100644 index 00000000..1cd73ac3 --- /dev/null +++ b/src/main/java/duke/dukeutility/parser/Parser.java @@ -0,0 +1,6 @@ +package duke.dukeutility.parser; + +public class Parser { + protected Parser() { + } +} diff --git a/src/main/java/duke/dukeutility/parser/PathParser.java b/src/main/java/duke/dukeutility/parser/PathParser.java new file mode 100644 index 00000000..2aef343f --- /dev/null +++ b/src/main/java/duke/dukeutility/parser/PathParser.java @@ -0,0 +1,18 @@ +package duke.dukeutility.parser; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public class PathParser extends Parser { + + /** + * Helper for parsing string as path + */ + public static Path stringToPath(String pathString) { + try { + return Paths.get(pathString); + } catch (Exception e) { + return null; + } + } +} diff --git a/src/main/java/duke/dukeutility/parser/TaskToJsonParser.java b/src/main/java/duke/dukeutility/parser/TaskToJsonParser.java new file mode 100644 index 00000000..221f67d0 --- /dev/null +++ b/src/main/java/duke/dukeutility/parser/TaskToJsonParser.java @@ -0,0 +1,73 @@ +package duke.dukeutility.parser; + +import static duke.dukeutility.definition.TaskField.TASK_FIELD_DEADLINE; +import static duke.dukeutility.definition.TaskField.TASK_FIELD_DESCRIPTION; +import static duke.dukeutility.definition.TaskField.TASK_FIELD_DONE_STATUS; +import static duke.dukeutility.definition.TaskField.TASK_FIELD_FROM; +import static duke.dukeutility.definition.TaskField.TASK_FIELD_TASK_ID; +import static duke.dukeutility.definition.TaskField.TASK_FIELD_TO; +import static duke.dukeutility.definition.TaskField.TASK_FIELD_TYPE; +import static duke.dukeutility.parser.DateParser.parseLocalDateTimeAsString; + +import com.google.gson.JsonObject; + +import duke.dukeutility.enums.JsonTaskType; +import duke.task.model.Deadline; +import duke.task.model.Event; +import duke.task.model.ToDo; + + +public class TaskToJsonParser extends Parser { + + private TaskToJsonParser() { + } + + /** + * Format event as JSON + * + * @param event + * @return event as JSON + */ + public static JsonObject parseEventAsJson(Event event) { + JsonObject obj = new JsonObject(); + obj.addProperty(TASK_FIELD_TYPE, JsonTaskType.Event.toString()); + obj.addProperty(TASK_FIELD_TASK_ID, event.getTaskId()); + obj.addProperty(TASK_FIELD_DESCRIPTION, event.getTaskDescription()); + obj.addProperty(TASK_FIELD_DONE_STATUS, event.isDone()); + obj.addProperty(TASK_FIELD_FROM, parseLocalDateTimeAsString(event.getFrom())); + obj.addProperty(TASK_FIELD_TO, parseLocalDateTimeAsString(event.getTo())); + return obj; + } + + + /** + * Format deadline as JSON + * + * @param deadline + * @return deadline as JSON + */ + public static JsonObject parseDeadlineAsJson(Deadline deadline) { + JsonObject obj = new JsonObject(); + obj.addProperty(TASK_FIELD_TYPE, JsonTaskType.Deadline.toString()); + obj.addProperty(TASK_FIELD_TASK_ID, deadline.getTaskId()); + obj.addProperty(TASK_FIELD_DESCRIPTION, deadline.getTaskDescription()); + obj.addProperty(TASK_FIELD_DONE_STATUS, deadline.isDone()); + obj.addProperty(TASK_FIELD_DEADLINE, parseLocalDateTimeAsString(deadline.getDeadline())); + return obj; + } + + /** + * Format toDo as JSON + * + * @param toDo + * @return toDo as JSON + */ + public static JsonObject parseToDoAsJson(ToDo toDo) { + JsonObject obj = new JsonObject(); + obj.addProperty(TASK_FIELD_TYPE, JsonTaskType.ToDo.toString()); + obj.addProperty(TASK_FIELD_TASK_ID, toDo.getTaskId()); + obj.addProperty(TASK_FIELD_DESCRIPTION, toDo.getTaskDescription()); + obj.addProperty(TASK_FIELD_DONE_STATUS, toDo.isDone()); + return obj; + } +} diff --git a/src/main/java/duke/dukeutility/prettify/Prettify.java b/src/main/java/duke/dukeutility/prettify/Prettify.java new file mode 100644 index 00000000..e5f09e41 --- /dev/null +++ b/src/main/java/duke/dukeutility/prettify/Prettify.java @@ -0,0 +1,216 @@ +package duke.dukeutility.prettify; + +import static duke.dukeutility.Helper.buildString; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import duke.TaskManager; +import duke.task.model.Deadline; +import duke.task.model.Event; +import duke.task.model.Task; +import duke.task.model.ToDo; + +public class Prettify { + + private static char getTaskTypeSymbol(Task t) { + return t.getClass().getSimpleName().charAt(0); + } + + private static String fillCell(String value, Integer lengthColMax) { + int lengthValue = value.length(); + int lengthPad = lengthColMax - lengthValue; + return String.format("%s%s", value, + String.format("%" + (lengthPad > 0 ? lengthPad : "") + "s", (lengthPad > 0 ? " " : ""))); + } + + /** + * Generates All Tasks as String + * Column widths are inherited from header columns. + * + * @return String + */ + public static String prettifyTaskMgr(TaskManager taskMgr) { + ArrayList tl = taskMgr.getAllAsArray(); + return prettifyTasks(tl); + + } + + /** + * Present an array of tasks. + * + * @param tl + * @return + */ + public static String prettifyTasks(ArrayList tl) { + + StringBuilder generating = new StringBuilder(); + // Title + int taskQty = tl.size(); + generating.append((taskQty + " task" + (taskQty > 1 ? "s" : "") + " in list" + System.lineSeparator())); + + // Header Values + + String headerId = String.format(" %-4s ", "id#"); + String headerDoneStatus = "Done "; + String headerTaskType = "Type "; + String headerDescription = "Task Description "; + String headerChronology = "Chronology"; + + generating.append(buildString(headerId, headerDoneStatus, headerTaskType, headerDescription, headerChronology, + System.lineSeparator())); + + /* column width references */ + + int lengthColId = headerId.length(); + int lengthColTaskType = headerTaskType.length(); + int lengthColDoneStatus = headerDoneStatus.length(); + int lengthColDesc = headerDescription.length(); + + // Body Values + for (Task t : tl) { + // fill column Id + String idValue = String.format("%4d", t.getTaskId()).replace(" ", "0"); + String id = fillCell(idValue, lengthColId); + // fill column Done Status + String doneStatusValue = String.format("[%s]", t.isDone() ? "X" : " "); + String doneStatus = fillCell(doneStatusValue, lengthColDoneStatus); + // fill column Task Type Status + String taskTypeSymbolValue = String.format("[%s]", getTaskTypeSymbol(t)); + String type = fillCell(taskTypeSymbolValue, lengthColTaskType); + // fill column Description + String descValueLong = t.getTaskDescription(); + String descValue = descValueLong.substring(0, Math.min(descValueLong.length(), lengthColDesc - 1)); + String desc = fillCell(descValue, lengthColDesc); + // fill column Chronology + String chronology = t.getChronologyString(); + + String line = buildString(id, doneStatus, type, desc, chronology, System.lineSeparator()); + generating.append(line); + } + return generating.toString(); + } + + public static String getStatisticsAll(ArrayList tasks) { + // Tabulate + int col = 0; + int colDone = col++; + int colNotDone = col++; + int row = 0; + int rowToDo = row++; + int rowDeadline = row++; + int rowEvent = row++; + + + Integer[][] stats = new Integer[col][row]; + for (Integer[] ints : stats) { + Arrays.fill(ints, 0); + } + for (Task t : tasks) { + + Integer thisRow = null; + if (t instanceof ToDo) { + thisRow = rowToDo; + } else if (t instanceof Deadline) { + thisRow = rowDeadline; + } else if (t instanceof Event) { + thisRow = rowEvent; + } + Integer thisCol = null; + if (t.isDone()) { + thisCol = colDone; + } else { + thisCol = colNotDone; + } + assert (thisCol != null); + assert (thisRow != null); + if (thisCol != null && thisRow != null) { + stats[thisCol][thisRow]++; + } + } + int rowHeader = row++; + int colTaskType = col++; + String[][] table = new String[col][row]; + for (String[] tableRow : table) { + Arrays.fill(tableRow, ""); + } + table[0][rowHeader] = "Task Type "; + table[0][rowToDo] = "To Do"; + table[0][rowDeadline] = "Deadline"; + table[0][rowEvent] = "Event"; + + table[2][rowHeader] = "Incomplete "; + table[2][rowToDo] = stats[colNotDone][rowToDo].toString(); + table[2][rowDeadline] = stats[colNotDone][rowDeadline].toString(); + table[2][rowEvent] = stats[colNotDone][rowEvent].toString(); + + table[1][rowHeader] = "Complete "; + table[1][rowToDo] = stats[colDone][rowToDo].toString(); + table[1][rowDeadline] = stats[colDone][rowDeadline].toString(); + table[1][rowEvent] = stats[colDone][rowEvent].toString(); + + + int[] colLength = new int[col]; + + colLength[colTaskType] = getMaxLength(table[colTaskType]); + colLength[colNotDone] = getMaxLength(table[colNotDone]); + colLength[colDone] = getMaxLength(table[colDone]); + + + for (int c = 0; c < col; c++) { + for (int r = 0; r < row; r++) { + table[c][r] = fillCell(table[c][r], colLength[c]); + } + } + StringBuilder lines = new StringBuilder(); + + lines.append(String.join("", getRowValue(table, rowHeader)) + System.lineSeparator()); + lines.append(String.join("", getRowValue(table, rowToDo)) + System.lineSeparator()); + lines.append(String.join("", getRowValue(table, rowDeadline)) + System.lineSeparator()); + lines.append(String.join("", getRowValue(table, rowEvent)) + System.lineSeparator()); + return lines.toString(); + } + + private static String getRowValue(String[][] table, int r) { + StringBuilder result = new StringBuilder(); + for (int c = 0; c < table.length; c++) { + result.append(table[c][r]); + } + return result.toString(); + } + + private static int getMaxLength(String[] strings) { + int maxLength = 0; + for (String s : strings) { + maxLength = Math.max(maxLength, s.length()); + } + return maxLength; + + } + + public static String getScanDuplicatesDescription(HashMap> descMap) { + + + StringBuilder result = new StringBuilder(); + + descMap.forEach((desc, dupe) -> result.append( + "\"" + desc + "\"" + ": " + stringifyTaskIdAndType(dupe) + System.lineSeparator())); + return result.toString(); + } + + public static String stringifyTaskIdAndType(ArrayList tasks) { + StringBuilder result = new StringBuilder(); + result.append("["); + int size = tasks.size(); + for (int i = 0; i < size; i++) { + Task task = tasks.get(i); + result.append(task.getTaskId().toString() + " (" + getTaskTypeSymbol(task) + ")"); + if (i != size - 1) { + result.append(", "); + } + } + result.append("]"); + return result.toString(); + } +} diff --git a/src/main/java/duke/dukeutility/validator/JsonObjectValidator.java b/src/main/java/duke/dukeutility/validator/JsonObjectValidator.java new file mode 100644 index 00000000..1805846d --- /dev/null +++ b/src/main/java/duke/dukeutility/validator/JsonObjectValidator.java @@ -0,0 +1,37 @@ +package duke.dukeutility.validator; + +import static duke.dukeutility.definition.TaskField.TASK_FIELD_TYPE; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import duke.dukeutility.enums.JsonTaskType; + +public class JsonObjectValidator { + + public static Boolean isNotNullJsonPropertyTaskType(JsonObject jsonObj) { + return jsonObj.has(TASK_FIELD_TYPE); + } + + private static Boolean isJsonTypeOf(JsonObject jsonObj, JsonTaskType enumVal) { + JsonElement elementTaskType = jsonObj.get(TASK_FIELD_TYPE); + String taskType = elementTaskType.getAsString(); + try { + return JsonTaskType.valueOf(taskType) == enumVal; + } catch (Exception e) { + return false; + } + } + + public static Boolean isJsonTypeToDo(JsonObject jsonObj) { + return isJsonTypeOf(jsonObj, JsonTaskType.ToDo); + } + + public static Boolean isJsonTypeDeadline(JsonObject jsonObj) { + return isJsonTypeOf(jsonObj, JsonTaskType.Deadline); + } + + public static Boolean isJsonTypeEvent(JsonObject jsonObj) { + return isJsonTypeOf(jsonObj, JsonTaskType.Event); + } +} diff --git a/src/main/java/duke/dukeutility/validator/StringValidator.java b/src/main/java/duke/dukeutility/validator/StringValidator.java new file mode 100644 index 00000000..0fbaeb24 --- /dev/null +++ b/src/main/java/duke/dukeutility/validator/StringValidator.java @@ -0,0 +1,17 @@ +package duke.dukeutility.validator; + + +public class StringValidator { + + public static Boolean isSubstring(String sentence, String keyword) { + + String[] words = sentence.split("\\s+"); + for (String word : words) { + if (word.equals(keyword)) { + return true; + } + } + return false; + + } +} diff --git a/src/main/java/duke/dukeutility/validator/TextCommandValidator.java b/src/main/java/duke/dukeutility/validator/TextCommandValidator.java new file mode 100644 index 00000000..4bf441f4 --- /dev/null +++ b/src/main/java/duke/dukeutility/validator/TextCommandValidator.java @@ -0,0 +1,89 @@ +package duke.dukeutility.validator; + +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_ADD_DEADLINE; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_ADD_EVENT; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_ADD_TODO; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_DELETE_TASK; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_EXIT_LOOP; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_FIND_BY_KEYWORD_DESCRIPTION; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_LIST; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_LIST_ONE; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_PROJECTION_ALL; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_PROJECTION_NOT_DONE; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_SAVE; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_SCAN_DUPLICATE_DESCRIPTION; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_STATISTICS_ALL; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_UPDATE_DONE; +import static duke.dukeutility.definition.CommandPromptsAndOptions.PROMPT_UPDATE_NOT_DONE; + +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Text Command Validation methods + */ +public class TextCommandValidator { + public static Boolean isRequestExitLoop(String text) { + return text.equals(PROMPT_EXIT_LOOP); + } + + public static Boolean isRequestList(String text) { + return text.equals(PROMPT_LIST); + } + + public static Boolean isRequestStatisticsAll(String text) { + return text.equals(PROMPT_STATISTICS_ALL); + } + + public static Boolean isRequestMarkTaskAsDone(String text) { + return text.startsWith(PROMPT_UPDATE_DONE); + } + + public static Boolean isRequestMarkTaskAsIncomplete(String text) { + return text.startsWith(PROMPT_UPDATE_NOT_DONE); + } + + public static Boolean isRequestScanDuplicates(String text) { + return text.equals(PROMPT_SCAN_DUPLICATE_DESCRIPTION); + } + + public static Boolean isRequestAddToDo(String text) { + return text.startsWith(PROMPT_ADD_TODO); + } + + public static Boolean isRequestAddDeadline(String text) { + return text.startsWith(PROMPT_ADD_DEADLINE); + } + + public static Boolean isRequestAddEvent(String text) { + return text.startsWith(PROMPT_ADD_EVENT); + } + + public static Boolean isRequestDeleteTask(String text) { + return text.startsWith(PROMPT_DELETE_TASK); + } + + public static Boolean isRequestFind(String text) { + return text.startsWith(PROMPT_FIND_BY_KEYWORD_DESCRIPTION); + } + + public static Boolean isRequestProjectionAll(String text) { + return text.startsWith(PROMPT_PROJECTION_ALL); + } + + public static Boolean isRequestProjectionNotDone(String text) { + return text.startsWith(PROMPT_PROJECTION_NOT_DONE); + } + + public static Boolean isParentDirectoryValid(Path p) { + return Files.exists(p.getParent()); + } + + public static Boolean isRequestSave(String text) { + return text.equals(PROMPT_SAVE); + } + + public static Boolean isRequestSee(String text) { + return text.startsWith(PROMPT_LIST_ONE); + } +} diff --git a/src/main/java/duke/task/TaskComparator.java b/src/main/java/duke/task/TaskComparator.java new file mode 100644 index 00000000..35f966b5 --- /dev/null +++ b/src/main/java/duke/task/TaskComparator.java @@ -0,0 +1,60 @@ +package duke.task; + +import java.time.LocalDate; + +import duke.task.model.Deadline; +import duke.task.model.Event; +import duke.task.model.Task; + +public class TaskComparator { + + public static Boolean isSameDay(Task t, LocalDate targetDay) { + LocalDate tDate = null; + if (t instanceof Deadline) { + tDate = ((Deadline) t).getDeadline().toLocalDate(); + } else if (t instanceof Event) { + tDate = ((Event) t).getFrom().toLocalDate(); + } + return targetDay.equals(tDate); + } + + + public static LocalDate getStartDate(Task t) { + LocalDate tDate = null; + if (t instanceof Deadline) { + tDate = ((Deadline) t).getDeadline().toLocalDate(); + } else if (t instanceof Event) { + tDate = ((Event) t).getFrom().toLocalDate(); + } + return tDate; + } + + public static Boolean isTaskWithinNextDays(Task t, Integer period) { + LocalDate today = LocalDate.now(); + LocalDate finalDay = today.plusDays(period); + LocalDate tDate = getStartDate(t); + if (tDate == null) { + return false; + } + return !(tDate.isBefore(today) || tDate.isAfter(finalDay)); + } + + public static int compareTaskDate(Task a, Task b) { + LocalDate aDate = getStartDate(a); + LocalDate bDate = getStartDate(b); + + if (aDate == null) { + aDate = LocalDate.MIN; + } + if (bDate == null) { + bDate = LocalDate.MIN; + } + if (aDate.isBefore(bDate)) { + return -1; + } else if (bDate.isAfter(aDate)) { + return 1; + } else { + return 0; + } + } +} diff --git a/src/main/java/duke/task/aggregator/TaskList.java b/src/main/java/duke/task/aggregator/TaskList.java new file mode 100644 index 00000000..cd9fee1a --- /dev/null +++ b/src/main/java/duke/task/aggregator/TaskList.java @@ -0,0 +1,57 @@ +package duke.task.aggregator; + +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; + +import duke.task.model.Task; + + +public class TaskList { + private final ConcurrentHashMap container = new ConcurrentHashMap<>(); + + public TaskList() { + } + + + public ConcurrentHashMap getContainer() { + return this.container; + } + + + public ArrayList getAllAsArray() { + return new ArrayList<>(this.getContainer().values()); + } + + private Integer getTaskKey(Task task) { + return task.getTaskId(); + } + + public void addTask(Task t) { + this.container.put(this.getTaskKey(t), t); + } + + public Integer getSize() { + return this.getContainer().size(); + } + + public Boolean containsKey(Integer key) { + return this.getContainer().containsKey(key); + } + + public Task getTaskById(Integer taskId) { + return this.getContainer().get(taskId); + } + + /** + * Remove a task in a container by task id + * + * @param taskId + * @return + */ + public Task removeTaskById(Integer taskId) { + Task task = this.getTaskById(taskId); + this.getContainer().remove(taskId); + return task; + } + +} diff --git a/src/main/java/duke/task/model/Deadline.java b/src/main/java/duke/task/model/Deadline.java new file mode 100644 index 00000000..013a9ff3 --- /dev/null +++ b/src/main/java/duke/task/model/Deadline.java @@ -0,0 +1,38 @@ +package duke.task.model; + +import static duke.dukeutility.parser.DateParser.prettifyLocalDateTime; + +import java.time.LocalDateTime; + +public final class Deadline extends Task { + private LocalDateTime deadline; + + /** + * A deadline + * + * @param taskDescription + * @param deadline + * @param taskId + * @param done + */ + public Deadline(String taskDescription, LocalDateTime deadline, Integer taskId, Boolean done) { + super(taskDescription, taskId, done); + this.setDeadline(deadline); + } + + private Deadline() { + } + + private void setDeadline(LocalDateTime deadline) { + this.deadline = deadline; + } + + public LocalDateTime getDeadline() { + return this.deadline; + } + + + public String getChronologyString() { + return "By: " + prettifyLocalDateTime(this.getDeadline()); + } +} diff --git a/src/main/java/duke/task/model/Event.java b/src/main/java/duke/task/model/Event.java new file mode 100644 index 00000000..c43715fc --- /dev/null +++ b/src/main/java/duke/task/model/Event.java @@ -0,0 +1,52 @@ +package duke.task.model; + +import static duke.dukeutility.parser.DateParser.prettifyLocalDateTime; + +import java.time.LocalDateTime; + +public final class Event extends Task { + private LocalDateTime from; + private LocalDateTime to; + + /** + * An event. + * + * @param taskDescription + * @param from + * @param to + * @param taskId + * @param done + */ + public Event(String taskDescription, LocalDateTime from, LocalDateTime to, Integer taskId, Boolean done) { + super(taskDescription, taskId, done); + this.setFrom(from); + this.setTo(to); + } + + private Event() { + } + + public LocalDateTime getTo() { + return this.to; + } + + public LocalDateTime getFrom() { + return this.from; + } + + private void setFrom(LocalDateTime from) { + this.from = from; + } + + private void setTo(LocalDateTime to) { + this.to = to; + } + + public String getChronologyString() { + + return String.format("From: %s, To: %s", prettifyLocalDateTime(this.getFrom()), + prettifyLocalDateTime(this.getTo())); + + } + +} diff --git a/src/main/java/duke/task/model/Task.java b/src/main/java/duke/task/model/Task.java new file mode 100644 index 00000000..3696ed38 --- /dev/null +++ b/src/main/java/duke/task/model/Task.java @@ -0,0 +1,53 @@ +package duke.task.model; + + +/** + * Root class for tasks + */ +public abstract class Task implements Comparable { + + + private String taskDescription; + private Integer taskId; + private Boolean done; + + protected Task() { + } + + protected Task(String taskDescription, Integer taskId, Boolean done) { + this.setTaskDescription(taskDescription); + this.setTaskId(taskId); + this.setDoneStatus(done); + } + + public String getTaskDescription() { + return this.taskDescription; + } + + public void setTaskDescription(String taskDescription) { + this.taskDescription = taskDescription; + } + + public Integer getTaskId() { + return this.taskId; + } + + public void setTaskId(Integer taskId) { + this.taskId = taskId; + } + + public Boolean isDone() { + return this.done; + } + + public Boolean setDoneStatus(Boolean next) { + this.done = next; + return this.done; + } + + public abstract String getChronologyString(); + + public int compareTo(Task u) { + return Integer.compare(this.getTaskId(), u.getTaskId()); + } +} diff --git a/src/main/java/duke/task/model/ToDo.java b/src/main/java/duke/task/model/ToDo.java new file mode 100644 index 00000000..d2d104fb --- /dev/null +++ b/src/main/java/duke/task/model/ToDo.java @@ -0,0 +1,15 @@ +package duke.task.model; + + +public final class ToDo extends Task { + private ToDo() { + } + + public ToDo(String taskDescription, Integer taskId, Boolean done) { + super(taskDescription, taskId, done); + } + + public String getChronologyString() { + return ("-"); + } +} diff --git a/src/test/java/duke/integrationtest/TestIoAddTasks.java b/src/test/java/duke/integrationtest/TestIoAddTasks.java new file mode 100644 index 00000000..75d5fac8 --- /dev/null +++ b/src/test/java/duke/integrationtest/TestIoAddTasks.java @@ -0,0 +1,280 @@ +package duke.integrationtest; + +import static duke.testhelper.help.Builder.buildCommandInputStream; +import static duke.testhelper.help.Builder.buildExpectedResponse; +import static duke.testhelper.help.Builder.buildString; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputAddedDeadline; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputAddedEvent; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputAddedToDo; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputBeginInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputEntry; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputExitInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputImportAttempt; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputList; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputReadPathNotFound; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputTerminate; +import static duke.testhelper.help.codeundertest.PrettifyUnderTest.getExpectedTaskList; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandExit; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandLineAddDeadline; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandLineAddEvent; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandLineAddToDo; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandList; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksImportTestPathString; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksTestExportPathString; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import duke.FileResourceManager; +import duke.Main; +import duke.TaskManager; +import duke.mock.mocktask.MockDeadline; +import duke.mock.mocktask.MockEvent; +import duke.mock.mocktask.MockTask; +import duke.mock.mocktask.MockToDo; +import duke.testhelper.TestStream; +import duke.testhelper.help.codeundertest.ParserUnderTest; + +public class TestIoAddTasks extends TestStream { + + @Test + public void greet_addToDoList_exit() throws Exception { + + // Arrange Input + + /* + * Commands executed: + * + * add task 0 with task description + * list + * exit loop + */ + + + String taskDesc0 = "taskDesc abc"; + + String storeCommand0 = generateTextCommandLineAddToDo(taskDesc0); + String listCommand = generateTextCommandList(); + String exitCommand = generateTextCommandExit(); + + System.setIn(buildCommandInputStream(storeCommand0, listCommand, exitCommand)); + + // Arrange Expected Output + /* + * Should display: + * Entry Message + * Input Loop Message + * "Added todo" message + * tabled tasks list + * exit loop + * terminate + */ + + MockToDo expectedToDo1 = new MockToDo(taskDesc0, 0, false); + TaskManager tm = new TaskManager(); + FileResourceManager frm = + new FileResourceManager(getDefaultTasksTestExportPathString(), getDefaultTasksImportTestPathString()); + + MockTask[] mockTasks = {expectedToDo1}; + String out0 = getExpectedOutputEntry(); + String out1 = getExpectedOutputImportAttempt(frm.getImportPath()); + String out2 = getExpectedOutputReadPathNotFound(); + String out3 = getExpectedOutputBeginInputLoop(); + String out4 = getExpectedOutputAddedToDo(taskDesc0, 0); + String out5 = getExpectedOutputList(getExpectedTaskList(mockTasks)); + String out6 = getExpectedOutputExitInputLoop(); + String out7 = getExpectedOutputTerminate(); + String expectedOutputResponse = buildExpectedResponse(out0, out1, out2, out3, out4, out5, out6, out7); + + // Act + Main.run(this.getPrintStream(), tm, frm); + // Assert + assertEquals(expectedOutputResponse, this.getOutput()); + } + + @Test + public void greet_addEventList_exit() throws Exception { + + /* Arrange Input + * Commands executed: + * + * add event 0 with task description + * list + * exit loop + */ + + String taskDesc0 = "event_desc_abc asfasfasf"; + String fromDateString = "19990101"; + String toDateString = "19990202"; + + String storeEventCommand0 = + generateTextCommandLineAddEvent(taskDesc0, + fromDateString, toDateString); + + String listCommand = generateTextCommandList(); + String exitCommand = generateTextCommandExit(); + + System.setIn(buildCommandInputStream(storeEventCommand0, listCommand, exitCommand)); + + /* + * Arrange Output + * Should display: + * Entry Message + * Input Loop Message + * "Added event" message + * tabled tasks list + * exit loop + * terminate + */ + + MockEvent expectedEvent = + new MockEvent(taskDesc0, 0, false, ParserUnderTest.parseStringAsLocalDateTime(fromDateString), + ParserUnderTest.parseStringAsLocalDateTime(toDateString)); + MockTask[] mockEvents = {expectedEvent}; + + TaskManager tm = new TaskManager(); + FileResourceManager frm = + new FileResourceManager(getDefaultTasksTestExportPathString(), getDefaultTasksImportTestPathString()); + + String out1 = getExpectedOutputEntry(); + String out2 = getExpectedOutputImportAttempt(frm.getImportPath()); + String out3 = getExpectedOutputReadPathNotFound(); + String out4 = getExpectedOutputBeginInputLoop(); + String out5 = getExpectedOutputAddedEvent(expectedEvent.getDesc(), 0); + String out6 = getExpectedOutputList(getExpectedTaskList(mockEvents)); + String out7 = getExpectedOutputExitInputLoop(); + String out8 = getExpectedOutputTerminate(); + + String expectedOutputResponse = buildString(out1, out2, out3, out4, out5, out6, out7, out8); + + Main.run(this.getPrintStream(), tm, frm); + assertEquals(expectedOutputResponse, this.getOutput()); + } + + @Test + public void greet_addDeadlineList_exit() throws Exception { + + /* Arrange Input + * Commands executed: + * + * add deadline 0 with task description + * list + * exit loop + */ + + + String taskDesc0 = "deadline asfasfasf"; + String byDateString = "20200101"; + + String storeDeadlineCommand0 = + generateTextCommandLineAddDeadline(taskDesc0, + byDateString); + String listCommand = generateTextCommandList(); + String exitCommand = generateTextCommandExit(); + System.setIn(buildCommandInputStream(storeDeadlineCommand0, listCommand, exitCommand)); + + /* Arrange Expected Output + * + * Should display: + * Entry Message + * Input Loop Message + * "Added event" message + * tabled tasks list + * exit loop + * terminate + */ + + TaskManager tm = new TaskManager(); + FileResourceManager frm = + new FileResourceManager(getDefaultTasksTestExportPathString(), getDefaultTasksImportTestPathString()); + + MockDeadline expectedEvent = + new MockDeadline(taskDesc0, 0, false, ParserUnderTest.parseStringAsLocalDateTime(byDateString)); + MockTask[] mockDeadlines = {expectedEvent}; + + String out0 = getExpectedOutputEntry(); + String out1 = getExpectedOutputImportAttempt(frm.getImportPath()); + String out2 = getExpectedOutputReadPathNotFound(); + String out3 = getExpectedOutputBeginInputLoop(); + String out4 = getExpectedOutputAddedDeadline(taskDesc0, 0); + String out5 = getExpectedOutputList(getExpectedTaskList(mockDeadlines)); + String out6 = getExpectedOutputExitInputLoop(); + String out7 = getExpectedOutputTerminate(); + + String expectedOutputResponse = buildExpectedResponse(out0, out1, out2, out3, out4, out5, out6, out7); + Main.run(this.getPrintStream(), tm, frm); + assertEquals(expectedOutputResponse, this.getOutput()); + } + + @Test + public void greet_addEachTaskType_list() throws Exception { + // Arrange Input + + String task0ToDoDescription = "todo_desc asfasfasf"; + String task1DeadlineDescription = "deadline_desc ndfrgndfndfn"; + String task1DeadlineByString = "20010101"; + String task2EventDescription = "event_desc_abc 213t12 3b52"; + String task2EventFromDateString = "20200102"; + String task2EventToDateString = "20200102"; + + String storeToDoCommand0 = generateTextCommandLineAddToDo(task0ToDoDescription); + String storeDeadlineCommand1 = + generateTextCommandLineAddDeadline(task1DeadlineDescription, + task1DeadlineByString); + String storeEventCommand2 = + generateTextCommandLineAddEvent(task2EventDescription, + task2EventFromDateString, task2EventToDateString); + + String listCommand = generateTextCommandList(); + String exitCommand = generateTextCommandExit(); + + System.setIn(buildCommandInputStream(storeToDoCommand0, storeDeadlineCommand1, storeEventCommand2, listCommand, + exitCommand)); + + + /* + * Should display: + * Entry Message + * Input Loop Message + * "Added todo" message + * "Added deadline" message + * "Added event" message + * tabled tasks list + * exit loop + * terminate + */ + + + MockToDo expectedToDo = new MockToDo(task0ToDoDescription, 0, false); + + MockDeadline expectedDeadline = new MockDeadline(task1DeadlineDescription, 1, false, + ParserUnderTest.parseStringAsLocalDateTime(task1DeadlineByString)); + MockEvent expectedEvent = new MockEvent(task2EventDescription, 2, false, + ParserUnderTest.parseStringAsLocalDateTime(task2EventFromDateString), + ParserUnderTest.parseStringAsLocalDateTime(task2EventToDateString)); + + MockTask[] mockEvents = {expectedToDo, expectedDeadline, expectedEvent}; + + TaskManager tm = new TaskManager(); + FileResourceManager frm = + new FileResourceManager(getDefaultTasksTestExportPathString(), getDefaultTasksImportTestPathString()); + + String out0 = getExpectedOutputEntry(); + String out1 = getExpectedOutputImportAttempt(frm.getImportPath()); + String out2 = getExpectedOutputReadPathNotFound(); + String out3 = getExpectedOutputBeginInputLoop(); + String out4 = getExpectedOutputAddedToDo(task0ToDoDescription, 0); + String out5 = getExpectedOutputAddedDeadline(task1DeadlineDescription, 1); + String out6 = getExpectedOutputAddedEvent(task2EventDescription, 2); + String out7 = getExpectedOutputList(getExpectedTaskList(mockEvents)); + String out8 = getExpectedOutputExitInputLoop(); + String out9 = getExpectedOutputTerminate(); + + String expectedOutputResponse = + buildExpectedResponse(out0, out1, out2, out3, out4, out5, out6, out7, out8, out9); + Main.run(this.getPrintStream(), tm, frm); + + assertEquals(expectedOutputResponse, this.getOutput()); + + } +} diff --git a/src/test/java/duke/integrationtest/TestIoFind.java b/src/test/java/duke/integrationtest/TestIoFind.java new file mode 100644 index 00000000..d480d207 --- /dev/null +++ b/src/test/java/duke/integrationtest/TestIoFind.java @@ -0,0 +1,192 @@ +package duke.integrationtest; + + +import static duke.testhelper.help.Builder.buildCommandInputStream; +import static duke.testhelper.help.Builder.buildExpectedResponse; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputAddedToDo; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputBeginInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputEntry; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputExitInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputImportAttempt; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputListTasksWithKeywordDescription; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputReadPathNotFound; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputTerminate; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getMsgUnderTestErrorSpacedKeyword; +import static duke.testhelper.help.codeundertest.PrettifyUnderTest.getExpectedTaskList; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.PROMPT_UNDER_TEST_FIND; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandExit; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandFindKeywordInDescription; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandLineAddToDo; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksImportTestPathString; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksTestExportPathString; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +import duke.FileResourceManager; +import duke.Main; +import duke.TaskManager; +import duke.mock.mocktask.MockTask; +import duke.mock.mocktask.MockToDo; +import duke.testhelper.TestStream; + +public class TestIoFind extends TestStream { + /** + * Test find routine. + */ + @Test + public void greet_addToDoListTasksWithKeywordSave_exit() { + + String keyword = "MAGIK"; + String taskDesc0 = "nons afasf09qhy2gr"; + String taskDesc1 = "aasfg " + keyword + " c124124"; + String taskDesc2 = "aasfg " + keyword.toLowerCase() + " negdndetnjd"; + String store0Command = generateTextCommandLineAddToDo(taskDesc0); + String store1Command = generateTextCommandLineAddToDo(taskDesc1); + String store2Command = generateTextCommandLineAddToDo(taskDesc2); + String findCommand = generateTextCommandFindKeywordInDescription(PROMPT_UNDER_TEST_FIND, keyword); + String exitCommand = generateTextCommandExit(); + + System.setIn(buildCommandInputStream(store0Command, store1Command, store2Command, findCommand, exitCommand)); + + /* + * Should display: + * Entry Message + * Input Loop Message + * "Added todo0" message + * "Added todo0" message + * queried tasks list + * exit loop + * terminate + */ + + MockToDo expectedTask1 = new MockToDo(taskDesc1, 1, false); + MockToDo expectedTask2 = new MockToDo(taskDesc2, 2, false); + + MockTask[] mockTasks = {expectedTask1, expectedTask2}; // only task 1 should be displayed after query + TaskManager tm = new TaskManager(); + + String importPathString = getDefaultTasksTestExportPathString(); + String exportPathString = getDefaultTasksImportTestPathString(); + FileResourceManager frm = new FileResourceManager(importPathString, exportPathString); + + String out0 = (getExpectedOutputEntry()); + String out1 = (getExpectedOutputImportAttempt(frm.getImportPath())); + String out2 = (getExpectedOutputReadPathNotFound()); + String out3 = (getExpectedOutputBeginInputLoop()); + String out4 = (getExpectedOutputAddedToDo(taskDesc0, 0)); + String out5 = (getExpectedOutputAddedToDo(taskDesc1, 1)); + String out6 = (getExpectedOutputAddedToDo(taskDesc2, 2)); + String out7 = (getExpectedOutputListTasksWithKeywordDescription(getExpectedTaskList(mockTasks), keyword)); + String out8 = (getExpectedOutputExitInputLoop()); + String out9 = (getExpectedOutputTerminate()); + String expectedOutputResponse = + buildExpectedResponse(out0, out1, out2, out3, out4, out5, out6, out7, out8, out9); + + try { + Main.run(this.getPrintStream(), tm, frm); + } catch (Exception e) { + fail(e.toString()); + } + + assertEquals(expectedOutputResponse, this.getOutput()); + } + + /** + * Test find routine. + */ + @Test + public void greet_addToDoListTasksWithKeywordSaveExit_emptyResult() { + + String keyword = "MAGIK"; + String taskDesc0 = "nons afasf09qhy2gr"; + String store0Command = generateTextCommandLineAddToDo(taskDesc0); + String findCommand = generateTextCommandFindKeywordInDescription(PROMPT_UNDER_TEST_FIND, keyword); + String exitCommand = generateTextCommandExit(); + + System.setIn(buildCommandInputStream(store0Command, findCommand, exitCommand)); + + /* + * Should display: + * Entry Message + * Input Loop Message + * "Added todo0" message + * queried tasks list + * exit loop + * terminate + */ + + + MockTask[] mockTasks = {}; // no should be displayed after query + TaskManager tm = new TaskManager(); + FileResourceManager frm = + new FileResourceManager(getDefaultTasksTestExportPathString(), getDefaultTasksImportTestPathString()); + + String out0 = (getExpectedOutputEntry()); + String out1 = (getExpectedOutputImportAttempt(frm.getImportPath())); + String out2 = (getExpectedOutputReadPathNotFound()); + String out3 = (getExpectedOutputBeginInputLoop()); + String out4 = (getExpectedOutputAddedToDo(taskDesc0, 0)); + String out5 = (getExpectedOutputListTasksWithKeywordDescription(getExpectedTaskList(mockTasks), keyword)); + String out6 = (getExpectedOutputExitInputLoop()); + String out7 = (getExpectedOutputTerminate()); + String expectedOutputResponse = buildExpectedResponse(out0, out1, out2, out3, out4, out5, out6, out7); + + try { + Main.run(this.getPrintStream(), tm, frm); + } catch (Exception e) { + fail(e.toString()); + } + assertEquals(expectedOutputResponse, this.getOutput()); + } + + /** + * Test find routine with invalid keyword. Should not execute the search. + */ + @Test + public void greet_addToDoListTasksWithInvalidKeywordSave_exit() { + + String keyword = "MAGIK asfasf"; + String taskDesc0 = "nons afasf09qhy2gr"; + String store0Command = generateTextCommandLineAddToDo(taskDesc0); + String findCommand = generateTextCommandFindKeywordInDescription(PROMPT_UNDER_TEST_FIND, keyword); + String exitCommand = generateTextCommandExit(); + + System.setIn(buildCommandInputStream(store0Command, findCommand, exitCommand)); + + /* + * Should display: + * Entry Message + * Input Loop Message + * "Added todo0" message + * queried tasks list + * exit loop + * terminate + */ + + TaskManager tm = new TaskManager(); + FileResourceManager frm = + new FileResourceManager(getDefaultTasksTestExportPathString(), getDefaultTasksImportTestPathString()); + + String out0 = (getExpectedOutputEntry()); + String out1 = (getExpectedOutputImportAttempt(frm.getImportPath())); + String out2 = (getExpectedOutputReadPathNotFound()); + String out3 = (getExpectedOutputBeginInputLoop()); + String out4 = (getExpectedOutputAddedToDo(taskDesc0, 0)); + String out5 = (getMsgUnderTestErrorSpacedKeyword()); + String out6 = (getExpectedOutputExitInputLoop()); + String out7 = (getExpectedOutputTerminate()); + String expectedOutputResponse = buildExpectedResponse(out0, out1, out2, out3, out4, out5, out6, out7); + + try { + Main.run(this.getPrintStream(), tm, frm); + } catch (Exception e) { + fail(e.toString()); + } + + assertEquals(expectedOutputResponse, this.getOutput()); + } + + +} diff --git a/src/test/java/duke/integrationtest/TestIoList.java b/src/test/java/duke/integrationtest/TestIoList.java new file mode 100644 index 00000000..506af84c --- /dev/null +++ b/src/test/java/duke/integrationtest/TestIoList.java @@ -0,0 +1,141 @@ +package duke.integrationtest; + +import static duke.testhelper.help.Builder.buildCommandInputStream; +import static duke.testhelper.help.Builder.buildExpectedResponse; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputAddedToDo; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputBeginInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputCommandDeleted; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputEntry; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputExitInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputImportAttempt; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputList; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputReadPathNotFound; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputTerminate; +import static duke.testhelper.help.codeundertest.PrettifyUnderTest.getExpectedTaskList; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.PROMPT_UNDER_TEST_DELETE_TASK; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandDeleteTaskByTaskId; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandExit; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandLineAddToDo; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandList; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksImportTestPathString; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksTestExportPathString; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import duke.FileResourceManager; +import duke.Main; +import duke.TaskManager; +import duke.mock.mocktask.MockTask; +import duke.mock.mocktask.MockToDo; +import duke.testhelper.TestStream; + +public class TestIoList extends TestStream { + + @Test + public void greet_addThenDeleteToDoList_exit() throws Exception { + + /* Arrange Input + * Commands executed: + * + * add task 0 with task description + * delete task 0 + * list + * exit loop + */ + + + String taskDesc0 = "taskDesc abc"; + + String store0Command = generateTextCommandLineAddToDo(taskDesc0); + String delete0Command = generateTextCommandDeleteTaskByTaskId(PROMPT_UNDER_TEST_DELETE_TASK, 0); + String listCommand = generateTextCommandList(); + String exitCommand = generateTextCommandExit(); + + System.setIn(buildCommandInputStream(store0Command, delete0Command, listCommand, exitCommand)); + + /* Arrange Expected Output + * Should display: + * Entry Message + * Input Loop Message + * "Added todo" message + * tabled tasks list + * exit loop + * terminate + */ + TaskManager tm = new TaskManager(); + FileResourceManager frm = + new FileResourceManager(getDefaultTasksTestExportPathString(), getDefaultTasksImportTestPathString()); + MockTask[] mockTasks = {}; // since task is deleted + String out0 = (getExpectedOutputEntry()); + String out1 = (getExpectedOutputImportAttempt(frm.getImportPath())); + String out2 = (getExpectedOutputReadPathNotFound()); + String out3 = (getExpectedOutputBeginInputLoop()); + String out4 = (getExpectedOutputAddedToDo(taskDesc0, 0)); + String out5 = (getExpectedOutputCommandDeleted(0)); + String out6 = (getExpectedOutputList(getExpectedTaskList(mockTasks))); + String out7 = (getExpectedOutputExitInputLoop()); + String out8 = (getExpectedOutputTerminate()); + + String expectedOutputResponse = buildExpectedResponse(out0, out1, out2, out3, out4, out5, out6, out7, out8); + Main.run(this.getPrintStream(), tm, frm); + assertEquals(expectedOutputResponse, this.getOutput()); + } + + + @Test + public void greet_add2ToDosDeleteList_exit() throws Exception { + + /* + * Commands executed: + * + * add task 0 with task description + * add task 1 with task description + * delete task 0 + * list + * exit loop + */ + + String taskDesc0 = "taskDesc abc"; + String taskDesc1 = "taskDesc def"; + + String store0Command = generateTextCommandLineAddToDo(taskDesc0); + String store1Command = generateTextCommandLineAddToDo(taskDesc1); + String delete0Command = generateTextCommandDeleteTaskByTaskId(PROMPT_UNDER_TEST_DELETE_TASK, 0); + String listCommand = generateTextCommandList(); + String exitCommand = generateTextCommandExit(); + + System.setIn(buildCommandInputStream(store0Command, store1Command, delete0Command, listCommand, exitCommand)); + + /* + * Should display: + * Entry Message + * Input Loop Message + * "Added todo" message + * tabled tasks list + * exit loop + * terminate + */ + MockToDo expectedToDo1 = new MockToDo(taskDesc1, 1, false); + MockTask[] mockTasks = {expectedToDo1}; // since task0 is deleted + + TaskManager tm = new TaskManager(); + FileResourceManager frm = + new FileResourceManager(getDefaultTasksTestExportPathString(), getDefaultTasksImportTestPathString()); + String out0 = (getExpectedOutputEntry()); + String out1 = (getExpectedOutputImportAttempt(frm.getImportPath())); + String out2 = (getExpectedOutputReadPathNotFound()); + String out3 = (getExpectedOutputBeginInputLoop()); + String out4 = (getExpectedOutputAddedToDo(taskDesc0, 0)); + String out5 = (getExpectedOutputAddedToDo(taskDesc1, 1)); + String out6 = (getExpectedOutputCommandDeleted(0)); + String out7 = (getExpectedOutputList(getExpectedTaskList(mockTasks))); + String out8 = (getExpectedOutputExitInputLoop()); + String out9 = (getExpectedOutputTerminate()); + String expectedOutputResponse = + buildExpectedResponse(out0, out1, out2, out3, out4, out5, out6, out7, out8, out9); + Main.run(this.getPrintStream(), tm, frm); + assertEquals(expectedOutputResponse, this.getOutput()); + } + +} diff --git a/src/test/java/duke/integrationtest/TestIoNegative.java b/src/test/java/duke/integrationtest/TestIoNegative.java new file mode 100644 index 00000000..2434f71f --- /dev/null +++ b/src/test/java/duke/integrationtest/TestIoNegative.java @@ -0,0 +1,52 @@ +package duke.integrationtest; + +import static duke.testhelper.help.Builder.buildCommandInputStream; +import static duke.testhelper.help.Builder.buildExpectedResponse; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputBeginInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputCommandUnknown; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputEntry; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputExitInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputImportAttempt; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputReadPathNotFound; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputTerminate; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandExit; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandRandom; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksImportTestPathString; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksTestExportPathString; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import duke.FileResourceManager; +import duke.Main; +import duke.TaskManager; +import duke.testhelper.TestStream; + +public class TestIoNegative extends TestStream { + + @Test + public void testUnknownCommand() throws Exception { + + String randomTextCommand = generateTextCommandRandom("s0meUnknownPrompt"); + String exitLoopCommand = generateTextCommandExit(); + System.setIn(buildCommandInputStream(randomTextCommand, exitLoopCommand)); + + TaskManager tm = new TaskManager(); + FileResourceManager frm = + new FileResourceManager(getDefaultTasksTestExportPathString(), getDefaultTasksImportTestPathString()); + String out0 = getExpectedOutputEntry(); + String out1 = getExpectedOutputImportAttempt(frm.getImportPath()); + String out2 = getExpectedOutputReadPathNotFound(); + String out3 = getExpectedOutputBeginInputLoop(); + String out4 = getExpectedOutputCommandUnknown(); + String out5 = getExpectedOutputExitInputLoop(); + String out6 = getExpectedOutputTerminate(); + + String expectedOutputResponse = buildExpectedResponse(out0, out1, out2, out3, out4, out5, out6); + + Main.run(this.getPrintStream(), tm, frm); + + assertEquals(expectedOutputResponse, this.getOutput()); + } + +} diff --git a/src/test/java/duke/integrationtest/TestIoSave.java b/src/test/java/duke/integrationtest/TestIoSave.java new file mode 100644 index 00000000..a18077d8 --- /dev/null +++ b/src/test/java/duke/integrationtest/TestIoSave.java @@ -0,0 +1,53 @@ +package duke.integrationtest; + +import static duke.testhelper.help.Builder.buildCommandInputStream; +import static duke.testhelper.help.Builder.buildExpectedResponse; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputBeginInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputEntry; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputExitInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputImportAttempt; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputReadPathNotFound; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputSaved; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputTerminate; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.PROMPT_UNDER_TEST_SAVE; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandExit; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandSave; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksImportTestPathString; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksTestExportPathString; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import duke.FileResourceManager; +import duke.Main; +import duke.TaskManager; +import duke.testhelper.TestStream; + +public class TestIoSave extends TestStream { + + @Test + public void saveMessage() throws Exception { + String saveCommand = generateTextCommandSave(PROMPT_UNDER_TEST_SAVE); + String exitLoopCommand = generateTextCommandExit(); + System.setIn(buildCommandInputStream(saveCommand, exitLoopCommand)); + + TaskManager tm = new TaskManager(); + + String exportPath = getDefaultTasksTestExportPathString(); + FileResourceManager frm = new FileResourceManager(exportPath, getDefaultTasksImportTestPathString()); + String out0 = getExpectedOutputEntry(); + String out1 = getExpectedOutputImportAttempt(frm.getImportPath()); + String out2 = getExpectedOutputReadPathNotFound(); + String out3 = getExpectedOutputBeginInputLoop(); + String out4 = getExpectedOutputSaved(exportPath); + String out5 = getExpectedOutputExitInputLoop(); + String out6 = getExpectedOutputTerminate(); + + String expectedOutputResponse = buildExpectedResponse(out0, out1, out2, out3, out4, out5, out6); + + Main.run(this.getPrintStream(), tm, frm); + + assertEquals(expectedOutputResponse, this.getOutput()); + } + +} diff --git a/src/test/java/duke/integrationtest/TestIoToggleDoneStatus.java b/src/test/java/duke/integrationtest/TestIoToggleDoneStatus.java new file mode 100644 index 00000000..e705d3cf --- /dev/null +++ b/src/test/java/duke/integrationtest/TestIoToggleDoneStatus.java @@ -0,0 +1,107 @@ +package duke.integrationtest; + +import static duke.testhelper.help.Builder.buildCommandInputStream; +import static duke.testhelper.help.Builder.buildExpectedResponse; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputAddedToDo; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputBeginInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputEntry; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputExitInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputImportAttempt; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputList; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputReadPathNotFound; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputTaskSetCompleted; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputTaskSetIncomplete; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputTerminate; +import static duke.testhelper.help.codeundertest.PrettifyUnderTest.getExpectedTaskList; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandExit; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandLineAddToDo; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandList; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandSetCompleted; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandSetIncomplete; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksImportTestPathString; +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksTestExportPathString; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import duke.FileResourceManager; +import duke.Main; +import duke.TaskManager; +import duke.mock.mocktask.MockTask; +import duke.mock.mocktask.MockToDo; +import duke.testhelper.TestStream; + +public class TestIoToggleDoneStatus extends TestStream { + + @Test + public void greet_addToDoToggleList_shouldBeSame() throws Exception { + + // Arrange Input + + /* + * Commands executed: + * + * add task 0 with task description + * list + * exit loop + */ + + // Arrange Expected Output + /* + * Should display: + * Entry Message + * Input Loop Message + * "Added todo" message + * tabled tasks list + * exit loop + * terminate + */ + + TaskManager tm = new TaskManager(); + FileResourceManager frm = + new FileResourceManager(getDefaultTasksTestExportPathString(), getDefaultTasksImportTestPathString()); + + + String taskDesc0 = "taskDesc abc"; + + String out0 = getExpectedOutputEntry(); + String out1 = getExpectedOutputImportAttempt(frm.getImportPath()); + String out2 = getExpectedOutputReadPathNotFound(); + String out3 = getExpectedOutputBeginInputLoop(); + + + String storeCommand = generateTextCommandLineAddToDo(taskDesc0); + String out4 = getExpectedOutputAddedToDo(taskDesc0, 0); + + + String setCompleteCommand = generateTextCommandSetCompleted(0); + String out5 = getExpectedOutputTaskSetCompleted(0); + + String setIncompleteCommand = generateTextCommandSetIncomplete(0); + String out6 = getExpectedOutputTaskSetIncomplete(0); + + String listCommand = generateTextCommandList(); + + MockToDo expectedToDo1 = new MockToDo(taskDesc0, 0, false); + MockTask[] mockTasks = {expectedToDo1}; + String out7 = getExpectedOutputList(getExpectedTaskList(mockTasks)); + + String exitCommand = generateTextCommandExit(); + String out8 = getExpectedOutputExitInputLoop(); + + String out9 = getExpectedOutputTerminate(); + + + System.setIn( + buildCommandInputStream(storeCommand, setCompleteCommand, setIncompleteCommand, listCommand, exitCommand)); + + String expectedOutputResponse = + buildExpectedResponse(out0, out1, out2, out3, out4, out5, out6, out7, out8, out9); + + // Act + Main.run(this.getPrintStream(), tm, frm); + // Assert + assertEquals(expectedOutputResponse, this.getOutput()); + } + +} diff --git a/src/test/java/duke/mock/mocktask/MockDeadline.java b/src/test/java/duke/mock/mocktask/MockDeadline.java new file mode 100644 index 00000000..114ee5e3 --- /dev/null +++ b/src/test/java/duke/mock/mocktask/MockDeadline.java @@ -0,0 +1,26 @@ +package duke.mock.mocktask; + +import java.time.LocalDateTime; + +import duke.testhelper.help.codeundertest.ParserUnderTest; + +public class MockDeadline extends MockTask { + private final LocalDateTime by; + + /** + * Mock deadline + * + * @param desc desc + * @param id id + * @param done done + * @param by by + */ + public MockDeadline(String desc, Integer id, Boolean done, LocalDateTime by) { + super(desc, id, done); + this.by = by; + } + + public String getbyDateString() { + return ParserUnderTest.prettifyLocalDateTime(this.by); + } +} diff --git a/src/test/java/duke/mock/mocktask/MockEvent.java b/src/test/java/duke/mock/mocktask/MockEvent.java new file mode 100644 index 00000000..ae61cd79 --- /dev/null +++ b/src/test/java/duke/mock/mocktask/MockEvent.java @@ -0,0 +1,34 @@ +package duke.mock.mocktask; + + +import java.time.LocalDateTime; + +import duke.testhelper.help.codeundertest.ParserUnderTest; + +public class MockEvent extends MockTask { + private final LocalDateTime from; + private final LocalDateTime to; + + /** + * Mock event + * + * @param desc desc + * @param id id + * @param done done + * @param from from + * @param to to + */ + public MockEvent(String desc, Integer id, Boolean done, LocalDateTime from, LocalDateTime to) { + super(desc, id, done); + this.from = (from); + this.to = (to); + } + + public String getToDateString() { + return ParserUnderTest.prettifyLocalDateTime(this.to); + } + + public String getFromDateString() { + return ParserUnderTest.prettifyLocalDateTime(this.from); + } +} diff --git a/src/test/java/duke/mock/mocktask/MockTask.java b/src/test/java/duke/mock/mocktask/MockTask.java new file mode 100644 index 00000000..32169d6b --- /dev/null +++ b/src/test/java/duke/mock/mocktask/MockTask.java @@ -0,0 +1,28 @@ +package duke.mock.mocktask; + +public abstract class MockTask { + private String desc; + private Integer id; + private Boolean done = false; + + private MockTask() { + } + + protected MockTask(String desc, Integer id, Boolean done) { + this.desc = desc; + this.id = id; + this.done = done; + } + + public String getDesc() { + return this.desc; + } + + public Boolean getDone() { + return this.done; + } + + public Integer getId() { + return this.id; + } +} diff --git a/src/test/java/duke/mock/mocktask/MockToDo.java b/src/test/java/duke/mock/mocktask/MockToDo.java new file mode 100644 index 00000000..b8895925 --- /dev/null +++ b/src/test/java/duke/mock/mocktask/MockToDo.java @@ -0,0 +1,8 @@ +package duke.mock.mocktask; + +public class MockToDo extends MockTask { + + public MockToDo(String desc, Integer id, Boolean done) { + super(desc, id, done); + } +} diff --git a/src/test/java/duke/systemtest/SmokeTest.java b/src/test/java/duke/systemtest/SmokeTest.java new file mode 100644 index 00000000..5f1a83b2 --- /dev/null +++ b/src/test/java/duke/systemtest/SmokeTest.java @@ -0,0 +1,25 @@ +package duke.systemtest; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import duke.Ui; +import duke.testhelper.TestStream; +import duke.testhelper.help.codeundertest.OutputUnderTest; + +public class SmokeTest extends TestStream { + /** + * should display entry and exit message and terminates gently. + */ + @Test + public void greetAndExit() { + String expectedEntryMessage = OutputUnderTest.getExpectedOutputEntry(); + String expectedExitMessage = OutputUnderTest.getExpectedOutputTerminate(); + String expectedOutput = expectedEntryMessage + expectedExitMessage; + Ui ui = new Ui(this.getPrintStream()); + ui.printEntryMessage(); + ui.printTerminateMessage(); + assertEquals(expectedOutput, this.getOutput()); + } +} diff --git a/src/test/java/duke/systemtest/SystemExportTest.java b/src/test/java/duke/systemtest/SystemExportTest.java new file mode 100644 index 00000000..862ca553 --- /dev/null +++ b/src/test/java/duke/systemtest/SystemExportTest.java @@ -0,0 +1,249 @@ +package duke.systemtest; + +import static duke.dukeutility.config.DukeIo.getDefaultTasksImportPathString; +import static duke.dukeutility.validator.TextCommandValidator.isParentDirectoryValid; +import static duke.testhelper.help.Builder.buildCommandInputStream; +import static duke.testhelper.help.Builder.buildString; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputAddedDeadline; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputAddedEvent; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputAddedToDo; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputBeginInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputCommandDeleted; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputEntry; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputExitInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputImportAttempt; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputList; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputListTasksWithKeywordDescription; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputReadPathNotFound; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputTaskSetCompleted; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputTerminate; +import static duke.testhelper.help.codeundertest.ParserUnderTest.parseStringAsLocalDateTime; +import static duke.testhelper.help.codeundertest.ParserUnderTest.stringToPath; +import static duke.testhelper.help.codeundertest.PrettifyUnderTest.getExpectedTaskList; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.PROMPT_UNDER_TEST_DELETE_TASK; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.PROMPT_UNDER_TEST_EXIT_LOOP; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.PROMPT_UNDER_TEST_FIND; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.PROMPT_UNDER_TEST_SAVE; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandDeleteTaskByTaskId; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandExit; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandFindKeywordInDescription; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandLineAddDeadline; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandLineAddEvent; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandLineAddToDo; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandList; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandSave; +import static duke.testhelper.help.codeundertest.TextCommandUnderTest.generateTextCommandSetCompleted; +import static duke.testhelper.help.config.DukeIoTestPath.getResourceTestFolder; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import org.junit.jupiter.api.Test; + +import com.google.gson.JsonArray; + +import duke.FileResourceManager; +import duke.Main; +import duke.TaskManager; +import duke.command.commandfactory.ImportCommandFactory; +import duke.mock.mocktask.MockDeadline; +import duke.mock.mocktask.MockEvent; +import duke.mock.mocktask.MockTask; +import duke.mock.mocktask.MockToDo; +import duke.testhelper.TestStream; + +public class SystemExportTest extends TestStream { + + /** + * Execute add tasks commands and save as export1 + * import export1, save as export2 + * export1 == export2 ? pass + */ + @Test + public void idempotentExport() { + String thisTestSign = "saveTasksToJsonTestFile"; + String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-hh-mm-ss")); + String export1PathString = getResourceTestFolder() + "-added" + thisTestSign + date + ".json"; + String export2PathString = getResourceTestFolder() + "-load" + thisTestSign + date + ".json"; + FileResourceManager frm1 = new FileResourceManager(export1PathString, null); + TaskManager tm1 = new TaskManager(); + // sets of add tasks textCommands + int countPerTaskType = 10; + int expectedTotalTaskCount = 0; + + String[] addCommands = new String[countPerTaskType]; + for (int i = 0; i < countPerTaskType; i++) { + + String todoDesc = "tododesc" + i; + + String deadlineDesc = "deadlinedesc"; + String deadlineBy = "202002" + String.format("%2s", 19); + + String eventDesc = "eventdesc"; + String eventFrom = "20200202"; + String eventTo = "20210202"; + + String addToDoCommand = generateTextCommandLineAddToDo(todoDesc); + expectedTotalTaskCount++; + String addDeadlineCommand = generateTextCommandLineAddDeadline(deadlineDesc, + deadlineBy); + expectedTotalTaskCount++; + String addEventCommand = + generateTextCommandLineAddEvent(eventDesc, eventFrom, + eventTo); + expectedTotalTaskCount++; + + addCommands[i] = addToDoCommand + addDeadlineCommand + addEventCommand; + } + + String in0 = String.join("", addCommands); + String in1 = generateTextCommandSetCompleted(5); + String in2 = (generateTextCommandSave(PROMPT_UNDER_TEST_SAVE)); + String in3 = (generateTextCommandExit()); + System.setIn(buildCommandInputStream(in0, in1, in2, in3)); + try { + Main.run(this.getPrintStream(), tm1, frm1); + assertSame(tm1.getSize(), expectedTotalTaskCount, "expected amount " + expectedTotalTaskCount + + ", actual " + tm1.getSize() + System.lineSeparator()); + } catch (Exception e) { + fail(e.toString()); + } + + String secondIn0 = generateTextCommandSave(PROMPT_UNDER_TEST_SAVE); + String secondIn1 = generateTextCommandSave(PROMPT_UNDER_TEST_EXIT_LOOP); + + System.setIn(buildCommandInputStream(secondIn0, secondIn1)); + FileResourceManager frm2 = new FileResourceManager(export2PathString, export1PathString); + + TaskManager tm2 = new TaskManager(); + try { + Main.run(this.getPrintStream(), tm2, frm2); + } catch (Exception e) { + fail(e.toString()); + } + assertEquals(30, tm2.getSize()); + try { + JsonArray export1 = + new ImportCommandFactory().executeExtractTasksFromFile(frm1.getExportPath()).getJsonArg() + .getAsJsonArray(); + JsonArray export2 = + new ImportCommandFactory().executeExtractTasksFromFile(frm2.getExportPath()).getJsonArg() + .getAsJsonArray(); + assertNotNull(export1); + assertNotNull(export2); + assertEquals(30, export1.size()); + assertEquals(30, export2.size()); + assertEquals(export1, export2); + } catch (Exception e) { + fail("Failure during comparing exports. " + e + this.getOutput()); + } + } + + + private void writeToFile(String pathString, String... data) throws Exception { + Path exportPath = stringToPath(pathString); + try { + if (exportPath == null) { + throw new Exception("Export path validation failed."); + } + Files.createDirectories(exportPath.getParent()); + if (!isParentDirectoryValid(exportPath)) { + throw new Exception("Export path validation failed."); + } + } catch (Exception e) { + throw new IOException("Export path validation failed."); + } + Files.write(exportPath, buildString(data).getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); + } + + /** + * This method generates 1) the input and 2) its expected output for system level test. No assertions required. + * + * @throws Exception Unable to generate files, may impact on test phase. + */ + @Test + public void generateExpectedTestFileInActualEnvironment() throws Exception { + String importPathString = getDefaultTasksImportPathString(); + + String loadInputPath = + String.join(File.separator, new String[] {"src", "test", "resources", "linux-test", "input.txt"}); + String expectedOutputPath = + String.join(File.separator, new String[] {"src", "test", "resources", "linux-test", "expected.txt"}); + + String out0 = (getExpectedOutputEntry()); + + String out1 = (getExpectedOutputImportAttempt(stringToPath(importPathString))); + String out2 = (getExpectedOutputReadPathNotFound()); + + String out3 = (getExpectedOutputBeginInputLoop()); + + String todoDesc = "tododesc"; + + String deadlineDesc = "deadlinedesc"; + String deadlineBy = "202002" + String.format("%2s", 19); + + String eventDesc = "eventdesc asdasdasd"; + String eventFrom = "20200202"; + String eventTo = "20210202"; + + String in0 = generateTextCommandLineAddToDo(todoDesc); + String out4 = (getExpectedOutputAddedToDo(todoDesc, 0)); + + String in1 = generateTextCommandLineAddDeadline(deadlineDesc, + deadlineBy); + String out5 = (getExpectedOutputAddedDeadline(deadlineDesc, 1)); + + String in2 = + generateTextCommandLineAddEvent(eventDesc, eventFrom, + eventTo); + String out6 = (getExpectedOutputAddedEvent(eventDesc, 2)); + + String in3 = generateTextCommandList(); + + MockTask[] allMockTasks = new MockTask[] {new MockToDo(todoDesc, 0, false), + new MockDeadline(deadlineDesc, 1, false, parseStringAsLocalDateTime(deadlineBy)), + new MockEvent(eventDesc, 2, false, parseStringAsLocalDateTime(eventFrom), + parseStringAsLocalDateTime(eventTo))}; + String out7 = (getExpectedOutputList(getExpectedTaskList(allMockTasks))); + + String in4 = generateTextCommandDeleteTaskByTaskId(PROMPT_UNDER_TEST_DELETE_TASK, 0); + String out8 = (getExpectedOutputCommandDeleted(0)); + + String in5 = generateTextCommandList(); + + MockTask[] remainingMockTasks = + new MockTask[] {new MockDeadline(deadlineDesc, 1, false, parseStringAsLocalDateTime(deadlineBy)), + new MockEvent(eventDesc, 2, false, parseStringAsLocalDateTime(eventFrom), + parseStringAsLocalDateTime(eventTo))}; + String out9 = (getExpectedOutputList(getExpectedTaskList(remainingMockTasks))); + + String in6 = generateTextCommandSetCompleted(2); + String out10 = getExpectedOutputTaskSetCompleted(2); + + String keyword = "eventdesc"; + String in7 = generateTextCommandFindKeywordInDescription(PROMPT_UNDER_TEST_FIND, keyword); + MockTask[] selectedMockTasks = new MockTask[] { + new MockEvent(eventDesc, 2, true, parseStringAsLocalDateTime(eventFrom), + parseStringAsLocalDateTime(eventTo))}; + String out11 = + getExpectedOutputListTasksWithKeywordDescription(getExpectedTaskList(selectedMockTasks), keyword); + + String in8 = generateTextCommandExit(); + String out12 = (getExpectedOutputExitInputLoop()); + + String out13 = (getExpectedOutputTerminate()); + + writeToFile(loadInputPath, in0, in1, in2, in3, in4, in5, in6, in7, in8); + writeToFile(expectedOutputPath, out0, out1, out2, out3, out4, out5, out6, out7, out8, out9, out10, out11, out12, + out13); + } +} diff --git a/src/test/java/duke/testhelper/TestStream.java b/src/test/java/duke/testhelper/TestStream.java new file mode 100644 index 00000000..0d5f5a54 --- /dev/null +++ b/src/test/java/duke/testhelper/TestStream.java @@ -0,0 +1,46 @@ +package duke.testhelper; + + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +public class TestStream { + + private ByteArrayOutputStream outputStreamCaptor; + private PrintStream printStream; + + protected PrintStream getPrintStream() { + return this.printStream; + } + + protected void setPrintStream(PrintStream ps) { + this.printStream = ps; + } + + protected void setOutputStreamCaptor(ByteArrayOutputStream baos) { + this.outputStreamCaptor = baos; + } + + protected ByteArrayOutputStream getOutputStreamCaptor() { + return this.outputStreamCaptor; + } + + // helper class + protected String getOutput() { + return this.outputStreamCaptor.toString(); + } + + @BeforeEach + public void setOutputStream() { + this.setOutputStreamCaptor(new ByteArrayOutputStream()); + this.setPrintStream(new PrintStream(this.getOutputStreamCaptor())); + } + + @AfterEach + public void unsetStreams() { + this.setOutputStreamCaptor(null); + this.setPrintStream(null); + } +} diff --git a/src/test/java/duke/testhelper/help/Builder.java b/src/test/java/duke/testhelper/help/Builder.java new file mode 100644 index 00000000..de360251 --- /dev/null +++ b/src/test/java/duke/testhelper/help/Builder.java @@ -0,0 +1,36 @@ +package duke.testhelper.help; + +import java.io.ByteArrayInputStream; + +public class Builder { + + + public static ByteArrayInputStream buildCommandInputStream(String... commands) { + return new ByteArrayInputStream(buildString(commands).getBytes()); + } + + /** + * Helper to combine array of strings. + * + * @param commands + * @return + */ + public static String buildString(String... commands) { + StringBuilder commandBuilder = new StringBuilder(); + for (String c : commands) { + commandBuilder.append(c); + } + return commandBuilder.toString(); + } + + /** + * Helper to build expected output response. + * + * @param responses + * @return output response + */ + public static String buildExpectedResponse(String... responses) { + return buildString(responses); + } + +} diff --git a/src/test/java/duke/testhelper/help/codeundertest/OutputUnderTest.java b/src/test/java/duke/testhelper/help/codeundertest/OutputUnderTest.java new file mode 100644 index 00000000..6a5427f3 --- /dev/null +++ b/src/test/java/duke/testhelper/help/codeundertest/OutputUnderTest.java @@ -0,0 +1,109 @@ +package duke.testhelper.help.codeundertest; + +import java.nio.file.Path; + +public class OutputUnderTest { + private static final String LOGO = " _ _ _ " + + System.lineSeparator() + + "| | | | | | " + + System.lineSeparator() + + "| |_ __ _ ___ | | __ _ __ ___ __ _ ___ | |_ ___ _ __ " + + System.lineSeparator() + + "| __| / _` | / __| | |/ / | '_ ` _ \\ / _` | / __| | __| / _ \\ | '__|" + + System.lineSeparator() + + "| |_ | (_| | \\__ \\ | < | | | | | | | (_| | \\__ \\ | |_ | __/ | | " + + System.lineSeparator() + + " \\__| \\__,_| |___/ |_|\\_\\ |_| |_| |_| \\__,_| |___/ \\__| \\___| |_| " + + System.lineSeparator(); + + private static final String RESPONSE_TERMINATOR_UNDER_TEST = "\t\t\t\t\t\t\t\t -" + System.lineSeparator(); + + public static String getExpectedOutputEntry() { + return "Hello from" + System.lineSeparator() + LOGO; + } + + public static String getExpectedOutputTerminate() { + return "See you again!" + System.lineSeparator(); + } + + public static String getExpectedOutputBeginInputLoop() { + return "How can i help you? (See README.md for usage)" + System.lineSeparator(); + } + + public static String getExpectedOutputImportAttempt(Path path) { + return "Attempting to import tasks from " + path + "." + System.lineSeparator(); + } + + public static String getExpectedOutputReadPathNotFound() { + return "Read path not found/invalid. " + System.lineSeparator(); + } + + public static String getExpectedOutputSaved(String pathString) { + return getExpectedOutputTemplate("Saved task to file: " + pathString); + } + + public static String getExpectedOutputTemplate(String text) { + return text + System.lineSeparator() + RESPONSE_TERMINATOR_UNDER_TEST; + } + + + public static String getExpectedOutputExitInputLoop() { + return getExpectedOutputTemplate("ok bye"); + } + + public static String getExpectedOutputAddedToDo(String desc, Integer id) { + return getExpectedOutputTemplate("Added To Do [id #" + id + "]: " + desc); + } + + public static String getExpectedOutputAddedDeadline(String desc, Integer id) { + return getExpectedOutputTemplate("Added Deadline [id #" + id + "]: " + desc); + } + + public static String getExpectedOutputAddedEvent(String desc, Integer id) { + return getExpectedOutputTemplate("Added Event [id #" + id + "]: " + desc); + + } + + public static String getExpectedOutputList(String list) { + return list + RESPONSE_TERMINATOR_UNDER_TEST; + } + + public static String getExpectedOutputStatsAll(String stats) { + return "Task Summary " + System.lineSeparator() + stats + RESPONSE_TERMINATOR_UNDER_TEST; + } + + public static String getExpectedOutputListTasksWithKeywordDescription(String list, String keyword) { + String info = "Query keyword in description: " + keyword; + return info + System.lineSeparator() + list + RESPONSE_TERMINATOR_UNDER_TEST; + } + + public static String getExpectedOutputListTasksWithinPeriod(String list, Integer period) { + String info = "All tasks for the next " + period + " days: "; + return info + System.lineSeparator() + list + RESPONSE_TERMINATOR_UNDER_TEST; + } + + public static String getExpectedOutputTaskSetCompleted(Integer taskId) { + return getExpectedOutputTemplate("update done #" + taskId.toString()); + } + + public static String getExpectedOutputTaskSetIncomplete(Integer taskId) { + return getExpectedOutputTemplate("update not done #" + taskId.toString()); + } + + public static String getExpectedOutputCommandUnknown() { + return getExpectedOutputTemplate("Unknown command. . ."); + } + + public static String getExpectedOutputCommandDeleted(Integer taskId) { + return getExpectedOutputTemplate("Task Deleted: #" + taskId); + } + + public static String getMsgUnderTestErrorSpacedKeyword() { + String text = "Invalid syntax. Keyword should not have spacing."; + return getExpectedOutputTemplate(text); + } + + public static String getMsgUnderTestErrorParseStringAsLocalDate() { + return getExpectedOutputTemplate("Invalid parameters: java.lang.Exception: Parse as LocalDateTime failed."); + } +} diff --git a/src/test/java/duke/testhelper/help/codeundertest/ParserUnderTest.java b/src/test/java/duke/testhelper/help/codeundertest/ParserUnderTest.java new file mode 100644 index 00000000..071e48d1 --- /dev/null +++ b/src/test/java/duke/testhelper/help/codeundertest/ParserUnderTest.java @@ -0,0 +1,67 @@ +package duke.testhelper.help.codeundertest; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + + +public class ParserUnderTest { + private static final String transitiveJsonAndTextPattern = "yyyy-MM-dd-HH-mm-ss"; + private static final List patterns = + List.of("yyyyMMdd", "yyyyMMdd HH:mm", ParserUnderTest.transitiveJsonAndTextPattern); + + public static String parseLocalDateTimeAsString(LocalDateTime ldt) { + return DateTimeFormatter.ofPattern(ParserUnderTest.transitiveJsonAndTextPattern).format(ldt); + } + + /** + * Test Helper + * Format string as LocalDateTime. Multiple patterns will be attempted to match string. + * + * @param dateTimeString + * @return date + * @throws Exception if fail to format. + */ + public static LocalDateTime parseStringAsLocalDateTime(String dateTimeString) throws Exception { + LocalDateTime ldt = null; + for (String pattern : ParserUnderTest.patterns) { + try { + ldt = LocalDateTime.parse(dateTimeString, DateTimeFormatter.ofPattern(pattern)); + } catch (Exception e) { + } + } + try { + ldt = parseStringAsLocalDate(dateTimeString).atTime(0, 0); + } catch (Exception e) { + } + if (ldt == null) { + throw new Exception("Parse as LocalDateTime failed."); + } + return ldt; + } + + public static LocalDate parseStringAsLocalDate(String dateString) { + return LocalDate.parse(dateString, DateTimeFormatter.ofPattern("yyyyMMdd")); + } + + public static String prettifyLocalDateTime(LocalDateTime ldt) { + return ldt.toString().replace("T", " "); + } + + /** + * Helper for parsing string as path + * + * @param pathString + * @return + */ + public static Path stringToPath(String pathString) { + try { + return Paths.get(pathString); + } catch (Exception e) { + return null; + } + } +} diff --git a/src/test/java/duke/testhelper/help/codeundertest/PrettifyUnderTest.java b/src/test/java/duke/testhelper/help/codeundertest/PrettifyUnderTest.java new file mode 100644 index 00000000..ee6f33c4 --- /dev/null +++ b/src/test/java/duke/testhelper/help/codeundertest/PrettifyUnderTest.java @@ -0,0 +1,204 @@ +package duke.testhelper.help.codeundertest; + +import java.util.ArrayList; +import java.util.Arrays; + +import duke.mock.mocktask.MockDeadline; +import duke.mock.mocktask.MockEvent; +import duke.mock.mocktask.MockTask; +import duke.mock.mocktask.MockToDo; +import duke.task.model.Deadline; +import duke.task.model.Event; +import duke.task.model.Task; +import duke.task.model.ToDo; + +public class PrettifyUnderTest { + + private static String getTaskCharCodeUnderTest(MockTask task) { + if (task instanceof MockToDo) { + return "T"; + } + if (task instanceof MockEvent) { + return "E"; + } + if (task instanceof MockDeadline) { + return "D"; + } + return " "; + } + + private static String getTaskChronologyString(MockTask task) { + if (task instanceof MockToDo) { + return "-"; + } + if (task instanceof MockEvent) { + return "From: " + ((MockEvent) task).getFromDateString() + ", To: " + ((MockEvent) task).getToDateString(); + } + if (task instanceof MockDeadline) { + return "By: " + ((MockDeadline) task).getbyDateString(); + } + return " "; + } + + private static String fillCellUnderTest(String value, Integer lengthColMax) { + int lengthValue = value.length(); + int lengthPad = lengthColMax - lengthValue; + return String.format("%s%s", value, + String.format("%" + (lengthPad > 0 ? lengthPad : "") + "s", (lengthPad > 0 ? " " : ""))); + } + + public static String getExpectedTaskList(MockTask... mockTasks) { + + int taskQty = mockTasks.length; + StringBuilder generating = new StringBuilder(); + generating.append((taskQty + " task" + (taskQty > 1 ? "s" : "") + " in list" + System.lineSeparator())); + /* header values */ + + String headerId = String.format(" %-4s ", "id#"); + String headerDoneStatus = "Done "; + String headerTaskType = "Type "; + String headerDescription = "Task Description "; + String headerChronology = "Chronology"; + + /* append headers */ + + generating.append(headerId); + generating.append(headerDoneStatus); + generating.append(headerTaskType); + generating.append(headerDescription); + generating.append(headerChronology); + generating.append(System.lineSeparator()); + + /* column width references */ + + int lengthColId = headerId.length(); + int lengthColTaskType = headerTaskType.length(); + int lengthColDoneStatus = headerDoneStatus.length(); + int lengthColDesc = headerDescription.length(); + + for (MockTask task : mockTasks) { + // fill column Id + String idValue = String.format("%4d", task.getId()).replace(" ", "0"); + String columnId = fillCellUnderTest(idValue, lengthColId); + + // fill column Done Status + String doneStatusValue = String.format("[%s]", task.getDone() ? "X" : " "); + String columnDoneStatus = fillCellUnderTest(doneStatusValue, lengthColDoneStatus); + // fill column Task Type Status + String taskTypeSymbolValue = String.format("[%s]", getTaskCharCodeUnderTest(task)); + String columnTaskType = fillCellUnderTest(taskTypeSymbolValue, lengthColTaskType); + // fill column Description + String descValueLong = task.getDesc(); + String descValue = descValueLong.substring(0, Math.min(descValueLong.length(), lengthColDesc - 1)); + String columnDescription = fillCellUnderTest(descValue, lengthColDesc); + // fill column Chronology + String columnChronology = getTaskChronologyString(task); + + generating.append(columnId); + generating.append(columnDoneStatus); + generating.append(columnTaskType); + generating.append(columnDescription); + generating.append(columnChronology); + generating.append(System.lineSeparator()); + } + String generated = generating.toString(); + return generated; + } + + public static String getExpectedStatisticsAll(ArrayList tasks) { + // Tabulate + int col = 0; + int colDone = col++; + int colNotDone = col++; + int row = 0; + int rowToDo = row++; + int rowDeadline = row++; + int rowEvent = row++; + + + Integer[][] stats = new Integer[col][row]; + for (Integer[] ints : stats) { + Arrays.fill(ints, 0); + } + for (Task t : tasks) { + + Integer thisRow = null; + if (t instanceof ToDo) { + thisRow = rowToDo; + } else if (t instanceof Deadline) { + thisRow = rowDeadline; + } else if (t instanceof Event) { + thisRow = rowEvent; + } + Integer thisCol = null; + if (t.isDone()) { + thisCol = colDone; + } else { + thisCol = colNotDone; + } + assert (thisCol != null); + assert (thisRow != null); + if (thisCol != null && thisRow != null) { + stats[thisCol][thisRow]++; + } + } + int rowHeader = row++; + int colTaskType = col++; + String[][] table = new String[col][row]; + for (String[] tableRow : table) { + Arrays.fill(tableRow, ""); + } + table[0][rowHeader] = "Task Type "; + table[0][rowToDo] = "To Do"; + table[0][rowDeadline] = "Deadline"; + table[0][rowEvent] = "Event"; + + table[2][rowHeader] = "Incomplete "; + table[2][rowToDo] = stats[colNotDone][rowToDo].toString(); + table[2][rowDeadline] = stats[colNotDone][rowDeadline].toString(); + table[2][rowEvent] = stats[colNotDone][rowEvent].toString(); + + table[1][rowHeader] = "Complete "; + table[1][rowToDo] = stats[colDone][rowToDo].toString(); + table[1][rowDeadline] = stats[colDone][rowDeadline].toString(); + table[1][rowEvent] = stats[colDone][rowEvent].toString(); + + + int[] colLength = new int[col]; + + colLength[colTaskType] = getMaxLength(table[colTaskType]); + colLength[colNotDone] = getMaxLength(table[colNotDone]); + colLength[colDone] = getMaxLength(table[colDone]); + + + for (int c = 0; c < col; c++) { + for (int r = 0; r < row; r++) { + table[c][r] = fillCellUnderTest(table[c][r], colLength[c]); + } + } + StringBuilder lines = new StringBuilder(); + + lines.append(String.join("", getRowValue(table, rowHeader)) + System.lineSeparator()); + lines.append(String.join("", getRowValue(table, rowToDo)) + System.lineSeparator()); + lines.append(String.join("", getRowValue(table, rowDeadline)) + System.lineSeparator()); + lines.append(String.join("", getRowValue(table, rowEvent)) + System.lineSeparator()); + return lines.toString(); + } + + public static String getRowValue(String[][] table, int r) { + StringBuilder result = new StringBuilder(); + for (int c = 0; c < table.length; c++) { + result.append(table[c][r]); + } + return result.toString(); + } + + private static int getMaxLength(String[] strings) { + int maxLength = 0; + for (String s : strings) { + maxLength = Math.max(maxLength, s.length()); + } + return maxLength; + + } +} diff --git a/src/test/java/duke/testhelper/help/codeundertest/TextCommandUnderTest.java b/src/test/java/duke/testhelper/help/codeundertest/TextCommandUnderTest.java new file mode 100644 index 00000000..ec8c500b --- /dev/null +++ b/src/test/java/duke/testhelper/help/codeundertest/TextCommandUnderTest.java @@ -0,0 +1,92 @@ +package duke.testhelper.help.codeundertest; + +public class TextCommandUnderTest { + public static final String PROMPT_UNDER_TEST_EXIT_LOOP = "bye"; + public static final String PROMPT_UNDER_TEST_LIST = "list"; + public static final String PROMPT_UNDER_TEST_STATS_ALL = "stats:all"; + public static final String PROMPT_UNDER_TEST_SCAN_DUPLICATE_DESCRIPTION = "scan:duplicates"; + public static final String PROMPT_UNDER_TEST_PROJECTION = "projection "; + + public static final String PROMPT_UNDER_TEST_MARK_AS_DONE = "done "; + public static final String PROMPT_UNDER_TEST_MARK_AS_INCOMPLETE = "undone "; + + public static final String PROMPT_UNDER_TEST_ADD_TO_DO = "todo "; + + public static final String PROMPT_UNDER_TEST_ADD_DEADLINE = "deadline "; + public static final String DELIMITER_DEADLINE_DEADLINE = " /by "; + + public static final String PROMPT_UNDER_TEST_ADD_EVENT = "event "; + public static final String DELIMITER_EVENT_EVENT = " /at "; + public static final String DELIMITER_EVENT_TO = "-"; + + public static final String PROMPT_UNDER_TEST_DELETE_TASK = "delete "; + + public static final String PROMPT_UNDER_TEST_SAVE = "save"; + + public static final String PROMPT_UNDER_TEST_FIND = "find "; + + private static String singleArgumentCommand(String text) { + return text + System.lineSeparator(); + } + + public static String generateTextCommandExit() { + return singleArgumentCommand(PROMPT_UNDER_TEST_EXIT_LOOP); + } + + public static String generateTextCommandStatsAll() { + return singleArgumentCommand(PROMPT_UNDER_TEST_STATS_ALL); + } + + public static String generateTextCommandRandom(String text) { + return singleArgumentCommand(text); + } + + public static String generateTextCommandList() { + return singleArgumentCommand(PROMPT_UNDER_TEST_LIST); + } + + public static String generateTextCommandSetCompleted(Integer taskId) { + return PROMPT_UNDER_TEST_MARK_AS_DONE + taskId + System.lineSeparator(); + } + + public static String generateTextCommandSetIncomplete(Integer taskId) { + return PROMPT_UNDER_TEST_MARK_AS_INCOMPLETE + taskId + System.lineSeparator(); + } + + public static String generateTextCommandScanDuplicateDescription() { + return PROMPT_UNDER_TEST_SCAN_DUPLICATE_DESCRIPTION + System.lineSeparator(); + } + + public static String generateTextCommandLineAddToDo(String description) { + return PROMPT_UNDER_TEST_ADD_TO_DO + description + System.lineSeparator(); + } + + public static String generateTextCommandLineAddDeadline(String description, + String deadlineString) { + return PROMPT_UNDER_TEST_ADD_DEADLINE + description + DELIMITER_DEADLINE_DEADLINE + deadlineString + + System.lineSeparator(); + } + + public static String generateTextCommandLineAddEvent(String desc, + String from, + String to) { + return PROMPT_UNDER_TEST_ADD_EVENT + desc + DELIMITER_EVENT_EVENT + from + DELIMITER_EVENT_TO + to + + System.lineSeparator(); + } + + public static String generateTextCommandDeleteTaskByTaskId(String invoke, Integer taskId) { + return invoke + taskId + System.lineSeparator(); + } + + public static String generateTextCommandSave(String invoke) { + return singleArgumentCommand(invoke); + } + + public static String generateTextCommandFindKeywordInDescription(String invoke, String keyword) { + return invoke + keyword + System.lineSeparator(); + } + + public static String generateTextCommandProjection(Integer days) { + return PROMPT_UNDER_TEST_PROJECTION + days + System.lineSeparator(); + } +} diff --git a/src/test/java/duke/testhelper/help/config/DukeIoTestPath.java b/src/test/java/duke/testhelper/help/config/DukeIoTestPath.java new file mode 100644 index 00000000..20ff3fdb --- /dev/null +++ b/src/test/java/duke/testhelper/help/config/DukeIoTestPath.java @@ -0,0 +1,35 @@ +package duke.testhelper.help.config; + + +import java.io.File; + +public class DukeIoTestPath { + + private static final String resourceTestFolder = + String.join(File.separator, new String[] {System.getProperty("user.home"), "duke", "test"}); + + private static String testPathStringDefaultTasksExportJsonPath = null; + private static String testPathStringDefaultTasksImportPath = null; + + public static String getDefaultTasksTestExportPathString() { + if (DukeIoTestPath.testPathStringDefaultTasksExportJsonPath == null) { + DukeIoTestPath.testPathStringDefaultTasksExportJsonPath = + DukeIoTestPath.resourceTestFolder + File.separator + "exports" + File.separator + "tasks.json"; + } + return DukeIoTestPath.testPathStringDefaultTasksExportJsonPath; + } + + public static String getResourceTestFolder() { + return DukeIoTestPath.resourceTestFolder; + } + + public static String getDefaultTasksImportTestPathString() { + if (DukeIoTestPath.testPathStringDefaultTasksImportPath == null) { + DukeIoTestPath.testPathStringDefaultTasksImportPath = + DukeIoTestPath.resourceTestFolder + File.separator + "imports" + File.separator + "tasks.json"; + } + return DukeIoTestPath.testPathStringDefaultTasksImportPath; + } + + +} diff --git a/src/test/java/duke/unittest/UiTest.java b/src/test/java/duke/unittest/UiTest.java new file mode 100644 index 00000000..8b7fc49b --- /dev/null +++ b/src/test/java/duke/unittest/UiTest.java @@ -0,0 +1,140 @@ +package duke.unittest; + +import static duke.testhelper.help.Builder.buildCommandInputStream; +import static duke.testhelper.help.Builder.buildExpectedResponse; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputBeginInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputExitInputLoop; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputListTasksWithinPeriod; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputStatsAll; +import static duke.testhelper.help.codeundertest.OutputUnderTest.getExpectedOutputTemplate; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.LocalDateTime; + +import org.junit.jupiter.api.Test; + +import duke.TaskManager; +import duke.Ui; +import duke.mock.mocktask.MockDeadline; +import duke.mock.mocktask.MockEvent; +import duke.task.model.Deadline; +import duke.task.model.Event; +import duke.task.model.ToDo; +import duke.testhelper.TestStream; +import duke.testhelper.help.codeundertest.PrettifyUnderTest; +import duke.testhelper.help.codeundertest.TextCommandUnderTest; + +public class UiTest extends TestStream { + + @Test + public void project_withinNextDays_filteredAndSorted() throws Exception { + int period = 30; + // create some tasks spanning across different days in task manager, expect filter range in period of days. + + Ui ui = new Ui(this.getPrintStream()); + TaskManager tm = new TaskManager(); + + // Add tasks to task manager and mock expected tasks + + // task 0 + String toDoDesc = "todo should not appear"; + tm.addNewToDo(toDoDesc); + + // task 1 + String deadlineDesc = "due in " + period + " days"; + LocalDateTime dldl = LocalDateTime.now().plusDays(period); + tm.addNewDeadline(deadlineDesc, dldl); + MockDeadline task1 = new MockDeadline(deadlineDesc, 1, false, dldl); + + // task 2 + tm.addNewDeadline("assignment due beyond period", LocalDateTime.now().plusDays(period + 200)); + + // task 3 + String eventDesc = "event start today"; + LocalDateTime from = LocalDateTime.now(); + LocalDateTime to = LocalDateTime.now().plusDays(period + 1); + tm.addNewEvent(eventDesc, from, to); + MockEvent task3 = new MockEvent(eventDesc, 3, false, from, to); + + // build commands + String out0 = getExpectedOutputBeginInputLoop(); + String in0 = TextCommandUnderTest.generateTextCommandProjection(period); + + String expectedList = PrettifyUnderTest.getExpectedTaskList(task3, task1); + String out1 = getExpectedOutputListTasksWithinPeriod(expectedList, period); + + String in1 = TextCommandUnderTest.generateTextCommandExit(); + String out2 = getExpectedOutputExitInputLoop(); + + // set commands to input stream + System.setIn(buildCommandInputStream(in0, in1)); + ui.runTextCommandLoop(tm, null); + + String expectedOutput = buildExpectedResponse(out0, out1, out2); + assertEquals(expectedOutput, this.getOutput()); + } + + /** + * create a collection of tasks, summarise by task type and completion status. + */ + @Test + public void stats_showSummaryAll() throws Exception { + + TaskManager tm = new TaskManager(); + int expectedCountToDo = 0; + int expectedCountDeadline = 0; + int expectedCountEvent = 0; + LocalDateTime date = LocalDateTime.now(); + ToDo task0 = tm.addNewToDo("todo done" + expectedCountToDo++); + task0.setDoneStatus(true); + Deadline task1 = tm.addNewDeadline("deadline" + expectedCountDeadline++, date); + Event task2 = tm.addNewEvent("event" + expectedCountEvent++, date, date); + + String out0 = getExpectedOutputBeginInputLoop(); + String in0 = TextCommandUnderTest.generateTextCommandStatsAll(); + String stats = PrettifyUnderTest.getExpectedStatisticsAll(tm.getAllAsArray()); + String out1 = getExpectedOutputStatsAll(stats); + String in1 = TextCommandUnderTest.generateTextCommandExit(); + String out2 = getExpectedOutputExitInputLoop(); + System.setIn(buildCommandInputStream(in0, in1)); + String expectedOutput = buildExpectedResponse(out0, out1, out2); + new Ui(this.getPrintStream()).runTextCommandLoop(tm, null); + assertEquals(expectedOutput, this.getOutput()); + } + + /** + * Check and show task ids of duplicates. + */ + @Test + public void scan_checkDuplicateDescription() throws Exception { + TaskManager tm = new TaskManager(); + LocalDateTime date = LocalDateTime.now(); + + tm.addNewEvent("task0", date, date); + tm.addNewToDo("task0"); + tm.addNewDeadline("task0", date); + + tm.addNewEvent("task1", date, date); + tm.addNewToDo("task1"); + tm.addNewDeadline("task1", date); + + + tm.addNewDeadline("unique desc", date); + + String out0 = getExpectedOutputBeginInputLoop(); + + String in0 = TextCommandUnderTest.generateTextCommandScanDuplicateDescription(); + String dupes = + "Duplicates \"[Description]\":[...(id,type)] " + System.lineSeparator() + "\"" + "task0" + + "\"" + ": [0 (E), 1 (T), 2 (D)]" + + System.lineSeparator() + "\"" + "task1" + "\"" + ": [3 (E), 4 (T), 5 (D)]"; + String out1 = getExpectedOutputTemplate(dupes); + String in1 = TextCommandUnderTest.generateTextCommandExit(); + String out2 = getExpectedOutputExitInputLoop(); + + System.setIn(buildCommandInputStream(in0, in1)); + String expectedOutput = buildExpectedResponse(out0, out1, out2); + new Ui(this.getPrintStream()).runTextCommandLoop(tm, null); + assertEquals(expectedOutput, this.getOutput()); + } +} diff --git a/src/test/java/duke/unittest/taskmanagertest/ExportTasksTest.java b/src/test/java/duke/unittest/taskmanagertest/ExportTasksTest.java new file mode 100644 index 00000000..98a582f0 --- /dev/null +++ b/src/test/java/duke/unittest/taskmanagertest/ExportTasksTest.java @@ -0,0 +1,35 @@ +package duke.unittest.taskmanagertest; + +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksTestExportPathString; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import duke.FileResourceManager; +import duke.TaskManager; +import duke.command.Command; +import duke.dukeutility.enums.ResponseType; +import duke.testhelper.TestStream; + + +public class ExportTasksTest extends TestStream { + /** + * This test checks if file path is writable. + */ + @Test + public void addToDos_export_shouldBuffer() { + TaskManager tm = new TaskManager(); + + String desc0 = "desc0"; + String desc1 = "desc1"; + tm.addNewToDo(desc0); + tm.addNewToDo(desc1); + assertEquals(desc0, tm.getTaskById(0).getTaskDescription()); + assertEquals(desc1, tm.getTaskById(1).getTaskDescription()); + + FileResourceManager frm = new FileResourceManager(getDefaultTasksTestExportPathString(), null); + Command c = frm.executeSave(tm); + assertEquals(ResponseType.FILE_SAVED, c.getResponseType()); + } + +} diff --git a/src/test/java/duke/unittest/taskmanagertest/ImportTasksTest.java b/src/test/java/duke/unittest/taskmanagertest/ImportTasksTest.java new file mode 100644 index 00000000..a1f97f2c --- /dev/null +++ b/src/test/java/duke/unittest/taskmanagertest/ImportTasksTest.java @@ -0,0 +1,58 @@ +package duke.unittest.taskmanagertest; + +import static duke.testhelper.help.config.DukeIoTestPath.getDefaultTasksTestExportPathString; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import com.google.gson.JsonArray; + +import duke.FileResourceManager; +import duke.TaskManager; +import duke.command.Command; +import duke.command.CommandJsonResponse; +import duke.dukeutility.enums.ResponseType; +import duke.testhelper.TestStream; + + +public class ImportTasksTest extends TestStream { + + /** + * Save tasks from task manager 1 to a file, extract tasks from file and load (import) into task manager 2. + * The tasks in each manager should be the same. + * That is, exported data and imported data should be idempotent. + */ + @Test + public void addToDos_exportExtractLoad() { + + // add new tasks to task manager 1 + TaskManager tm1 = new TaskManager(); + + String desc0 = "desc0"; + String desc1 = "desc1"; + tm1.addNewToDo(desc0); + tm1.addNewToDo(desc1); + assertEquals(desc0, tm1.getTaskById(0).getTaskDescription()); + assertEquals(desc1, tm1.getTaskById(1).getTaskDescription()); + + String exportPathString = getDefaultTasksTestExportPathString(); + String importPathString = exportPathString; + FileResourceManager frm = new FileResourceManager(exportPathString, importPathString); + + // export + Command c = frm.executeSave(tm1); + assertEquals(ResponseType.FILE_SAVED, c.getResponseType()); + // extract + CommandJsonResponse reading = frm.executeExtractTasksFromFile(); + assertEquals(ResponseType.FILE_READ, reading.getResponseType()); + // load / import + JsonArray tasksFromFile = (JsonArray) reading.getJsonArg(); + + TaskManager tm2 = new TaskManager(); + frm.importTasks(tasksFromFile, tm2); + + // compares tasks + assertEquals(tm2.getTaskById(0).getTaskDescription(), tm1.getTaskById(0).getTaskDescription()); + assertEquals(tm2.getTaskById(1).getTaskDescription(), tm1.getTaskById(1).getTaskDescription()); + } +} diff --git a/src/test/java/duke/unittest/taskmanagertest/TaskManagerTest.java b/src/test/java/duke/unittest/taskmanagertest/TaskManagerTest.java new file mode 100644 index 00000000..559356e7 --- /dev/null +++ b/src/test/java/duke/unittest/taskmanagertest/TaskManagerTest.java @@ -0,0 +1,71 @@ +package duke.unittest.taskmanagertest; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.time.LocalDateTime; +import org.junit.jupiter.api.Test; + +import duke.TaskManager; +import duke.task.model.Event; +import duke.testhelper.TestStream; +import duke.testhelper.help.codeundertest.ParserUnderTest; + +public class TaskManagerTest extends TestStream { + + + /** + * Add todos to a task manager. + */ + @Test + public void addToDos() { + TaskManager tm = new TaskManager(); + + String desc0 = "desc0"; + String desc1 = "desc1"; + tm.addNewToDo(desc0); + tm.addNewToDo(desc1); + assertEquals(desc0, tm.getTaskById(0).getTaskDescription()); + assertEquals(desc1, tm.getTaskById(1).getTaskDescription()); + } + + /** + * Add events to a task manager. + */ + @Test + public void addEvents() throws Exception { + TaskManager tm = new TaskManager(); + + String desc0 = "desc0"; + String desc1 = "desc1"; + tm.addNewEvent(desc0, ParserUnderTest.parseStringAsLocalDateTime("20210101"), + ParserUnderTest.parseStringAsLocalDateTime("20210101")); + + LocalDateTime from1 = ParserUnderTest.parseStringAsLocalDateTime("20210101"); + LocalDateTime to1 = ParserUnderTest.parseStringAsLocalDateTime("20210101"); + tm.addNewEvent(desc1, from1, to1); + + assertEquals(desc0, tm.getTaskById(0).getTaskDescription()); + assertEquals(desc1, tm.getTaskById(1).getTaskDescription()); + + assertEquals(from1, ((Event) tm.getTaskById(1)).getFrom()); + assertEquals(to1, ((Event) tm.getTaskById(1)).getTo()); + } + + /** + * Add find tasks in a task manager. + */ + @Test + public void findTasksByDescription() throws Exception { + TaskManager tm = new TaskManager(); + + String keyword = "MAGIK"; + LocalDateTime from0 = ParserUnderTest.parseStringAsLocalDateTime("20210101"); + LocalDateTime to0 = ParserUnderTest.parseStringAsLocalDateTime("20210202"); + LocalDateTime from1 = ParserUnderTest.parseStringAsLocalDateTime("20210101"); + LocalDateTime to1 = ParserUnderTest.parseStringAsLocalDateTime("20210202"); + tm.addNewEvent("desc0", from0, to0); + tm.addNewEvent("desc1 " + keyword + " a", from1, to1); // with keyword + + assertEquals(1, tm.getTasksWithKeywordInDescription(keyword).size()); + } +} diff --git a/src/test/java/duke/unittest/taskmanagertest/ToggleDoneTest.java b/src/test/java/duke/unittest/taskmanagertest/ToggleDoneTest.java new file mode 100644 index 00000000..98144fb6 --- /dev/null +++ b/src/test/java/duke/unittest/taskmanagertest/ToggleDoneTest.java @@ -0,0 +1,31 @@ +package duke.unittest.taskmanagertest; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import duke.TaskManager; +import duke.testhelper.TestStream; + +public class ToggleDoneTest extends TestStream { + + + /** + * Add todos to a task manager. + */ + @Test + public void addToDosSetUnsetDone() { + TaskManager tm = new TaskManager(); + + String desc0 = "desc0"; + tm.addNewToDo(desc0); + assertEquals(desc0, tm.getTaskById(0).getTaskDescription()); + + tm.getTaskByIdAndSetCompleted(0); + assertTrue(tm.getTaskById(0).isDone()); + tm.getTaskByIdAndSetIncomplete(0); + assertFalse(tm.getTaskById(0).isDone()); + } +} diff --git a/src/test/java/duke/unittest/tasktest/ComparisonTest.java b/src/test/java/duke/unittest/tasktest/ComparisonTest.java new file mode 100644 index 00000000..707a38d7 --- /dev/null +++ b/src/test/java/duke/unittest/tasktest/ComparisonTest.java @@ -0,0 +1,25 @@ +package duke.unittest.tasktest; + +import static duke.testhelper.help.codeundertest.ParserUnderTest.parseStringAsLocalDate; +import static duke.testhelper.help.codeundertest.ParserUnderTest.parseStringAsLocalDateTime; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import duke.task.TaskComparator; +import duke.task.model.Deadline; +import duke.testhelper.TestStream; + +public class ComparisonTest extends TestStream { + + @Test + public void shouldBeSameDay() throws Exception { + LocalDateTime dl = parseStringAsLocalDateTime("20200101 00:50"); + Deadline deadline = new Deadline("writing assignment", dl, 0, false); + LocalDate targetDate = parseStringAsLocalDate("20200101"); + Assertions.assertTrue(TaskComparator.isSameDay(deadline, targetDate)); + } +} + diff --git a/src/test/java/duke/unittest/tasktest/EventTest.java b/src/test/java/duke/unittest/tasktest/EventTest.java new file mode 100644 index 00000000..4b187252 --- /dev/null +++ b/src/test/java/duke/unittest/tasktest/EventTest.java @@ -0,0 +1,29 @@ +package duke.unittest.tasktest; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import duke.TaskManager; +import duke.command.Command; +import duke.command.commandfactory.UiCommandFactory; +import duke.command.errorcommand.CommandInvalidRequestParameters; +import duke.testhelper.help.codeundertest.TextCommandUnderTest; + + +public class EventTest { + @Test + void createEventTest_expectInvalidDateStringToReturnCommandError() { + String desc = "event1"; + String fromString = "nonsense day1"; + String toString = "asdasdasd asdasd "; + int id = 1; + boolean done = false; + + String textCommand = TextCommandUnderTest.generateTextCommandLineAddEvent(desc, fromString, toString); + Command c = new UiCommandFactory().executeTextCommand(textCommand, new TaskManager(), null); + + assertTrue(c instanceof CommandInvalidRequestParameters); + } + +} diff --git a/src/test/java/duke/unittest/tasktest/aggregator/TaskListTest.java b/src/test/java/duke/unittest/tasktest/aggregator/TaskListTest.java new file mode 100644 index 00000000..b636170c --- /dev/null +++ b/src/test/java/duke/unittest/tasktest/aggregator/TaskListTest.java @@ -0,0 +1,31 @@ +package duke.unittest.tasktest.aggregator; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import duke.task.aggregator.TaskList; +import duke.task.model.ToDo; +import duke.testhelper.TestStream; + +public class TaskListTest extends TestStream { + /** + * Adds tasks to task manager. + */ + @Test + public void addToDos() { + + TaskList tl = new TaskList(); + String task0Description = "task 0"; + tl.addTask(new ToDo(task0Description, 0, false)); + assertEquals(task0Description, tl.getTaskById(0).getTaskDescription()); + String task1Description = "task 1"; + tl.addTask(new ToDo(task1Description, 1, false)); + assertEquals(task1Description, tl.getTaskById(1).getTaskDescription()); + assertTrue(tl.containsKey(0)); + assertTrue(tl.containsKey(1)); + assertFalse(tl.containsKey(2)); + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT deleted file mode 100644 index 657e74f6..00000000 --- a/text-ui-test/EXPECTED.TXT +++ /dev/null @@ -1,7 +0,0 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/text-ui-test/runtest.bat b/text-ui-test/runtest.bat deleted file mode 100644 index 08737446..00000000 --- a/text-ui-test/runtest.bat +++ /dev/null @@ -1,21 +0,0 @@ -@ECHO OFF - -REM create bin directory if it doesn't exist -if not exist ..\bin mkdir ..\bin - -REM delete output from previous run -if exist ACTUAL.TXT del ACTUAL.TXT - -REM compile the code into the bin folder -javac -cp ..\src\main\java -Xlint:none -d ..\bin ..\src\main\java\*.java -IF ERRORLEVEL 1 ( - echo ********** BUILD FAILURE ********** - exit /b 1 -) -REM no error here, errorlevel == 0 - -REM run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ..\bin Duke < input.txt > ACTUAL.TXT - -REM compare the output to the expected output -FC ACTUAL.TXT EXPECTED.TXT diff --git a/text-ui-test/runtest.sh b/text-ui-test/runtest.sh deleted file mode 100644 index c9ec8700..00000000 --- a/text-ui-test/runtest.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -# create bin directory if it doesn't exist -if [ ! -d "../bin" ] -then - mkdir ../bin -fi - -# delete output from previous run -if [ -e "./ACTUAL.TXT" ] -then - rm ACTUAL.TXT -fi - -# compile the code into the bin folder, terminates if error occurred -if ! javac -cp ../src/main/java -Xlint:none -d ../bin ../src/main/java/*.java -then - echo "********** BUILD FAILURE **********" - exit 1 -fi - -# run the program, feed commands from input.txt file and redirect the output to the ACTUAL.TXT -java -classpath ../bin Duke < input.txt > ACTUAL.TXT - -# convert to UNIX format -cp EXPECTED.TXT EXPECTED-UNIX.TXT -dos2unix ACTUAL.TXT EXPECTED-UNIX.TXT - -# compare the output to the expected output -diff ACTUAL.TXT EXPECTED-UNIX.TXT -if [ $? -eq 0 ] -then - echo "Test result: PASSED" - exit 0 -else - echo "Test result: FAILED" - exit 1 -fi \ No newline at end of file