Commit 34439f4a authored by Ethan Ordentlich's avatar Ethan Ordentlich
Browse files

Merge branch 'tests' into 'master'

Review Tests for Project 6

Closes #2, #3, #6, #8, #9, #10, #11, and #12

See merge request !1
1 merge request!1Review Tests for Project 6
Pipeline #45809 canceled with stage
Showing with 660 additions and 369 deletions
+660 -369
<component name="libraryTable">
<library name="org.hamcrest:hamcrest-all:1.3" type="repository">
<properties maven-id="org.hamcrest:hamcrest-all:1.3" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
\ No newline at end of file
<component name="libraryTable">
<library name="org.jsoup:jsoup:1.11.31" type="repository">
<properties maven-id="org.jsoup:jsoup:1.11.3" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jsoup/jsoup/1.11.3/jsoup-1.11.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
\ No newline at end of file
<component name="libraryTable">
<library name="org.junit.jupiter:junit-jupiter-params:5.4.0-RC2" type="repository">
<properties maven-id="org.junit.jupiter:junit-jupiter-params:5.4.0-M1" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-params/5.4.0-M1/junit-jupiter-params-5.4.0-M1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.0.0/apiguardian-api-1.0.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.4.0-M1/junit-jupiter-api-5.4.0-M1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.1.1/opentest4j-1.1.1.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.4.0-M1/junit-platform-commons-1.4.0-M1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11.0.5" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11.0.9" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
This diff is collapsed.
......@@ -8,9 +8,9 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="org.hamcrest:hamcrest-all:1.3" level="project" />
<orderEntry type="library" name="org.junit.jupiter:junit-jupiter-params:5.4.0-RC2" level="project" />
<orderEntry type="library" name="com.github.javaparser:javaparser-core:3.5.121" level="project" />
<orderEntry type="library" name="org.jsoup:jsoup:1.11.31" level="project" />
<orderEntry type="library" name="org.hamcrest:hamcrest-core:2.2" level="project" />
<orderEntry type="library" name="org.jsoup:jsoup:1.13.1" level="project" />
<orderEntry type="library" name="org.junit.jupiter:junit-jupiter:5.6.0-M1" level="project" />
</component>
</module>
\ No newline at end of file
......@@ -7,7 +7,7 @@ import java.util.Iterator;
public class MinFourHeap<E> implements IPriorityQueue<E> {
private static final int DEFAULT_CAPACITY = 5;
private static final int DEFAULT_CAPACITY = 10;
private int size;
private PQElement<E>[] data;
......
......@@ -24,7 +24,7 @@ public interface IDeque<E> extends ICollection<E> {
*/
@Override
default public void add(E e){
this.addFront(e);
this.addBack(e);
}
/**
......
package edu.caltech.cs2.interfaces;
import edu.caltech.cs2.datastructures.LinkedDeque;
public interface IDictionary<K, V> extends Iterable<K> {
public static class Entry<K, V> {
......@@ -85,7 +87,7 @@ public interface IDictionary<K, V> extends Iterable<K> {
*
* @return a collection of the keys contained in this map
*/
public ICollection<K> keySet();
public ICollection<K> keys();
/**
* Returns a {@link ICollection} of the values contained in this map
......@@ -97,7 +99,7 @@ public interface IDictionary<K, V> extends Iterable<K> {
default public ICollection<Entry<K, V>> entrySet() {
IDeque<Entry<K, V>> entries = new LinkedDeque<Entry<K,V>>();
for (K key : keySet()) {
for (K key : keys()) {
entries.add(new Entry(key, this.get(key)));
}
return entries;
......
package edu.caltech.cs2.interfaces;
public interface IList<E> extends ICollection<E> {
public E get(int index);
public E set(int index, E element);
public E remove(int index);
}
......@@ -2,15 +2,15 @@ package edu.caltech.cs2.interfaces;
/**
* This interface represents a Priority Queue - a data structure that is very similar to a queue but
* stores values in ascending order.
* stores values in ascending order based on their priority.
* @param <E> Element type
*/
public interface IPriorityQueue<E> extends IQueue<IPriorityQueue.PQElement<E>> {
public static class PQElement<E> {
public final E data;
public final int priority;
public final double priority;
public PQElement(E data, int priority) {
public PQElement(E data, double priority) {
this.data = data;
this.priority = priority;
}
......@@ -36,14 +36,16 @@ public interface IPriorityQueue<E> extends IQueue<IPriorityQueue.PQElement<E>> {
/**
* Increase the priority of the key at idx
* @param key - the new key with the new priority
* Increase the priority of the element in the queue with data equal to key.data
* @param key - the PQElement with data, and new priority for that data
* @throws IllegalArgumentException if key.data is not an element in the queue
*/
public void increaseKey(PQElement<E> key);
/**
* Decrease the priority of the key at idx
* @param key - the new key with the new priority
* Decrease the priority of the element in the queue with data equal to key.data
* @param key - the PQElement with data, and new priority for that data
* @throws IllegalArgumentException if key.data is not an element in the queue
*/
public void decreaseKey(PQElement<E> key);
......
package edu.caltech.cs2.types;
import edu.caltech.cs2.datastructures.LinkedDeque;
import edu.caltech.cs2.interfaces.IDeque;
import java.util.Iterator;
......
......@@ -5,6 +5,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import edu.caltech.cs2.datastructures.ArrayDeque;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
......
......@@ -6,6 +6,7 @@ import edu.caltech.cs2.helpers.Reflection;
import java.util.*;
import java.util.ArrayList;
import edu.caltech.cs2.interfaces.IDictionary;
import edu.caltech.cs2.interfaces.IPriorityQueue;
import edu.caltech.cs2.misc.IntegerComparator;
import org.junit.jupiter.api.*;
......@@ -14,46 +15,80 @@ import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.*;
public class HeapTester {
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MinFourHeapTests {
private static String STRING_SOURCE = "src/edu/caltech/cs2/datastructures/MinFourHeap.java";
public void checkKeyToIndexMap(MinFourHeap<Integer> heap) {
// Check keyToIndexMap
IPriorityQueue.PQElement<Integer>[] heapData = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
IDictionary<Integer, Integer> indexMap = Reflection.getFieldValue(MinFourHeap.class, "keyToIndexMap",
heap);
assertEquals(heap.size(), indexMap.size(), "Heap size and keyToIndexMap sizes are different");
// Reconstruct data from map
Integer[] dataFromMap = new Integer[heap.size()];
for (IDictionary.Entry<Integer, Integer> entry : indexMap.entrySet()) {
assertTrue(entry.value < heap.size(), "Index in keyToIndexMap is larger than heap size");
// If not null, then was set prior
assertNull(dataFromMap[entry.value], "Index appears multiple times in keyToIndexMap");
dataFromMap[entry.value] = entry.key;
}
// Only check data that's actually in the heap
for (int i = 0; i < heap.size(); i++) {
assertEquals(heapData[i].data, dataFromMap[i], "keyToIndexMap does not match heap data at index " + i);
}
}
@Order(0)
@DisplayName("Does not use or import disallowed classes")
@Test
@Tag("C")
public void testForInvalidClasses() {
List<String> regexps = List.of("java.util.(?!Iterator|function.Function)", "java.lang.reflect", "java.io");
List<String> regexps = List.of("java.lang.reflect", "java.io");
Inspection.assertNoImportsOf(STRING_SOURCE, regexps);
Inspection.assertNoUsageOf(STRING_SOURCE, regexps);
}
@Order(1)
@DisplayName("Does not use or import disallowed classes from java.util")
@Test
@Tag("C")
public void testForInvalidImportsJavaUtil() {
List<String> allowed = List.of("Iterator");
Inspection.assertNoImportsOfExcept(STRING_SOURCE, "java\\.util", allowed);
List<String> bannedUsages = List.of("java\\.util\\.(?!" + String.join("|", allowed) + ")");
Inspection.assertNoUsageOf(STRING_SOURCE, bannedUsages);
}
@Test
@Tag("C")
@Order(2)
@DisplayName("The public interface is correct")
public void testPublicInterface() {
Reflection.assertPublicInterface(MinFourHeap.class, List.of(
"enqueue",
"dequeue",
"iterator",
"decreaseKey",
"increaseKey",
"peek",
"size"
));
Reflection.assertPublicInterface(MinFourHeap.class,
List.of("enqueue", "dequeue", "iterator", "decreaseKey", "increaseKey", "peek", "size"));
}
@Test
@Tag("C")
@Order(3)
@DisplayName("Attempting to enqueue duplicate elements throws an exception")
public void testDuplicateThrows() {
MinFourHeap<Integer> heap = new MinFourHeap<>();
heap.enqueue(new IPriorityQueue.PQElement(10, 10));
heap.enqueue(new IPriorityQueue.PQElement<>(10, 10));
assertThrows(IllegalArgumentException.class, () -> {
heap.enqueue(new IPriorityQueue.PQElement(10, 10));
heap.enqueue(new IPriorityQueue.PQElement<>(10, 10));
});
}
@Test
@Tag("C")
public void testIdxTooLargeThrows() {
Comparator<Integer> c = new IntegerComparator();
@Order(3)
@DisplayName("Attempting to modify the priority of a nonexistent element throws an exception")
public void testChangeKeyNonexistentElem() {
MinFourHeap<Integer> heap = new MinFourHeap<>();
heap.enqueue(new IPriorityQueue.PQElement<>(10, 10));
assertThrows(IllegalArgumentException.class, () -> {
......@@ -66,6 +101,8 @@ public class HeapTester {
@Test
@Tag("C")
@Order(4)
@DisplayName("Smoke test enqueue while checking internal state of heap")
public void testEnqueue() {
// create heap
MinFourHeap<Integer> heap = new MinFourHeap<>();
......@@ -90,103 +127,140 @@ public class HeapTester {
assertTrue(heap.enqueue(new IPriorityQueue.PQElement<>(values.get(i), values.get(i))));
assertEquals(i + 1, heap.size());
IPriorityQueue.PQElement[] heap_data = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
for(int j = 0; j < heap.size(); j++) {
assertEquals(step_by_step.get(i).toArray()[j], heap_data[j].data);
IPriorityQueue.PQElement<Integer>[] heapData = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
for (int j = 0; j < heap.size(); j++) {
assertEquals(step_by_step.get(i).toArray()[j], heapData[j].data);
}
checkKeyToIndexMap(heap);
}
}
@Test
@Tag("C")
@Order(4)
@DisplayName("Smoke test dequeue while checking internal state of heap")
public void testDequeue() {
Comparator<Integer> c = new IntegerComparator();
MinFourHeap<Integer> heap = new MinFourHeap<>();
PriorityQueue<Integer> reference = new PriorityQueue<>(c);
List<Integer> values = new ArrayList<>(Arrays.asList(9, -100, 19, 3, -2, 1, 7, -84, -4, 2, 70));
for (int i = 0; i < values.size(); i++) {
assertTrue(heap.enqueue(new IPriorityQueue.PQElement<>(values.get(i), values.get(i))));
reference.add(values.get(i));
for (int value : values) {
assertTrue(heap.enqueue(new IPriorityQueue.PQElement<>(value, value)));
reference.add(value);
}
for (int i = 0; i < reference.size(); i++) {
assertEquals(reference.remove(), heap.dequeue().data);
checkKeyToIndexMap(heap);
assertEquals(reference.size(), heap.size());
}
}
@Tag("C")
@ParameterizedTest(name = "Stress test enqueue and dequeue.")
@CsvSource({
"100, 3000", "42, 1000"
})
public void stressTestAddRemove(int seed, int size) {
MinFourHeap<Integer> heap = new MinFourHeap<>();
Comparator<Integer> c = new IntegerComparator();
PriorityQueue<Integer> reference = new PriorityQueue<>(c);
Random r = new Random(seed);
for (int i = 0; i < size; i++) {
int num = r.nextInt();
while (reference.contains(num)) {
num = r.nextInt();
}
reference.add(num);
assertTrue(heap.enqueue(new IPriorityQueue.PQElement<>(num, num)));
assertEquals(heap.size(), reference.size());
}
while (heap.size() != 0) {
assertEquals(heap.dequeue().data, reference.remove());
}
}
@Test
@Tag("C")
@Order(5)
@DisplayName("Smoke test increaseKey while checking internal state of heap")
public void testIncreaseKey() {
Comparator<Integer> c = new IntegerComparator();
// Build heap
MinFourHeap<Integer> heap = new MinFourHeap<>();
List<Integer> values = new ArrayList<>(Arrays.asList(9, -100, 19, 3, -2, 1, 7, -84, -4, 2, 70));
for (int i = 0; i < values.size(); i++) {
assertTrue(heap.enqueue(new IPriorityQueue.PQElement<>(values.get(i), values.get(i))));
for (Integer value : values) {
assertTrue(heap.enqueue(new IPriorityQueue.PQElement<>(value, value)));
}
// Assert constructed heap is correct
Integer[] correctHeapData = {-100, -84, 2, 3, -2, 9, 7, 1, -4, 19, 70};
IPriorityQueue.PQElement[] heap_data = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
for(int j = 0; j < heap.size(); j++) {
assertEquals(correctHeapData[j], heap_data[j].data);
IPriorityQueue.PQElement<Integer>[] heapData = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
for (int j = 0; j < heap.size(); j++) {
assertEquals(correctHeapData[j], heapData[j].data);
}
// Increase the root's priority
heap.increaseKey(new IPriorityQueue.PQElement<>(-100, 100));
Integer[] correctHeapAfterIncrease = {-84, -4, 2, 3, -2, 9, 7, 1, 100, 19, 70};
heap_data = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
// Verify the heap after moving is correct
double[] correctHeapPrioritiesAfterIncrease = {-84, -4, 2, 3, -2, 9, 7, 1, 100, 19, 70};
heapData = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
checkKeyToIndexMap(heap);
for (int i = 0; i < heap.size(); i++) {
assertEquals(correctHeapAfterIncrease[i], heap_data[i].priority);
assertEquals(correctHeapPrioritiesAfterIncrease[i], heapData[i].priority);
}
}
@Test
@Tag("C")
@Order(5)
@DisplayName("Smoke test decreaseKey while checking internal state of heap")
public void testDecreaseKey() {
// Build heap
MinFourHeap<Integer> heap = new MinFourHeap<>();
List<Integer> values = new ArrayList<>(Arrays.asList(9, -100, 19, 3, -2, 1, 7, -84, -4, 2, 70));
for (int i = 0; i < values.size(); i++) {
assertTrue(heap.enqueue(new IPriorityQueue.PQElement<>(values.get(i), values.get(i))));
for (Integer value : values) {
assertTrue(heap.enqueue(new IPriorityQueue.PQElement<>(value, value)));
}
// Assert constructed heap is correct
Integer[] correctHeapData = {-100, -84, 2, 3, -2, 9, 7, 1, -4, 19, 70};
IPriorityQueue.PQElement[] heap_data = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
for(int j = 0; j < heap.size(); j++) {
assertEquals(correctHeapData[j], heap_data[j].data);
IPriorityQueue.PQElement<Integer>[] heapData = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
for (int j = 0; j < heap.size(); j++) {
assertEquals(correctHeapData[j], heapData[j].data);
}
// Decrease some node's priority
heap.decreaseKey(new IPriorityQueue.PQElement<>(7, -105));
Integer[] correctHeapAfterDecrease = {-105, -100, 2, 3, -2, 9, -84, 1, -4, 19, 70};
heap_data = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
// Verify the heap after moving is correct
double[] correctHeapPrioritiesAfterDecrease = {-105, -100, 2, 3, -2, 9, -84, 1, -4, 19, 70};
heapData = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
checkKeyToIndexMap(heap);
for (int i = 0; i < heap.size(); i++) {
assertEquals(correctHeapAfterDecrease[i], heap_data[i].priority);
assertEquals(correctHeapPrioritiesAfterDecrease[i], heapData[i].priority);
}
}
@Test
@Tag("C")
@ParameterizedTest(name = "Stress test increase/decrease key")
@CsvSource({
"100, 3000, 1500", "42, 1000, 500"
})
@Order(6)
@DisplayName("Dequeueing with no further percolation leaves the heap in a consistent state")
public void testDequeueWithNoPercolation() {
MinFourHeap<Integer> heap = new MinFourHeap<>();
List<Integer> values = new ArrayList<>(Arrays.asList(1, 6, 7, 8, 2));
for (int value : values) {
assertTrue(heap.enqueue(new IPriorityQueue.PQElement<>(value, value)));
}
// Dequeueing 1 won't cause any further percolations, since 2 is in the right place.
// There's some edge cases around this for some reason, which is why the test is here...
assertEquals(1, heap.dequeue().data);
checkKeyToIndexMap(heap);
}
@Tag("C")
@Order(6)
@Test
@DisplayName("Check that increaseKey that percolates near end of array does not throw")
public void testDecreaseKeyBeyondArrayBounds() {
MinFourHeap<Integer> heap = new MinFourHeap<>();
// Heap:
// 0 => [2 => [5, 6, 7, 8], 1 => [9], 3 => [], 4 => []]
List<Integer> values = new ArrayList<>(Arrays.asList(0, 2, 1, 3, 4, 5, 6, 7, 8, 9));
for (int value : values) {
assertTrue(heap.enqueue(new IPriorityQueue.PQElement<>(value, value)));
}
IPriorityQueue.PQElement<Integer>[] heapData = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
// Make sure our heap data is still "good" for the test
assertEquals(10, heapData.length, "Heap data array is not a default size of 10 or was resized prematurely");
// Increase the node at the root. The node gets swapped with 1, then compared against children.
// But, 9 is at the last index in the heap array and not the last child.
heap.increaseKey(new IPriorityQueue.PQElement<>(0, 100));
// Correctness is checked elsewhere, so don't do anything here. Only thing that matters is that this
// executes successfully.
// 1 => [2 => [5, 6, 7, 8], 9 => [0 (100)], 3 => [], 4 => []]
}
@Tag("C")
@Order(7)
@ParameterizedTest(name = "Stress test increaseKey and decreaseKey with {1} random elements and seed = {0}")
@DisplayName("Stress test increaseKey, decreaseKey")
@CsvSource({"100, 30000, 15000", "42, 10000, 5000"})
public void stressTestIncreaseDecrease(int seed, int size, int numToReplace) {
MinFourHeap<Integer> heap = new MinFourHeap<>();
Comparator<Integer> c = new IntegerComparator();
......@@ -204,35 +278,72 @@ public class HeapTester {
}
for (int j = 0; j < numToReplace; j++) {
int newKey = r.nextInt();
while (reference.contains(newKey) || removed.contains(newKey)) {
newKey = r.nextInt();
int newPriority = r.nextInt();
while (reference.contains(newPriority) || removed.contains(newPriority)) {
newPriority = r.nextInt();
}
IPriorityQueue.PQElement[] heap_data = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
int idx = r.nextInt(heap.size());
Integer origKey = (Integer)heap_data[idx].data;
IPriorityQueue.PQElement<Integer>[] heapData = Reflection.getFieldValue(MinFourHeap.class, "data", heap);
Integer origKey = heapData[r.nextInt(heap.size())].data;
while (removed.contains(origKey)) {
idx = r.nextInt(heap.size());
origKey = (Integer)heap_data[idx].data;
}
if (newKey < origKey) {
heap.decreaseKey(new IPriorityQueue.PQElement(origKey, newKey));
origKey = heapData[r.nextInt(heap.size())].data;
}
else {
heap.increaseKey(new IPriorityQueue.PQElement(origKey, newKey));
if (newPriority < origKey) {
heap.decreaseKey(new IPriorityQueue.PQElement<>(origKey, newPriority));
} else {
heap.increaseKey(new IPriorityQueue.PQElement<>(origKey, newPriority));
}
assertEquals(reference.size(), heap.size());
removed.add(origKey);
reference.remove(origKey);
reference.add(newKey);
reference.add(newPriority);
assertEquals(reference.size(), heap.size());
}
while(!reference.isEmpty()) {
int i = 0;
while (!reference.isEmpty()) {
Integer er = reference.remove();
IPriorityQueue.PQElement mr = heap.dequeue();
assertEquals(er, mr.priority);
IPriorityQueue.PQElement<Integer> mr = heap.dequeue();
if (er != mr.priority) {
System.err.println(i);
System.err.println(reference.size());
System.err.println(heap.size());
}
assertEquals((double) er, mr.priority);
i++;
}
}
@Tag("C")
@Order(7)
@ParameterizedTest(name = "Stress test enqueue and dequeue with {1} random elements and seed = {0}")
@CsvSource({"100, 10000", "42, 10000"})
@DisplayName("Stress test enqueue, dequeue")
public void stressTestEnqueueDequeue(int seed, int size) {
MinFourHeap<Integer> heap = new MinFourHeap<>();
Comparator<Integer> c = new IntegerComparator();
PriorityQueue<Integer> reference = new PriorityQueue<>(c);
Random r = new Random(seed);
for (int i = 0; i < size; i++) {
int num = r.nextInt();
while (reference.contains(num)) {
num = r.nextInt();
}
reference.add(num);
assertTrue(heap.enqueue(new IPriorityQueue.PQElement<>(num, num)));
// Check at intervals to save computation
if (i % 499 == 0) {
checkKeyToIndexMap(heap);
}
assertEquals(reference.size(), heap.size());
}
while (heap.size() != 0) {
assertEquals(reference.remove(), heap.dequeue().data);
if (heap.size() % 499 == 0) {
checkKeyToIndexMap(heap);
}
}
}
}
\ No newline at end of file
package edu.caltech.cs2.datastructures;
import edu.caltech.cs2.helpers.Inspection;
import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.IDictionary;
import edu.caltech.cs2.interfaces.IPriorityQueue;
import edu.caltech.cs2.types.NGram;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.*;
import java.util.List;
import java.util.Scanner;
import java.util.function.Function;
import java.util.function.Supplier;
import static org.junit.jupiter.api.Assertions.*;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class NGramMapTests {
private static String STRING_SOURCE = "src/edu/caltech/cs2/datastructures/NGramMap.java";
......@@ -25,32 +21,36 @@ public class NGramMapTests {
@Tag("B")
@Test
public void testForInvalidClasses() {
List<String> regexps = List.of("java.util.(?!Iterator|function.Function|function.Supplier|Random|Scanner)", "java.lang.reflect", "java.io");
List<String> regexps = List.of(
"java.lang.reflect", "java.io");
Inspection.assertNoImportsOf(STRING_SOURCE, regexps);
Inspection.assertNoUsageOf(STRING_SOURCE, regexps);
}
private static final String SRC_FILE_PATH = System.getProperty("user.dir") + "/data/test_ngrammap_src";
IDictionary<NGram, IDictionary<IterableString, Integer>> newOuter;
Supplier<IDictionary<IterableString, Integer>> newInner;
@Order(1)
@DisplayName("Does not use or import disallowed classes from java.util")
@Test
@Tag("B")
public void testForInvalidImportsJavaUtil() {
List<String> allowed = List.of("Iterator", "function.Supplier", "Random", "Scanner");
Inspection.assertNoImportsOfExcept(STRING_SOURCE, "java\\.util", allowed);
List<String> bannedUsages = List.of("java\\.util\\.(?!" + String.join("|", allowed) + ")");
Inspection.assertNoUsageOf(STRING_SOURCE, bannedUsages);
}
private NGramMap initNGramMap(Scanner in, int N) {
Function<IDeque<String>, NGram> ngramCollector = (IDeque<String> x) -> new NGram(x);
Function<IDeque<Character>, IterableString> stringCollector = (IDeque<Character> x) -> {
char[] chars = new char[x.size()];
for (int i = 0; i < chars.length; i++) {
chars[i] = x.peekFront();
x.addBack(x.removeFront());
}
return new IterableString(new String(chars));
};
IDictionary<NGram, IDictionary<IterableString, Integer>> newOuter = new ChainingHashDictionary<>(MoveToFrontDictionary::new);
Supplier<IDictionary<IterableString, Integer>> newInner = () -> new ChainingHashDictionary<>(MoveToFrontDictionary::new);
IDictionary<NGram, IDictionary<IterableString, Integer>> newOuter = new ChainingHashDictionary<>(
MoveToFrontDictionary::new);
Supplier<IDictionary<IterableString, Integer>> newInner = () -> new ChainingHashDictionary<>(
MoveToFrontDictionary::new);
return new NGramMap(in, N, newOuter, newInner);
}
@Test
@Tag("B")
@Order(2)
@DisplayName("Test text containing a single NGram")
public void testSingleNGram() {
String text = "One two three";
NGramMap map = initNGramMap(new Scanner(text), 2);
......@@ -62,6 +62,8 @@ public class NGramMapTests {
@Test
@Tag("B")
@Order(3)
@DisplayName("Test text containing multiple NGrams")
public void testMultipleNGram() {
String text = "a a a a a a a a a a a a";
NGramMap map = initNGramMap(new Scanner(text), 2);
......@@ -73,6 +75,8 @@ public class NGramMapTests {
@Test
@Tag("B")
@Order(4)
@DisplayName("Test getCountsAfter on a nonexistent NGram")
public void testNonexistentNGram() {
NGramMap map = initNGramMap(new Scanner("blah blah"), 2);
assertNull(map.getCountsAfter(new NGram("not in")));
......@@ -80,6 +84,8 @@ public class NGramMapTests {
@Test
@Tag("B")
@Order(5)
@DisplayName("Test seenWordAfter")
public void testSeenWordAfter() {
String text = "The quick brown fox jumps over the lazy dog";
NGramMap map = initNGramMap(new Scanner(text), 2);
......@@ -95,8 +101,7 @@ public class NGramMapTests {
for (int i = 0; i < items.length; i++) {
if (items[i].data.equals("over")) {
assertEquals(1, items[i].priority);
}
else {
} else {
assertEquals("nonexistent", items[i].data);
assertEquals(1, items[i].priority);
}
......@@ -105,6 +110,8 @@ public class NGramMapTests {
@Test
@Tag("B")
@Order(6)
@DisplayName("Test getWordsAfter")
public void testGetWordsAfter() {
String text = "The quick brown fox jumps over the lazy dog";
NGramMap map = initNGramMap(new Scanner(text), 2);
......@@ -112,6 +119,11 @@ public class NGramMapTests {
map.updateCount(new NGram("fox jumps"), "nonexistent");
map.updateCount(new NGram("fox jumps"), "nonexistent");
// Check nonexistent behavior
String[] afterNull = map.getWordsAfter(new NGram("lol fake"), 1);
assertNotNull(afterNull, "getWordsAfter() returns a null value, not a String array");
assertEquals(0, afterNull.length);
String[] afterQuick = map.getWordsAfter(new NGram("The quick"), 2);
assertEquals(1, afterQuick.length);
assertEquals("brown", afterQuick[0]);
......@@ -120,5 +132,4 @@ public class NGramMapTests {
assertEquals(2, afterJumps.length);
assertEquals("nonexistent", afterJumps[0]);
assertEquals("over", afterJumps[1]);
}
}
}}
\ No newline at end of file
......@@ -18,11 +18,11 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.fail;
public class Inspection {
private static String getUsageOf(List<String> regexps, List<? extends Node> codeObjects) {
private static String[] getUsageOf(List<String> regexps, List<? extends Node> codeObjects) {
for (Node d : codeObjects) {
for (String regex : regexps) {
if (d.toString().replaceAll("\\R", "").matches(".*" + regex + ".*")) {
return regex;
return new String[]{d.toString().replaceAll("\\R", ""), regex};
}
}
}
......@@ -32,9 +32,25 @@ public class Inspection {
public static void assertNoImportsOf(String filePath, List<String> regexps) {
try {
CompilationUnit cu = JavaParser.parse(new File(filePath));
String usage = getUsageOf(regexps, cu.getImports());
String[] usage = getUsageOf(regexps, cu.getImports());
if (usage != null) {
fail("You may not import " + usage + " in " + Paths.get(filePath).getFileName() + ".");
fail(usage[0] + " is a banned import under " + usage[1] + " in " + Paths.get(filePath).getFileName()
+ ".");
}
} catch (FileNotFoundException e) {
fail("Missing Java file: " + Paths.get(filePath).getFileName());
}
}
public static void assertNoImportsOfExcept(String filePath, String bannedImport, List<String> allowedClasses) {
try {
CompilationUnit cu = JavaParser.parse(new File(filePath));
String bannedRegex = bannedImport + "\\.(?!" + String.join("|", allowedClasses) + ")";
String[] usage = getUsageOf(List.of(bannedRegex), cu.getImports());
if (usage != null) {
fail(usage[0] + " is a banned import under " + bannedImport +
" and is not an allowed import " +
allowedClasses + " in " + Paths.get(filePath).getFileName() + ".");
}
} catch (FileNotFoundException e) {
fail("Missing Java file: " + Paths.get(filePath).getFileName());
......@@ -66,16 +82,16 @@ public class Inspection {
List<ConstructorDeclaration> constructors = new ArrayList<>();
CONSTRUCTOR_COLLECTOR.visit(cu, constructors);
String usage = getUsageOf(regexps, constructors);
String[] usage = getUsageOf(regexps, constructors);
if (usage != null) {
fail("You may not use " + usage + " in " + Paths.get(filePath).getFileName() + ".");
fail("You may not use " + usage[1] + " in " + Paths.get(filePath).getFileName() + ".");
}
List<MethodDeclaration> methods = new ArrayList<>();
METHOD_COLLECTOR.visit(cu, methods);
usage = getUsageOf(regexps, methods);
if (usage != null) {
fail("You may not use " + usage + " in " + Paths.get(filePath).getFileName() + ".");
fail("You may not use " + usage[1] + " in " + Paths.get(filePath).getFileName() + ".");
}
} catch (FileNotFoundException e) {
fail("Missing Java file: " + Paths.get(filePath).getFileName());
......@@ -93,8 +109,7 @@ public class Inspection {
List<Statement> statements = body.getStatements();
if (statements.size() != 1) {
nonThisConstructors++;
}
else if (!statements.get(0).toString().startsWith("this(")) {
} else if (!statements.get(0).toString().startsWith("this(")) {
nonThisConstructors++;
}
......
......@@ -7,10 +7,7 @@ import edu.caltech.cs2.interfaces.IDeque;
import edu.caltech.cs2.interfaces.IDictionary;
import edu.caltech.cs2.misc.CorrectionChoice;
import edu.caltech.cs2.types.NGram;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.*;
import wordcorrector.SpellingCorrector;
import java.io.File;
......@@ -18,12 +15,12 @@ import java.io.FileNotFoundException;
import java.time.Duration;
import java.util.List;
import java.util.Scanner;
import java.util.function.Function;
import java.util.function.Supplier;
import static org.junit.jupiter.api.Assertions.*;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class SpellingCorrectorTests {
private static final String DICT_FILENAME = "./data/dictionary.txt";
private static final String EDIT_DIST_FILENAME = "./data/edit_distance_test";
......@@ -39,21 +36,24 @@ public class SpellingCorrectorTests {
@Test
@Tag("A")
public void testForInvalidClasses() {
List<String> regexps = List.of("java.util.(?!Iterator|function.Function)", "java.lang.reflect", "java.io");
List<String> regexps = List.of("java.lang.reflect", "java.io");
Inspection.assertNoImportsOf(STRING_SOURCE, regexps);
Inspection.assertNoUsageOf(STRING_SOURCE, regexps);
}
@Order(0)
@DisplayName("Does not use or import disallowed classes from java.util")
@Test
@Tag("A")
public void testForInvalidClassesUtil() {
List<String> allowed = List.of("Iterator");
Inspection.assertNoImportsOfExcept(STRING_SOURCE, "java\\.util", allowed);
List<String> bannedUsages = List.of("java\\.util\\.(?!" + String.join("|", allowed) + ")");
Inspection.assertNoUsageOf(STRING_SOURCE, bannedUsages);
}
private NGramMap initNGramMap(Scanner in, int N) {
Function<IDeque<String>, NGram> ngramCollector = (IDeque<String> x) -> new NGram(x);
Function<IDeque<Character>, IterableString> stringCollector = (IDeque<Character> x) -> {
char[] chars = new char[x.size()];
for (int i = 0; i < chars.length; i++) {
chars[i] = x.peekFront();
x.addBack(x.removeFront());
}
return new IterableString(new String(chars));
};
IDictionary<NGram, IDictionary<IterableString, Integer>> newOuter = new ChainingHashDictionary<>(MoveToFrontDictionary::new);
Supplier<IDictionary<IterableString, Integer>> newInner = () -> new ChainingHashDictionary<>(MoveToFrontDictionary::new);
return new NGramMap(in, N, newOuter, newInner);
......@@ -61,6 +61,8 @@ public class SpellingCorrectorTests {
@Test
@Tag("A")
@Order(1)
@DisplayName("Test correctness and speed of editDistance")
public void testEditDistance() {
assertTimeout(Duration.ofMillis(EDIT_DIST_TIMEOUT), this::testEditDistanceHelper);
}
......@@ -81,6 +83,8 @@ public class SpellingCorrectorTests {
@Test
@Tag("A")
@Order(2)
@DisplayName("Test possibleCorrections")
public void testPossibleCorrections() {
File folder = new File(POSSIBLE_CORRECTIONS_DIRECTORY);
File[] listOfFiles = folder.listFiles();
......@@ -120,6 +124,8 @@ public class SpellingCorrectorTests {
@Test
@Tag("A")
@Order(3)
@DisplayName("Test bestCorrection")
public void testBestCorrection () {
// test best corrections
SpellingCorrector corrector = new SpellingCorrector(DICT_FILENAME);
......@@ -145,5 +151,4 @@ public class SpellingCorrectorTests {
elements[1], elements[2].strip(), 10));
}
}
}
......@@ -2,11 +2,7 @@ package edu.caltech.cs2.sorts;
import edu.caltech.cs2.helpers.Inspection;
import edu.caltech.cs2.interfaces.IPriorityQueue;
import edu.caltech.cs2.misc.IntegerComparator;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
......@@ -18,7 +14,8 @@ import java.util.Random;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class SortTester {
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TopKSortTests {
private static String STRING_SOURCE = "src/edu/caltech/cs2/sorts/TopKSort.java";
@Order(0)
......@@ -26,25 +23,39 @@ public class SortTester {
@Test
@Tag("C")
public void testForInvalidClasses() {
List<String> regexps = List.of("java.util.(?!Iterator|function.Function)", "java.lang.reflect", "java.io");
List<String> regexps = List.of("java.util", "java.lang.reflect", "java.io");
Inspection.assertNoImportsOf(STRING_SOURCE, regexps);
Inspection.assertNoUsageOf(STRING_SOURCE, regexps);
}
@Order(0)
@DisplayName("Does not use or import disallowed classes from java.util")
@Test
@Tag("C")
public void testForInvalidClassesUtil() {
List<String> allowed = List.of("Iterator");
Inspection.assertNoImportsOfExcept(STRING_SOURCE, "java\\.util", allowed);
List<String> bannedUsages = List.of("java\\.util\\.(?!" + String.join("|", allowed) + ")");
Inspection.assertNoUsageOf(STRING_SOURCE, bannedUsages);
}
@Test
@Tag("C")
@Order(1)
@DisplayName("Sorting an empty array does not fail")
public void testEmptyArray() {
Comparator<Integer> c = new IntegerComparator();
IPriorityQueue.PQElement[] array = new IPriorityQueue.PQElement[0];
IPriorityQueue.PQElement<Integer>[] array = new IPriorityQueue.PQElement[0];
TopKSort.sort(array, 1);
assertArrayEquals(new Integer[0], array);
}
@Test
@Tag("C")
@Order(2)
@DisplayName("K < 0 throws an exception")
public void testNegativeK() {
Comparator<Integer> c = new IntegerComparator();
IPriorityQueue.PQElement[] array = new IPriorityQueue.PQElement[0];
IPriorityQueue.PQElement<Integer>[] array = new IPriorityQueue.PQElement[0];
assertThrows(IllegalArgumentException.class, () -> {
TopKSort.sort(array, -1);
});
......@@ -52,14 +63,13 @@ public class SortTester {
@Test
@Tag("C")
@Order(3)
@DisplayName("K = 0 does not change the array")
public void testZeroK() {
Comparator<Integer> c = new IntegerComparator();
IPriorityQueue.PQElement[] array = {
new IPriorityQueue.PQElement(1, 1),
new IPriorityQueue.PQElement(2, 2),
new IPriorityQueue.PQElement(3, 3),
new IPriorityQueue.PQElement(4, 4),
new IPriorityQueue.PQElement(5, 5)
IPriorityQueue.PQElement<Integer>[] array = new IPriorityQueue.PQElement[]{
new IPriorityQueue.PQElement<>(1, 1), new IPriorityQueue.PQElement<>(2, 2),
new IPriorityQueue.PQElement<>(3, 3), new IPriorityQueue.PQElement<>(4, 4),
new IPriorityQueue.PQElement<>(5, 5)
};
TopKSort.sort(array, 0);
Integer[] correct = new Integer[5];
......@@ -67,26 +77,26 @@ public class SortTester {
}
@Tag("C")
@Order(4)
@ParameterizedTest(name = "Stress test TopKSort: size: {1}, k: {2}")
@CsvSource({
"42, 3000, 2000", "15, 5000, 1235", "20, 1000, 50"
})
@CsvSource({"42, 3000, 2000", "15, 5000, 1235", "20, 1000, 50"})
@DisplayName("Stress test TopKSort")
public void stressTest(int seed, int size, int k) {
Comparator<IPriorityQueue.PQElement<Integer>> c = (x, y) -> Integer.compare(x.priority, y.priority);
Comparator<IPriorityQueue.PQElement<Integer>> c = Comparator.comparingDouble(x -> x.priority);
Random r = new Random(seed);
Integer[] intarray = r.ints(size).boxed().toArray(Integer[]::new);
IPriorityQueue.PQElement[] array = new IPriorityQueue.PQElement[intarray.length];
Double[] doublearray = Arrays.stream(intarray).map(Double::valueOf).toArray(Double[]::new);
IPriorityQueue.PQElement<Integer>[] array = new IPriorityQueue.PQElement[intarray.length];
for (int i = 0; i < intarray.length; i++) {
array[i] = new IPriorityQueue.PQElement(intarray[i], intarray[i]);
array[i] = new IPriorityQueue.PQElement<>(intarray[i], doublearray[i]);
}
IPriorityQueue.PQElement<Integer>[] sortedArray = array.clone();
Arrays.sort(sortedArray, c);
IPriorityQueue.PQElement[] correct = new IPriorityQueue.PQElement[size];
IPriorityQueue.PQElement<Integer>[] correct = new IPriorityQueue.PQElement[size];
for (int i = 0; i < correct.length; i++) {
if (i < k) {
correct[i] = sortedArray[sortedArray.length - i - 1];
}
else {
} else {
correct[i] = null;
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment