Merge pull request #1 from FontysVenlo/dev

Dev
This commit is contained in:
xGl0ck
2025-10-18 15:13:13 +02:00
committed by GitHub
21 changed files with 1346 additions and 21 deletions

11
.gitignore vendored
View File

@@ -78,6 +78,7 @@ cmake-build-*/
# IntelliJ # IntelliJ
out/ out/
.idea
# mpeltonen/sbt-idea plugin # mpeltonen/sbt-idea plugin
.idea_modules/ .idea_modules/
@@ -127,7 +128,8 @@ http-client.private.env.json
__MACOSX/ __MACOSX/
.AppleDouble .AppleDouble
.LSOverride .LSOverride
Icon[ Icon[
]
# Thumbnails # Thumbnails
._* ._*
@@ -150,3 +152,10 @@ Temporary Items
### Custom rules ### Custom rules
target target
assignment/.classpath
assignment/.project
assignment/.settings/org.eclipse.core.resources.prefs
assignment/.settings/org.eclipse.jdt.apt.core.prefs
assignment/.settings/org.eclipse.jdt.core.prefs
assignment/.settings/org.eclipse.m2e.core.prefs
.obsidian

View File

@@ -0,0 +1,144 @@
---
excalidraw-plugin: parsed
tags: [excalidraw]
---
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠== You can decompress Drawing data with the command palette: 'Decompress current Excalidraw file'. For more info check in plugin settings under 'Saving'
# Excalidraw Data
## Text Elements
CustomListList ^P7Hc0rRT
T add(T element) ^ilmdVsfP
head: Node = default null ^tPykSiIW
head is null ^Ml0BvnOF
new node with element ^cOy0ATvK
head is new node ^2wxZhqIw
head.next is new node ^B0QMOJrU
int size() ^l8GER5ik
recursion (count, element) ^imgFdzPB
element has next ^2e5NuXLN
return call(count++) ^ER1bqK84
return count ^plS5EIIg
head is null ^v4SnCGRz
return 0 ^Vapvn56m
%%
## Drawing
```compressed-json
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
QgAA
```
%%

View File

@@ -34,13 +34,12 @@ public class APFactory implements AbstractAPFactory {
@Override @Override
public AppointmentData createAppointmentData(String description, Duration duration) { public AppointmentData createAppointmentData(String description, Duration duration) {
//TODO Return an instance of your class that implements AppointmentData return new AppointmentDataImpl(duration, description);
return null;
} }
@Override @Override
public AppointmentRequest createAppointmentRequest(AppointmentData appData, LocalTime prefStart, TimePreference fallBack) { public AppointmentRequest createAppointmentRequest(AppointmentData appData, LocalTime prefStart,
//TODO Return an instance of your class that implements AppointmentRequest TimePreference fallBack) {
return null; return new AppointmentRequestImpl(appData, prefStart, fallBack);
} }
} }

View File

@@ -0,0 +1,26 @@
package appointmentplanner;
import appointmentplanner.api.AppointmentData;
import java.time.Duration;
public class AppointmentDataImpl implements AppointmentData {
private Duration duration;
private String description;
public AppointmentDataImpl(Duration duration, String description) {
this.duration = duration;
this.description = description;
}
@Override
public Duration duration() {
return this.duration;
}
@Override
public String description() {
return this.description;
}
}

View File

@@ -0,0 +1,30 @@
package appointmentplanner;
import java.time.Instant;
import appointmentplanner.api.Appointment;
import appointmentplanner.api.AppointmentRequest;
public class AppointmentImpl implements Appointment {
private Instant start;
private Instant stop;
private AppointmentRequest request;
@Override
public Instant start() {
return start;
}
@Override
public Instant end() {
return stop;
}
@Override
public AppointmentRequest request() {
return request;
}
}

View File

@@ -0,0 +1,41 @@
package appointmentplanner;
import java.time.LocalTime;
import appointmentplanner.api.AppointmentData;
import appointmentplanner.api.AppointmentRequest;
import appointmentplanner.api.TimePreference;
public class AppointmentRequestImpl implements AppointmentRequest {
private AppointmentData data;
private LocalTime startTime;
private TimePreference timePreference;
public AppointmentRequestImpl(AppointmentData data, LocalTime startTime, TimePreference timePreference) {
this.data = data;
this.startTime = startTime;
this.timePreference = timePreference;
}
public AppointmentRequestImpl(AppointmentData data, LocalTime startTime) {
this.data = data;
this.startTime = startTime;
}
@Override
public LocalTime startTime() {
return this.startTime;
}
@Override
public AppointmentData appointmentData() {
return this.data;
}
@Override
public TimePreference timePreference() {
return this.timePreference;
}
}

View File

@@ -0,0 +1,121 @@
package appointmentplanner;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalTime;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import appointmentplanner.api.Appointment;
import appointmentplanner.api.AppointmentData;
import appointmentplanner.api.AppointmentRequest;
import appointmentplanner.api.LocalDay;
import appointmentplanner.api.LocalDayPlan;
import appointmentplanner.api.TimePreference;
import appointmentplanner.api.TimeSlot;
import appointmentplanner.customlist.CustomLinkedListImpl;
import appointmentplanner.customlist.api.CustomLinkedList;
public class LocalDayPlanImpl implements LocalDayPlan {
private CustomLinkedList<AppointmentImpl> timeline = new CustomLinkedListImpl<>();
public LocalDayPlanImpl(LocalDay day, Instant start, Instant end) {
this.day = day;
this.start = start;
this.end = end;
}
private LocalDay day;
private Instant start;
private Instant end;
@Override
public LocalDay day() {
return day;
}
@Override
public Instant startOfDay() {
return start;
}
@Override
public Instant endOfDay() {
return end;
}
@Override
public Optional<Appointment> addAppointment(AppointmentData appointmentData, LocalTime start,
TimePreference fallback) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'addAppointment'");
}
@Override
public Optional<Appointment> addAppointment(AppointmentData appointmentData, LocalTime startTime) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'addAppointment'");
}
@Override
public Optional<Appointment> addAppointment(AppointmentData appointmentData, TimePreference preference) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'addAppointment'");
}
@Override
public AppointmentRequest removeAppointment(Appointment appointment) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'removeAppointment'");
}
@Override
public List<AppointmentRequest> removeAppointments(Predicate<Appointment> filter) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'removeAppointments'");
}
@Override
public List<Appointment> appointments() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'appointments'");
}
@Override
public List<TimeSlot> findMatchingFreeSlotsOfDuration(Duration duration, List<LocalDayPlan> plans) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'findMatchingFreeSlotsOfDuration'");
}
@Override
public List<TimeSlot> findGapsFitting(Duration duration) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'findGapsFitting'");
}
@Override
public List<Appointment> findAppointments(Predicate<Appointment> filter) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'findAppointments'");
}
@Override
public boolean contains(Appointment appointment) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'contains'");
}
@Override
public int nrOfAppointments() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'nrOfAppointments'");
}
@Override
public String toString() {
return String.format("Day: %s, TimeZone: %s", day.date(), day().zone());
}
}

View File

@@ -0,0 +1,175 @@
package appointmentplanner.customlist;
import java.util.Iterator;
import appointmentplanner.customlist.api.CustomLinkedList;
public class CustomLinkedListImpl<T> implements CustomLinkedList<T> {
CustomLinkedListNode<T> head = null;
@Override
public Iterator<T> iterator() {
return new CustomLinkedListIterator<T>(this);
}
@Override
public void add(T item) {
CustomLinkedListNode<T> newNode = new CustomLinkedListNode<>(item);
CustomLinkedListNode<T> oldHead = head;
head = newNode;
if (oldHead != null) {
head.setNext(oldHead);
}
}
private static enum ItemPosition {
AFTER, BEFORE, CURRENT
}
private CustomLinkedListNode<T> traverseFind(CustomLinkedListNode<T> currentNode, T item, ItemPosition position) {
if (currentNode == null) {
return null;
}
CustomLinkedListNode<T> nextNode = currentNode.getNext();
switch (position) {
case CURRENT:
if (currentNode.getItem().equals(item)) {
return currentNode;
}
break;
case BEFORE:
if (nextNode == null) {
return null;
}
if (nextNode.getItem().equals(item)) {
return currentNode;
}
break;
case AFTER:
if (nextNode == null) {
return null;
}
if (currentNode.getItem().equals(item)) {
return nextNode;
}
break;
default:
return null;
}
if (nextNode == null) {
return null;
}
return traverseFind(nextNode, item, position);
}
@Override
public void remove(T item) {
if (head == null) {
return;
}
if (head.getItem().equals(item)) {
head = head.getNext();
return;
}
CustomLinkedListNode<T> beforeNode = traverseFind(head, item, ItemPosition.BEFORE);
if (beforeNode == null) {
return;
}
CustomLinkedListNode<T> nodeToRemove = beforeNode.getNext();
if (beforeNode.getNext() == null) {
beforeNode.setNext(null);
return;
}
beforeNode.setNext(nodeToRemove.getNext());
}
@Override
public void insertAfter(T reference, T item) {
if (head.getItem().equals(reference)) {
add(item);
return;
}
CustomLinkedListNode<T> nodeBefore = traverseFind(head, reference, ItemPosition.BEFORE);
CustomLinkedListNode<T> nodeToInsert = new CustomLinkedListNode<>(nodeBefore.getNext(), item);
nodeBefore.setNext(nodeToInsert);
}
@Override
public void insertBefore(T reference, T item) {
CustomLinkedListNode<T> referenceNode = traverseFind(head, reference, ItemPosition.CURRENT);
CustomLinkedListNode<T> nodeToInsert = new CustomLinkedListNode<>(referenceNode.getNext(), item);
referenceNode.setNext(nodeToInsert);
}
@Override
public T getAfter(T reference) {
// head represents the last inserted item thus making entire list in reverse
// order
CustomLinkedListNode<T> node = traverseFind(head, reference, ItemPosition.BEFORE);
return node == null ? null : node.getItem();
}
@Override
public T getBefore(T reference) {
// head represents the last inserted item thus making entire list in reverse
// order
CustomLinkedListNode<T> node = traverseFind(head, reference, ItemPosition.AFTER);
return node == null ? null : node.getItem();
}
@Override
public boolean contains(T item) {
return traverseFind(head, item, ItemPosition.CURRENT) != null ? true : false;
}
private int recursiveSizeCalc(CustomLinkedListNode<T> node, int count) {
if (node == null) {
return 0;
}
if (node.getNext() == null) {
return count;
}
return recursiveSizeCalc(node.getNext(), count + 1);
}
@Override
public int size() {
return recursiveSizeCalc(this.head, 1);
}
}

View File

@@ -0,0 +1,30 @@
package appointmentplanner.customlist;
import java.util.Iterator;
import java.util.Iterator;
public class CustomLinkedListIterator<T> implements Iterator<T> {
private CustomLinkedListImpl<T> list;
private CustomLinkedListNode<T> lastNode;
public CustomLinkedListIterator(CustomLinkedListImpl<T> listToIterate) {
list = listToIterate;
lastNode = list.head;
}
@Override
public boolean hasNext() {
return lastNode.getNext() != null;
}
@Override
public T next() {
T item = lastNode.getItem();
lastNode = lastNode.getNext();
return item;
}
}

View File

@@ -0,0 +1,28 @@
package appointmentplanner.customlist;
public class CustomLinkedListNode<T> {
private T item;
private CustomLinkedListNode<T> next;
public CustomLinkedListNode(CustomLinkedListNode<T> next, T item) {
this.next = next;
this.item = item;
}
public CustomLinkedListNode(T item) {
this(null, item);
}
public T getItem() {
return item;
}
public CustomLinkedListNode<T> getNext() {
return next;
}
public void setNext(CustomLinkedListNode<T> next) {
this.next = next;
}
}

View File

@@ -0,0 +1,157 @@
package appointmentplanner.customlist;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import appointmentplanner.customlist.api.*;
public class CustomListToJavaBinding<T> implements List<T> {
private CustomLinkedList<T> list;
public CustomListToJavaBinding(CustomLinkedList<T> list) {
this.list = list;
}
@Override
public boolean add(T arg0) {
list.add(arg0);
return true;
}
@Override
public void add(int arg0, T arg1) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Cannot add to exact index in linked list");
}
@Override
public boolean addAll(Collection<? extends T> c) {
c.stream().forEach(list::add);
return true;
}
@Override
public boolean addAll(int index, Collection<? extends T> c) {
throw new UnsupportedOperationException("Cannot add to exact index in linked list");
}
@Override
public void clear() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'clear'");
}
@Override
public boolean containsAll(Collection<?> c) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'containsAll'");
}
@Override
public T get(int index) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'get'");
}
@Override
public int indexOf(Object o) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'indexOf'");
}
@Override
public boolean isEmpty() {
return list.size() == 0;
}
@Override
public Iterator<T> iterator() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'iterator'");
}
@Override
public int lastIndexOf(Object o) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'lastIndexOf'");
}
@Override
public ListIterator<T> listIterator() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'listIterator'");
}
@Override
public ListIterator<T> listIterator(int index) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'listIterator'");
}
@Override
public boolean remove(Object o) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'remove'");
}
@Override
public T remove(int index) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'remove'");
}
@Override
public boolean removeAll(Collection<?> c) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'removeAll'");
}
@Override
public boolean retainAll(Collection<?> c) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'retainAll'");
}
@Override
public T set(int arg0, T arg1) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'set'");
}
@Override
public int size() {
return list.size();
}
@Override
public List<T> subList(int fromIndex, int toIndex) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'subList'");
}
@Override
public Object[] toArray() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'toArray'");
}
@Override
public <T> T[] toArray(T[] arg0) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'toArray'");
}
@Override
public boolean contains(Object o) {
try {
T bal = (T) o;
return list.contains(bal);
} catch (Exception e) {
return false;
}
}
}

View File

@@ -0,0 +1,21 @@
package appointmentplanner.customlist.api;
public interface CustomLinkedList<T> extends Iterable<T> {
void add(T item);
void remove(T item);
void insertAfter(T reference, T item);
void insertBefore(T reference, T item);
T getAfter(T reference);
T getBefore(T reference);
boolean contains(T item);
int size();
}

View File

@@ -0,0 +1,33 @@
package appointmentplanner;
import static org.assertj.core.api.Assertions.assertThat;
import appointmentplanner.api.AppointmentData;
import java.time.Duration;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
public class AppointmentDataTest {
@ParameterizedTest
@CsvSource({ "56", "-5", "30" })
void getDurationTest(long duration) {
Duration dur = Duration.ofHours(duration);
AppointmentData instnace = new AppointmentDataImpl(dur, "smth");
assertThat(instnace.duration()).isEqualTo(dur);
}
@ParameterizedTest
@CsvSource({ "one", "two", "three" })
void getDescriptionTest(String desc) {
String descString = desc;
AppointmentData instance = new AppointmentDataImpl(
Duration.ofDays(5),
desc);
assertThat(instance.description()).isEqualTo(descString);
}
}

View File

@@ -0,0 +1,76 @@
package appointmentplanner;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalTime;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import appointmentplanner.api.AppointmentData;
import appointmentplanner.api.LocalDay;
import appointmentplanner.api.LocalDayPlan;
import appointmentplanner.api.TimePreference;
public class AppointmentFactoryTest {
private static APFactory factory = new APFactory();
static Stream<Arguments> localDayPlanProvider() {
return Stream.of(
Arguments.of(TestData.TODAY, Instant.parse("2025-10-13T08:00:00Z"), Instant.parse("2025-10-13T12:00:00Z")),
Arguments.of(TestData.TODAY, Instant.parse("2025-10-14T09:00:00Z"), Instant.parse("2025-10-14T11:00:00Z")),
Arguments.of(TestData.TODAY, Instant.parse("2025-10-15T10:00:00Z"), Instant.parse("2025-10-15T14:00:00Z")));
}
// @ParameterizedTest
// @MethodSource("localDayPlanProvider")
void testCreateLocalDayPlan_shouldCreateSccessfully(LocalDay localDay, Instant start, Instant end) {
LocalDayPlan plan = factory.createLocalDayPlan(localDay, start, end);
assertThat(plan.day()).isEqualTo(localDay);
assertThat(plan.startOfDay()).isEqualTo(start);
assertThat(plan.endOfDay()).isEqualTo(end);
}
static Stream<Arguments> appointmentDataProvider() {
return Stream.of(
Arguments.of("Meeting with team", TestData.D30),
Arguments.of("Doctor's appointment", TestData.D15),
Arguments.of("Project discussion", TestData.D90));
}
@ParameterizedTest
@MethodSource("appointmentDataProvider")
void testCreateAppintmentData_shouldCreateSuccessfully(String description, Duration duration) {
var appData = factory.createAppointmentData(description, duration);
assertThat(appData.description()).isEqualTo(description);
assertThat(appData.duration()).isEqualTo(duration);
}
static Stream<Arguments> appointmentRequestProvider() {
return Stream.of(
Arguments.of(factory.createAppointmentData("Meeting with team", TestData.D30), TestData.T09_00,
TimePreference.UNSPECIFIED),
Arguments.of(factory.createAppointmentData("Doctor's appointment", TestData.D15), TestData.T10_30,
TimePreference.EARLIEST),
Arguments.of(factory.createAppointmentData("Project discussion", TestData.D90), TestData.T16_00,
TimePreference.LATEST));
}
@ParameterizedTest
@MethodSource("appointmentRequestProvider")
void testCreateAppointmentRequest_shouldCreateSuccessfully(AppointmentData appData,
LocalTime prefStart, TimePreference timePref) {
var appRequest = factory.createAppointmentRequest(appData, prefStart, timePref);
assertThat(appRequest.appointmentData()).isEqualTo(appData);
assertThat(appRequest.startTime()).isEqualTo(prefStart);
assertThat(appRequest.timePreference()).isEqualTo(timePref);
}
}

View File

@@ -0,0 +1,41 @@
package appointmentplanner;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.Duration;
import java.time.LocalTime;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import appointmentplanner.api.AppointmentData;
import appointmentplanner.api.AppointmentRequest;
public class AppointmentRequestImplTest {
private static Stream<Arguments> provideAppointmentRequestArgumnets() {
return Stream.of(
Arguments.of(TestData.AR1, TestData.T09_00, TestData.DATA1),
Arguments.of(TestData.AR2, TestData.T09_30, TestData.DATA2),
Arguments.of(TestData.AR3, TestData.T10_30, TestData.DATA3),
Arguments.of(TestData.AR4, TestData.T10_45, TestData.DATA4),
Arguments.of(TestData.AR5, TestData.T11_10, TestData.DATA5),
Arguments.of(TestData.AR6, TestData.T14_30, TestData.DATA6),
Arguments.of(TestData.AR7, TestData.T16_00, TestData.DATA7));
}
@ParameterizedTest
@MethodSource("provideAppointmentRequestArgumnets")
void apReqStartTime_shouldReturnCorrectTimeSetInConstructor(AppointmentRequest testetReq, LocalTime expectedTime) {
assertThat(testetReq.startTime()).isEqualTo(expectedTime);
}
@ParameterizedTest
@MethodSource("provideAppointmentRequestArgumnets")
void apReqAppData_shouldReturnCorrectAppDataSetInConstructor(AppointmentRequest testReq, LocalTime expectedTime,
AppointmentData data) {
assertThat(testReq.appointmentData()).isEqualTo(data);
}
}

View File

@@ -0,0 +1,48 @@
package appointmentplanner;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.Instant;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import appointmentplanner.api.LocalDay;
import appointmentplanner.api.LocalDayPlan;
public class LocalDayPlanTest {
private static Stream<Arguments> provideLocalDPDataset() {
return Stream.of(
Arguments.of(new LocalDay(), Instant.parse("2023-01-01T08:30:00Z"), Instant.parse("2023-01-01T17:30:00Z")),
Arguments.of(new LocalDay(), Instant.parse("2023-06-15T09:00:00Z"), Instant.parse("2023-06-15T18:00:00Z")),
Arguments.of(new LocalDay(), Instant.parse("2024-12-31T07:00:00Z"), Instant.parse("2024-12-31T16:00:00Z")));
}
@ParameterizedTest
@MethodSource("provideLocalDPDataset")
void ldPlanInit_shouldBeSuccessful(LocalDay day, Instant start, Instant end) {
LocalDayPlan plan = new LocalDayPlanImpl(day, start, end);
assertThat(plan).isNotNull();
}
@ParameterizedTest
@MethodSource("provideLocalDPDataset")
void ldPlanGetters_shouldReturnSetValues(LocalDay day, Instant start, Instant end) {
LocalDayPlan plan = new LocalDayPlanImpl(day, start, end);
assertThat(plan.day()).isEqualTo(day);
assertThat(plan.startOfDay()).isEqualTo(start);
assertThat(plan.endOfDay()).isEqualTo(end);
}
@ParameterizedTest
@MethodSource("provideLocalDPDataset")
void ldToString_shouldReturnStringContainingLocalDateAndTimeZone(LocalDay day, Instant start, Instant end) {
LocalDayPlan plan = new LocalDayPlanImpl(day, start, end);
assertThat(plan.toString()).contains(day.zone().toString(), day.date().toString());
}
}

View File

@@ -0,0 +1,18 @@
package appointmentplanner;
import org.junit.jupiter.params.converter.ArgumentConversionException;
import org.junit.jupiter.params.converter.SimpleArgumentConverter;
public class StringArrayConverter extends SimpleArgumentConverter {
@Override
protected Object convert(Object source, Class<?> targetType) throws ArgumentConversionException {
if (source instanceof String && String[].class.isAssignableFrom(targetType)) {
return ((String) source).split("\\s*,\\s*");
} else {
throw new IllegalArgumentException("Conversion from " + source.getClass() + " to "
+ targetType + " not supported.");
}
}
}

View File

@@ -0,0 +1,108 @@
package appointmentplanner;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import appointmentplanner.api.AbstractAPFactory;
import appointmentplanner.api.AppointmentData;
import appointmentplanner.api.AppointmentRequest;
import appointmentplanner.api.LocalDay;
import appointmentplanner.api.LocalDayPlan;
import appointmentplanner.api.TimePreference;
import net.bytebuddy.asm.Advice.Local;
public interface TestData {
static final AbstractAPFactory FAC = ServiceFinder.getFactory();
static final LocalDay TODAY = new LocalDay();
static final LocalTime T08_30 = LocalTime.of(8, 30);
static final LocalTime T09_00 = LocalTime.of(9, 0);
static final LocalTime T09_30 = LocalTime.of(9, 30);
static final LocalTime T10_00 = LocalTime.of(10, 0);
static final LocalTime T10_30 = LocalTime.of(10, 30);
static final LocalTime T10_45 = LocalTime.of(10, 45);
static final LocalTime T11_10 = LocalTime.of(11, 10);
static final LocalTime T14_30 = LocalTime.of(14, 30);
static final LocalTime T15_00 = LocalTime.of(15, 0);
static final LocalTime T15_15 = LocalTime.of(15, 15);
static final LocalTime T15_45 = LocalTime.of(15, 45);
static final LocalTime T16_00 = LocalTime.of(16, 00);
static final LocalTime T17_30 = LocalTime.of(17, 30);
static final LocalTime WORKINGDAY_START = T08_30;
static final LocalTime WORKINGDAY_END = T17_30;
static final Duration D15 = Duration.ofMinutes(15);
static final Duration D30 = Duration.ofMinutes(30);
static final Duration D80 = Duration.ofMinutes(80);
static final Duration D90 = Duration.ofMinutes(90);
static final Duration D200 = Duration.ofMinutes(200);
static final AppointmentData DATA1 = FAC.createAppointmentData("app1 30 min @9:00", D30);
static final AppointmentData DATA2 = FAC.createAppointmentData("app2 30 min @9:30", D30);
static final AppointmentData DATA3 = FAC.createAppointmentData("app3 15 min @10:30", D15);
static final AppointmentData DATA4 = FAC.createAppointmentData("app4 15 min @10:45", D15);
static final AppointmentData DATA5 = FAC.createAppointmentData("app5 200 min @11:10", D200);
static final AppointmentData DATA6 = FAC.createAppointmentData("app6 30 min @14:30", D30);
static final AppointmentData DATA7 = FAC.createAppointmentData("app7 90 min @16:00", D90);
static final AppointmentRequest AR1 = FAC.createAppointmentRequest(DATA1, T09_00, TimePreference.UNSPECIFIED);
static final AppointmentRequest AR2 = FAC.createAppointmentRequest(DATA2, T09_30);
static final AppointmentRequest AR3 = FAC.createAppointmentRequest(DATA3, T10_30);
static final AppointmentRequest AR4 = FAC.createAppointmentRequest(DATA4, T10_45);
static final AppointmentRequest AR5 = FAC.createAppointmentRequest(DATA5, T11_10);
static final AppointmentRequest AR6 = FAC.createAppointmentRequest(DATA6, T14_30);
static final AppointmentRequest AR7 = FAC.createAppointmentRequest(DATA7, T16_00, TimePreference.EARLIEST);
static LocalDayPlan standardDay() {
LocalDayPlan td = emptyWorkingDay();
addApps(td, AR1, AR2, AR3, AR4, AR5, AR6, AR7);
return td;
}
static LocalDayPlan emptyWorkingDay() {
return emptyWorkingDay(WORKINGDAY_START);
}
static LocalDayPlan emptyWorkingDay(LocalTime startTime) {
return FAC.createLocalDayPlan(TODAY, startTime, WORKINGDAY_END);
}
static LocalDayPlan addApps(LocalDayPlan dp, AppointmentRequest... app) {
for (AppointmentRequest ar : app) {
dp.addAppointment(ar.appointmentData(), ar.startTime(), ar.timePreference());
}
return dp;
}
static LocalDayPlan dayPlanFactory(String startTime, String endTime) {
return FAC.createLocalDayPlan(TODAY, LocalTime.parse(startTime), LocalTime.parse(endTime));
}
static LocalDayPlan dayPlanFactory(LocalDay day, String startTime, String endTime) {
return FAC.createLocalDayPlan(day, LocalTime.parse(startTime), LocalTime.parse(endTime));
}
static final AppointmentData APD1 = appointmentDataFactory(30, "Some app1");
static final AppointmentData APD2 = appointmentDataFactory(30, "Some app2");
static final AppointmentData APD3 = appointmentDataFactory(15, "Some app3");
static final AppointmentData APD4 = appointmentDataFactory(15, "Some app4");
static final AppointmentData APD5 = appointmentDataFactory(200, "Some app5");
static final AppointmentData APD6 = appointmentDataFactory(30, "Some app6");
static final AppointmentData APD7 = appointmentDataFactory(90, "Some app7");
static final Instant T13_10 = TODAY.at(13, 10);
static AppointmentData appointmentDataFactory(int duration, String someApp) {
return FAC.createAppointmentData(someApp, Duration.ofMinutes(duration));
}
static LocalDayPlan createStandardDay(LocalDate at) {
LocalDay ld = new LocalDay(ZoneId.systemDefault(), at);
return FAC.createLocalDayPlan(ld, ld.at(8, 30), ld.at(17, 30));
}
}

View File

@@ -0,0 +1,10 @@
package appointmentplanner;
import java.time.Duration;
public class TestUtils {
public static Duration makeDuration(String durationString) {
return Duration.ofHours(Long.parseLong(durationString));
}
}

View File

@@ -0,0 +1,41 @@
package appointmentplanner.customlist;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class CustomListNodeTest {
@Test
void newListNode_shouldCreaeteNotNullNodeObject() {
CustomLinkedListNode<String> node = new CustomLinkedListNode<>(null, "item1");
assertThat(node).isNotNull();
}
@Test
void setNext_shouldSetNextNodeSuccessfully() {
CustomLinkedListNode<String> node1 = new CustomLinkedListNode<>(null, "item1");
CustomLinkedListNode<String> node2 = new CustomLinkedListNode<>(null, "item2");
node1.setNext(node2);
assertThat(node1.getNext()).isEqualTo(node2);
}
@Test
void getItem_shouldReturnSetItemSuccessfully() {
CustomLinkedListNode<String> node = new CustomLinkedListNode<>(null, "item1");
assertThat(node.getItem()).isEqualTo("item1");
}
@Test
void getNext_shouldReturnSetNextNodeSuccessfully() {
CustomLinkedListNode<String> node1 = new CustomLinkedListNode<>(null, "item1");
CustomLinkedListNode<String> node2 = new CustomLinkedListNode<>(null, "item2");
node1.setNext(node2);
assertThat(node1.getNext()).isEqualTo(node2);
}
}

View File

@@ -0,0 +1,169 @@
package appointmentplanner.customlist.api;
import static org.assertj.core.api.Assertions.*;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.converter.ConvertWith;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import appointmentplanner.StringArrayConverter;
import appointmentplanner.customlist.CustomLinkedListImpl;
public class CustomLinkedListTest {
@Test
void cllAddandCllSize_shouldAddElementSuccessfullyAndCalculateSizeSuccessfully() {
CustomLinkedList<String> list = new CustomLinkedListImpl<>();
int counter = 0;
assertThat(list.size()).isEqualTo(counter);
for (String word : new String[] { "Ahoj", "jak", "se", "mas", "vole" }) {
counter++;
list.add(word);
assertThat(list.size()).isEqualTo(counter);
}
}
@ParameterizedTest
@CsvSource({
"'Ahoj, jak, to, jde', 'to', true",
"'Ty, jsi, ale, hloupy', 'to', false"
})
void cllContains_shouldReturnCorrectResult(@ConvertWith(StringArrayConverter.class) String[] data, String toContain,
boolean shouldContain) {
CustomLinkedList<String> list = new CustomLinkedListImpl<>();
Stream.of(data).forEach(list::add);
assertThat(list.contains(toContain)).isEqualTo(shouldContain);
}
@ParameterizedTest
@CsvSource({
"'Tak, jsme, to, cele, totalne, dosrali, ale, co, uz', 'totalne'",
"'Tak, jsme, to, cele, totalne, dosrali, ale, co, uz', 'Tak'",
"'Tak, jsme, to, cele, totalne, dosrali, ale, co, uz', 'uz'",
"'Tak, jsme, to, cele, totalne, dosrali, ale, co, uz', 'non-existing'",
"'only', 'only'",
})
void cllRemove_shouldRemoveItemSuccessfully(@ConvertWith(StringArrayConverter.class) String[] data, String toRemove) {
CustomLinkedList<String> list = new CustomLinkedListImpl<>();
Stream.of(data).forEach(list::add);
list.remove(toRemove);
assertThat(list.contains(toRemove)).isFalse();
}
private static enum LlItemPosition {
BEFORE, AFTER
}
private static String[] someBasicDataSet = new String[] { "O", "MUJ", "BOZE" };
private static Stream<Arguments> getAfterBeforeData() {
return Stream.of(
Arguments.of(someBasicDataSet, LlItemPosition.BEFORE, "MUJ", "O"),
Arguments.of(someBasicDataSet, LlItemPosition.AFTER, "MUJ", "BOZE"),
Arguments.of(someBasicDataSet, LlItemPosition.AFTER, "O", "MUJ"),
Arguments.of(someBasicDataSet, LlItemPosition.AFTER, "BOZE", null),
Arguments.of(someBasicDataSet, LlItemPosition.BEFORE, "O", null));
}
@ParameterizedTest
@MethodSource("getAfterBeforeData")
void cllGetAfter_shouldReturnCorrectResult(String[] data, LlItemPosition position, String reference,
String expectedResult) {
CustomLinkedList<String> list = new CustomLinkedListImpl<>();
Stream.of(data).forEach(list::add);
switch (position) {
case BEFORE:
assertThat(list.getBefore(reference)).isEqualTo(expectedResult);
break;
case AFTER:
assertThat(list.getAfter(reference)).isEqualTo(expectedResult);
break;
default:
fail("Incorrect parameters");
break;
}
}
private static Stream<Arguments> insertBeforeAfterData() {
return Stream.of(
Arguments.of(someBasicDataSet, "DOPRDELE", "BOZE", LlItemPosition.AFTER),
Arguments.of(new String[] { "Ahoj" }, "vole", "Ahoj", LlItemPosition.AFTER),
Arguments.of(new String[] { "ahoj" }, "More", "ahoj", LlItemPosition.BEFORE),
Arguments.of(new String[] { "A", "tak", "se", "pochcal", "posral", "a", "jeste", "nakonec", "serval" },
"vyblil",
"posral", LlItemPosition.BEFORE),
Arguments.of(new String[] { "A", "tak", "se", "pochcal", "posral", "a", "jeste", "nakonec", "serval" },
"vyblil",
"posral", LlItemPosition.AFTER),
Arguments.of(someBasicDataSet, "PREMOCNY", "BOZE", LlItemPosition.BEFORE));
}
@ParameterizedTest
@MethodSource("insertBeforeAfterData")
void cllInsertAfterBefore_shouldInsertItemSuccessfully(String[] initData, String toInsert, String reference,
LlItemPosition position) {
CustomLinkedList<String> list = initPopulatedList(initData);
switch (position) {
case AFTER:
list.insertAfter(reference, toInsert);
assertThat(list.getAfter(reference)).isEqualTo(toInsert);
break;
case BEFORE:
list.insertBefore(reference, toInsert);
assertThat(list.getBefore(reference)).isEqualTo(toInsert);
break;
default:
fail("Incorrect position parameter");
break;
}
}
@Test
void cllIterate_shouldPerformCorrectIteration() {
String[] data = new String[] { "Ahoj", "jak", "se", "mas", "?" };
CustomLinkedList<String> list = initPopulatedList(data);
int counter = data.length - 1;
for (String element : list) {
assertThat(element).isEqualTo(data[counter]);
counter--;
}
}
private CustomLinkedList<String> initPopulatedList(String[] initData) {
CustomLinkedList<String> list = new CustomLinkedListImpl<>();
Stream.of(initData).forEach(list::add);
return list;
}
}