From 24d6567a47e74c0bc397e6c68c0ac35affa93c36 Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Tue, 21 Jun 2022 16:21:11 +0200 Subject: [PATCH 01/12] Remove tests for clean working tree --- src/grader/java/h06/H01_Tests.java | 37 -- src/grader/java/h06/H01_Testsa.java | 55 -- src/grader/java/h06/H01_Testsb.java | 57 -- src/grader/java/h06/H01_Testsc.java | 65 --- src/grader/java/h06/H02_Tests.java | 28 - src/grader/java/h06/H03_Tests.java | 573 -------------------- src/grader/java/h06/H04_Tests.java | 471 ---------------- src/grader/java/h06/H05_Tests.java | 49 -- src/grader/java/h06/H06_RubricProvider.java | 381 +------------ src/grader/java/h06/H06_Tests.java | 120 ---- src/grader/java/h06/Tutor_HashMap.java | 49 -- src/grader/java/h06/Util_Tests.java | 118 ---- 12 files changed, 30 insertions(+), 1973 deletions(-) delete mode 100644 src/grader/java/h06/H01_Tests.java delete mode 100644 src/grader/java/h06/H01_Testsa.java delete mode 100644 src/grader/java/h06/H01_Testsb.java delete mode 100644 src/grader/java/h06/H01_Testsc.java delete mode 100644 src/grader/java/h06/H02_Tests.java delete mode 100644 src/grader/java/h06/H03_Tests.java delete mode 100644 src/grader/java/h06/H04_Tests.java delete mode 100644 src/grader/java/h06/H05_Tests.java delete mode 100644 src/grader/java/h06/H06_Tests.java delete mode 100644 src/grader/java/h06/Tutor_HashMap.java delete mode 100644 src/grader/java/h06/Util_Tests.java diff --git a/src/grader/java/h06/H01_Tests.java b/src/grader/java/h06/H01_Tests.java deleted file mode 100644 index ae40df8..0000000 --- a/src/grader/java/h06/H01_Tests.java +++ /dev/null @@ -1,37 +0,0 @@ -package h06; - -import static org.junit.jupiter.api.Assertions.*; - -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.lang.reflect.TypeVariable; - -import org.junit.jupiter.api.Test; -import org.sourcegrade.jagr.api.rubric.TestForSubmission; - -@TestForSubmission("H06") -public class H01_Tests { - ///////////// - // Interfaces - ///////////// - - @SuppressWarnings("rawtypes") - @Test - public void H01_OtherToIntFunction() { - Class klass = Util_Tests.checkType("OtherToIntFunction", "h06.hashFunctions", true, 3, 1); - - Method apply = Util_Tests.checkMethod(klass, "apply", int.class, Object.class); - Method getTableSize = Util_Tests.checkMethod(klass, "setTableSize", void.class, int.class); - Method setTableSize = Util_Tests.checkMethod(klass, "getTableSize", int.class); - } - - @SuppressWarnings("rawtypes") - @Test - public void H01_OtherAndIntToIntFunction() { - Class klass = Util_Tests.checkType("OtherAndIntToIntFunction", "h06.hashFunctions", true, 3, 1); - - Method apply = Util_Tests.checkMethod(klass, "apply", int.class, Object.class, int.class); - Method getTableSize = Util_Tests.checkMethod(klass, "setTableSize", void.class, int.class); - Method setTableSize = Util_Tests.checkMethod(klass, "getTableSize", int.class); - } -} diff --git a/src/grader/java/h06/H01_Testsa.java b/src/grader/java/h06/H01_Testsa.java deleted file mode 100644 index 8dfb168..0000000 --- a/src/grader/java/h06/H01_Testsa.java +++ /dev/null @@ -1,55 +0,0 @@ -package h06; - -import org.junit.jupiter.api.Test; -import org.sourcegrade.jagr.api.rubric.TestForSubmission; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@TestForSubmission("H06") -public class H01_Testsa { - //////////////////////// - // HashCodeTableIndexFct - //////////////////////// - - @Test - public void H01_HashCodeTableIndexFctBasic() { - // Constructor - //Class klass = Util_Tests.checkType("HashCodeTableIndexFct", "h06.hashFunctions", false, -1, 1); - //Object Util_Test.invokeConstructor(new Class[] {int.class, int.class}, 10, 2); - new Hash2IndexFct(10, 2); - - // Type parameter - try { - // new HashCodeTableIndexFct>(0, 0); - // fail("HashCodeTableIndexFcts type parameter is not bound to classes."); - } catch (Exception e) { - } - - // Interface - Class[] interfaces = Hash2IndexFct.class.getInterfaces(); - assertEquals(1, interfaces.length); - assertEquals(Fct2Int.class.getName(), interfaces[0].getName()); - } - - @Test - public void H01_HashCodeTableIndexFctTableSize() { - Hash2IndexFct hashFunction = new Hash2IndexFct(10, 0); - assertEquals(10, hashFunction.getTableSize()); - hashFunction.setTableSize(20); - assertEquals(20, hashFunction.getTableSize()); - } - - @Test - public void H01_HashCodeTableIndexFctApplyBasic() { - Hash2IndexFct hashFunction = new Hash2IndexFct(10, 0); - assertEquals(8, hashFunction.apply("test")); - } - - @Test - public void H01_HashCodeTableIndexFctApplyAdvanced() { - Hash2IndexFct hashFunction = new Hash2IndexFct(10, 15); - assertEquals(1, hashFunction.apply("t")); - hashFunction.setTableSize(100); - assertEquals(38, hashFunction.apply("testen")); - } -} diff --git a/src/grader/java/h06/H01_Testsb.java b/src/grader/java/h06/H01_Testsb.java deleted file mode 100644 index 71d1b89..0000000 --- a/src/grader/java/h06/H01_Testsb.java +++ /dev/null @@ -1,57 +0,0 @@ -package h06; - -import org.junit.jupiter.api.Test; -import org.sourcegrade.jagr.api.rubric.TestForSubmission; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@TestForSubmission("H06") -public class H01_Testsb { - ///////////////////////////// - // LinearProbingTableIndexFct - ///////////////////////////// - - @Test - public void H01_LinearProbingTableIndexFctBasic() { - // Constructor - Hash2IndexFct internalHashFunction = new Hash2IndexFct(10, 3); - new LinearProbing(internalHashFunction); - - // Type parameter - try { - //new LinearProbingTableIndexFct>(null); - //fail("HashCodeTableIndexFcts type parameter is not bound to classes."); - } catch (Exception e) { - } - - // Interface - Class[] interfaces = LinearProbing.class.getInterfaces(); - assertEquals(1, interfaces.length); - assertEquals(BinaryFct2Int.class.getName(), interfaces[0].getName()); - } - - @Test - public void H01_LinearProbingTableIndexFctTableSize() { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(10, 3); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - assertEquals(10, hashFunction.getTableSize()); - hashFunction.setTableSize(20); - assertEquals(20, hashFunction.getTableSize()); - } - - @Test - public void H01_LinearProbingTableIndexFctApplyBasic() { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(25, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - assertEquals(23, hashFunction.apply("test", 0)); - } - - @Test - public void H01_LinearProbingTableIndexFctApplyAdvanced() { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(20, 5); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - assertEquals(2, hashFunction.apply("t", 1)); - hashFunction.setTableSize(100); - assertEquals(51, hashFunction.apply("testen", 23)); - } -} diff --git a/src/grader/java/h06/H01_Testsc.java b/src/grader/java/h06/H01_Testsc.java deleted file mode 100644 index b84f980..0000000 --- a/src/grader/java/h06/H01_Testsc.java +++ /dev/null @@ -1,65 +0,0 @@ -package h06; - -import org.junit.jupiter.api.Test; -import org.sourcegrade.jagr.api.rubric.TestForSubmission; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -@TestForSubmission("H06") -public class H01_Testsc { - ///////////////////////////// - // DoubleHashingTableIndexFct - ///////////////////////////// - - @Test - public void H01_DoubleHashingTableIndexFctBasic() { - // Constructor - Hash2IndexFct internalHashFunction0 = new Hash2IndexFct(41, 0); - Hash2IndexFct internalHashFunction1 = new Hash2IndexFct(41, 29); - new DoubleHashing(internalHashFunction0, internalHashFunction1); - - // Type parameter - try { - // new DoubleHashingTableIndexFct>(null, null); - // fail("HashCodeTableIndexFcts type parameter is not bound to classes."); - } catch (Exception e) { - } - - // Interface - Class[] interfaces = DoubleHashing.class.getInterfaces(); - assertEquals(1, interfaces.length); - assertEquals(BinaryFct2Int.class.getName(), interfaces[0].getName()); - } - - @Test - public void H01_DoubleHashingTableIndexFctTableSize() { - Hash2IndexFct internalHashFunction0 = new Hash2IndexFct(41, 0); - Hash2IndexFct internalHashFunction1 = new Hash2IndexFct(41, 29); - DoubleHashing hashFunction = new DoubleHashing(internalHashFunction0, internalHashFunction1); - - assertEquals(41, hashFunction.getTableSize()); - hashFunction.setTableSize(20); - assertEquals(20, hashFunction.getTableSize()); - } - - @Test - public void H01_DoubleHashingTableIndexFctApplyBasic() { - Hash2IndexFct internalHashFunction0 = new Hash2IndexFct(41, 0); - Hash2IndexFct internalHashFunction1 = new Hash2IndexFct(41, 29); - DoubleHashing hashFunction = new DoubleHashing(internalHashFunction0, internalHashFunction1); - - assertEquals(35, hashFunction.apply("test", 0)); - } - - @Test - public void H01_DoubleHashingTableIndexFctApplyAdvanced() { - Hash2IndexFct internalHashFunction0 = new Hash2IndexFct(41, 0); - Hash2IndexFct internalHashFunction1 = new Hash2IndexFct(41, 29); - DoubleHashing hashFunction = new DoubleHashing(internalHashFunction0, internalHashFunction1); - - assertEquals(15, hashFunction.apply("t", 1)); - hashFunction.setTableSize(674); - assertEquals(463, hashFunction.apply("testen", 134)); - } - -} diff --git a/src/grader/java/h06/H02_Tests.java b/src/grader/java/h06/H02_Tests.java deleted file mode 100644 index 77f3ed3..0000000 --- a/src/grader/java/h06/H02_Tests.java +++ /dev/null @@ -1,28 +0,0 @@ -package h06; - -import static org.junit.jupiter.api.Assertions.*; - -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.lang.reflect.TypeVariable; - -import org.junit.jupiter.api.Test; -import org.sourcegrade.jagr.api.rubric.TestForSubmission; - -@TestForSubmission("H06") -public class H02_Tests { - ///////////// - // Interface - ///////////// - - @SuppressWarnings("rawtypes") - @Test - public void H02_MyMap() { - Class klass = Util_Tests.checkType("MyMap", "h06.hashTables", true, 4, 2); - - Method containsKey = Util_Tests.checkMethod(klass, "containsKey", boolean.class, Object.class); - Method getValue = Util_Tests.checkMethod(klass, "getValue", Object.class, Object.class); - Method put = Util_Tests.checkMethod(klass, "put", Object.class, Object.class, Object.class); - Method remove = Util_Tests.checkMethod(klass, "remove", Object.class, Object.class); - } -} diff --git a/src/grader/java/h06/H03_Tests.java b/src/grader/java/h06/H03_Tests.java deleted file mode 100644 index 93d6244..0000000 --- a/src/grader/java/h06/H03_Tests.java +++ /dev/null @@ -1,573 +0,0 @@ -package h06; - -import static org.junit.jupiter.api.Assertions.*; - -import java.lang.reflect.Field; -import java.util.*; - -import org.junit.jupiter.api.Test; - -import org.sourcegrade.jagr.api.rubric.TestForSubmission; - -@TestForSubmission("H06") -public class H03_Tests -{ - ////////////////////////// - // MyIndexHoppingHashMap - ////////////////////////// - - @Test - public void H03_MyIndexHoppingHashMapBasic() - { - // Constructor - Hash2IndexFct internalHashFunction = new Hash2IndexFct(10, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - new MyIndexHoppingHashMap(10, 2, 0.75, hashFunction); - - // Type parameter - try - { - //new MyIndexHoppingHashMap, Object>(10, 2, 0.75, null); - //fail("MyIndexHoppingHashMap type parameter is not bound to classes."); - } - catch (Exception e){} - - // Interface - Class[] interfaces = MyIndexHoppingHashMap.class.getInterfaces(); - assertEquals(1, interfaces.length, "MyIndexHoppingHashMap implementiert nicht wie erwartet 1 Interface, sondern: " + interfaces.length); - assertEquals(MyMap.class.getName(), interfaces[0].getName(), "MyIndexHoppingHashMap implementiert nicht wie erwartet das Interface MyMap, sondern: " + interfaces[0].getName()); - } - - //////// - // Put - //////// - - @Test - public void H03_MyIndexHoppingHashMapPutSingle() - { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(15, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - MyIndexHoppingHashMap hashMap = new MyIndexHoppingHashMap(15, 1.5, 0.5, hashFunction); - - try - { - Field theKeys = hashMap.getClass().getDeclaredField("theKeys"); - theKeys.setAccessible(true); - Field theValues = hashMap.getClass().getDeclaredField("theValues"); - theValues.setAccessible(true); - Field occupiedSinceLastRehash = hashMap.getClass().getDeclaredField("occupiedSinceLastRehash"); - occupiedSinceLastRehash.setAccessible(true); - - assertEquals(null, hashMap.put("test", "value")); - - Object[] keys = (Object[])theKeys.get(hashMap); - Object[] values = (Object[])theValues.get(hashMap); - Object[] occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "test", null}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "value", null}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - occupied)); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - @Test - public void H03_MyIndexHoppingHashMapPutReplace() - { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(15, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - MyIndexHoppingHashMap hashMap = new MyIndexHoppingHashMap(15, 1.5, 0.5, hashFunction); - - try - { - Field theKeys = hashMap.getClass().getDeclaredField("theKeys"); - theKeys.setAccessible(true); - Field theValues = hashMap.getClass().getDeclaredField("theValues"); - theValues.setAccessible(true); - Field occupiedSinceLastRehash = hashMap.getClass().getDeclaredField("occupiedSinceLastRehash"); - occupiedSinceLastRehash.setAccessible(true); - - assertEquals(null, hashMap.put("test", "value")); - - Object[] keys = (Object[])theKeys.get(hashMap); - Object[] values = (Object[])theValues.get(hashMap); - Object[] occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "test", null}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "value", null}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - occupied)); - - assertEquals("value", hashMap.put("test", "a")); - - keys = (Object[])theKeys.get(hashMap); - values = (Object[])theValues.get(hashMap); - occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "test", null}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "a", null}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - occupied)); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - @Test - public void H03_MyIndexHoppingHashMapPutMultiple() - { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(15, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - MyIndexHoppingHashMap hashMap = new MyIndexHoppingHashMap(15, 1.5, 0.5, hashFunction); - - try - { - Field theKeys = hashMap.getClass().getDeclaredField("theKeys"); - theKeys.setAccessible(true); - Field theValues = hashMap.getClass().getDeclaredField("theValues"); - theValues.setAccessible(true); - Field occupiedSinceLastRehash = hashMap.getClass().getDeclaredField("occupiedSinceLastRehash"); - occupiedSinceLastRehash.setAccessible(true); - - assertEquals(null, hashMap.put("test", "value")); - - Object[] keys = (Object[])theKeys.get(hashMap); - Object[] values = (Object[])theValues.get(hashMap); - Object[] occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "test", null}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "value", null}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - occupied)); - - assertEquals(null, hashMap.put("key", 2)); - - keys = (Object[])theKeys.get(hashMap); - values = (Object[])theValues.get(hashMap); - occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "test", "key"}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "value", 2}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, false, false, false, false, false, false, false, false, false, false, false, true, true}, - occupied)); - - assertEquals(null, hashMap.put("m", "a")); - - keys = (Object[])theKeys.get(hashMap); - values = (Object[])theValues.get(hashMap); - occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, "m", null, null, null, null, null, null, null, null, "test", "key"}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, "a", null, null, null, null, null, null, null, null, "value", 2}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, false, false, true, false, false, false, false, false, false, false, false, true, true}, - occupied)); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - @Test - public void H03_MyIndexHoppingHashMapPutMultipleResize() - { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(4, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - MyIndexHoppingHashMap hashMap = new MyIndexHoppingHashMap(4, 2, 0.5, hashFunction); - - try - { - Field theKeys = hashMap.getClass().getDeclaredField("theKeys"); - theKeys.setAccessible(true); - Field theValues = hashMap.getClass().getDeclaredField("theValues"); - theValues.setAccessible(true); - Field occupiedSinceLastRehash = hashMap.getClass().getDeclaredField("occupiedSinceLastRehash"); - occupiedSinceLastRehash.setAccessible(true); - - assertEquals(null, hashMap.put("test", "value")); - - Object[] keys = (Object[])theKeys.get(hashMap); - Object[] values = (Object[])theValues.get(hashMap); - Object[] occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, "test", null}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, "value", null}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, true, false}, - occupied)); - - assertEquals(null, hashMap.put("a", 2)); - - keys = (Object[])theKeys.get(hashMap); - values = (Object[])theValues.get(hashMap); - occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, "a", "test", null}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, 2, "value", null}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, true, true, false}, - occupied)); - - assertEquals(null, hashMap.put("m", "l")); - - keys = (Object[])theKeys.get(hashMap); - values = (Object[])theValues.get(hashMap); - occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, "a", "test", null, null, "m", null, null}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, 2, "value", null, null, "l", null, null}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, true, true, false, false, true, false, false}, - occupied)); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - /////////// - // Remove - /////////// - - @Test - public void H03_MyIndexHoppingHashMapRemoveEmpty() - { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(10, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - MyIndexHoppingHashMap hashMap = new MyIndexHoppingHashMap(10, 1.5, 0.5, hashFunction); - - try - { - Field theKeys = hashMap.getClass().getDeclaredField("theKeys"); - theKeys.setAccessible(true); - Field theValues = hashMap.getClass().getDeclaredField("theValues"); - theValues.setAccessible(true); - Field occupiedSinceLastRehash = hashMap.getClass().getDeclaredField("occupiedSinceLastRehash"); - occupiedSinceLastRehash.setAccessible(true); - - assertEquals(null, hashMap.remove("test")); - - Object[] keys = (Object[])theKeys.get(hashMap); - Object[] values = (Object[])theValues.get(hashMap); - Object[] occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, false, false, false, false, false, false, false, false }, - occupied)); - - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - @Test - public void H03_MyIndexHoppingHashMapRemoveSingle() - { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(15, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - MyIndexHoppingHashMap hashMap = new MyIndexHoppingHashMap(15, 1.5, 0.5, hashFunction); - - try - { - Field theKeys = hashMap.getClass().getDeclaredField("theKeys"); - theKeys.setAccessible(true); - Field theValues = hashMap.getClass().getDeclaredField("theValues"); - theValues.setAccessible(true); - Field occupiedSinceLastRehash = hashMap.getClass().getDeclaredField("occupiedSinceLastRehash"); - occupiedSinceLastRehash.setAccessible(true); - - theKeys.set(hashMap, new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "test", null}); - theValues.set(hashMap, new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, "value", null}); - occupiedSinceLastRehash.set(hashMap, new boolean[] {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}); - - assertEquals("value", hashMap.remove("test")); - - Object[] keys = (Object[])theKeys.get(hashMap); - Object[] values = (Object[])theValues.get(hashMap); - Object[] occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, null, null}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, null, null}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - occupied)); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - @Test - public void H03_MyIndexHoppingHashMapRemoveMultiple() - { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(15, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - MyIndexHoppingHashMap hashMap = new MyIndexHoppingHashMap(15, 1.5, 0.5, hashFunction); - - try - { - Field theKeys = hashMap.getClass().getDeclaredField("theKeys"); - theKeys.setAccessible(true); - Field theValues = hashMap.getClass().getDeclaredField("theValues"); - theValues.setAccessible(true); - Field occupiedSinceLastRehash = hashMap.getClass().getDeclaredField("occupiedSinceLastRehash"); - occupiedSinceLastRehash.setAccessible(true); - - theKeys.set(hashMap, new Object[] {null, null, null, null, "m", null, null, null, null, null, null, null, null, "test", "key"}); - theValues.set(hashMap, new Object[] {null, null, null, null, "a", null, null, null, null, null, null, null, null, "value", 2}); - occupiedSinceLastRehash.set(hashMap, new boolean[] {false, false, false, false, true, false, false, false, false, false, false, false, false, true, true}); - - assertEquals("value", hashMap.remove("test")); - - Object[] keys = (Object[])theKeys.get(hashMap); - Object[] values = (Object[])theValues.get(hashMap); - Object[] occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, "m", null, null, null, null, null, null, null, null, null, "key"}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, "a", null, null, null, null, null, null, null, null, null, 2}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, false, false, true, false, false, false, false, false, false, false, false, true, true}, - occupied)); - - assertEquals("a", hashMap.remove("m")); - - keys = (Object[])theKeys.get(hashMap); - values = (Object[])theValues.get(hashMap); - occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, null, "key"}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, null, 2}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, false, false, true, false, false, false, false, false, false, false, false, true, true}, - occupied)); - - - assertEquals(2, hashMap.remove("key")); - - keys = (Object[])theKeys.get(hashMap); - values = (Object[])theValues.get(hashMap); - occupied = toObjectArray((boolean[])occupiedSinceLastRehash.get(hashMap)); - - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, null, null}, - keys)); - assertEquals(true, Arrays.deepEquals( - new Object[] {null, null, null, null, null, null, null, null, null, null, null, null, null, null, null}, - values)); - assertEquals(true, Arrays.deepEquals( - new Object[] {false, false, false, false, true, false, false, false, false, false, false, false, false, true, true}, - occupied)); - - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - ///////////// - // Contains - ///////////// - - @Test - public void H03_MyIndexHoppingHashMapContainsKeyTrue() - { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(15, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - MyIndexHoppingHashMap hashMap = new MyIndexHoppingHashMap(15, 1.5, 0.5, hashFunction); - - try - { - Field theKeys = hashMap.getClass().getDeclaredField("theKeys"); - theKeys.setAccessible(true); - Field theValues = hashMap.getClass().getDeclaredField("theValues"); - theValues.setAccessible(true); - Field occupiedSinceLastRehash = hashMap.getClass().getDeclaredField("occupiedSinceLastRehash"); - occupiedSinceLastRehash.setAccessible(true); - - theKeys.set(hashMap, new Object[] {null, null, null, null, "m", null, null, null, null, null, null, null, null, "test", "key"}); - theValues.set(hashMap, new Object[] {null, null, null, null, "a", null, null, null, null, null, null, null, null, "value", 2}); - occupiedSinceLastRehash.set(hashMap, new boolean[] {false, false, false, false, true, false, false, false, false, false, false, false, false, true, true}); - - assertEquals(true, hashMap.containsKey("test")); - assertEquals(true, hashMap.containsKey("key")); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - @Test - public void H03_MyIndexHoppingHashMapContainsKeyFalse() - { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(15, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - MyIndexHoppingHashMap hashMap = new MyIndexHoppingHashMap(15, 1.5, 0.5, hashFunction); - - try - { - Field theKeys = hashMap.getClass().getDeclaredField("theKeys"); - theKeys.setAccessible(true); - Field theValues = hashMap.getClass().getDeclaredField("theValues"); - theValues.setAccessible(true); - Field occupiedSinceLastRehash = hashMap.getClass().getDeclaredField("occupiedSinceLastRehash"); - occupiedSinceLastRehash.setAccessible(true); - - assertEquals(false, hashMap.containsKey("test")); - - theKeys.set(hashMap, new Object[] {null, null, null, null, "m", null, null, null, null, null, null, null, null, "test", "key"}); - theValues.set(hashMap, new Object[] {null, null, null, null, "a", null, null, null, null, null, null, null, null, "value", 2}); - occupiedSinceLastRehash.set(hashMap, new boolean[] {false, false, false, false, true, false, false, false, false, false, false, false, false, true, true}); - - assertEquals(false, hashMap.containsKey("l")); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - ///////////// - // GetValue - ///////////// - - @Test - public void H03_MyIndexHoppingHashMapGetValuePositive() - { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(15, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - MyIndexHoppingHashMap hashMap = new MyIndexHoppingHashMap(15, 1.5, 0.5, hashFunction); - - try - { - Field theKeys = hashMap.getClass().getDeclaredField("theKeys"); - theKeys.setAccessible(true); - Field theValues = hashMap.getClass().getDeclaredField("theValues"); - theValues.setAccessible(true); - Field occupiedSinceLastRehash = hashMap.getClass().getDeclaredField("occupiedSinceLastRehash"); - occupiedSinceLastRehash.setAccessible(true); - - theKeys.set(hashMap, new Object[] {null, null, null, null, "m", null, null, null, null, null, null, null, null, "test", "key"}); - theValues.set(hashMap, new Object[] {null, null, null, null, "a", null, null, null, null, null, null, null, null, "value", 2}); - occupiedSinceLastRehash.set(hashMap, new boolean[] {false, false, false, false, true, false, false, false, false, false, false, false, false, true, true}); - - assertEquals("value", hashMap.getValue("test")); - assertEquals(2, hashMap.getValue("key")); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - @Test - public void H03_MyIndexHoppingHashMapGetValueNegative() - { - Hash2IndexFct internalHashFunction = new Hash2IndexFct(15, 0); - LinearProbing hashFunction = new LinearProbing(internalHashFunction); - MyIndexHoppingHashMap hashMap = new MyIndexHoppingHashMap(15, 1.5, 0.5, hashFunction); - - try - { - Field theKeys = hashMap.getClass().getDeclaredField("theKeys"); - theKeys.setAccessible(true); - Field theValues = hashMap.getClass().getDeclaredField("theValues"); - theValues.setAccessible(true); - Field occupiedSinceLastRehash = hashMap.getClass().getDeclaredField("occupiedSinceLastRehash"); - occupiedSinceLastRehash.setAccessible(true); - - theKeys.set(hashMap, new Object[] {null, null, null, null, "m", null, null, null, null, null, null, null, null, "test", "key"}); - theValues.set(hashMap, new Object[] {null, null, null, null, "a", null, null, null, null, null, null, null, null, "value", 2}); - occupiedSinceLastRehash.set(hashMap, new boolean[] {false, false, false, false, true, false, false, false, false, false, false, false, false, true, true}); - - - assertEquals(null, hashMap.getValue("testa")); - assertEquals(null, hashMap.getValue("keya")); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - private Object[] toObjectArray(boolean[] boolArray) - { - Object[] booleanArray = new Boolean[boolArray.length]; - for (int i = 0; i < boolArray.length; i++) - { - booleanArray[i] = boolArray[i]; - } - return booleanArray; - } -} diff --git a/src/grader/java/h06/H04_Tests.java b/src/grader/java/h06/H04_Tests.java deleted file mode 100644 index 7f12d62..0000000 --- a/src/grader/java/h06/H04_Tests.java +++ /dev/null @@ -1,471 +0,0 @@ -package h06; - -import static org.junit.jupiter.api.Assertions.*; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.lang.reflect.Array; -import java.lang.reflect.Field; -import java.util.LinkedList; - -import org.junit.jupiter.api.Test; - -import org.sourcegrade.jagr.api.rubric.TestForSubmission; - -@TestForSubmission("H06") -public class H04_Tests -{ - ///////////////// - // KeyValuePair - ///////////////// - - @Test - public void H04_KeyValuePair() - { - // Constructor - KeyValuePair keyValuePair = new KeyValuePair(10, "test"); - - // Key - assertEquals(10, keyValuePair.getKey()); - - // Value - assertEquals("test", keyValuePair.getValue()); - keyValuePair.setValue(true); - assertEquals(true, keyValuePair.getValue()); - } - - - ////////////////////////// - // MyIndexHoppingHashMap - ////////////////////////// - - @Test - public void H04_MyListsHashMapBasic() - { - // Constructor - Hash2IndexFct hashFunction = new Hash2IndexFct(10, 0); - new MyListsHashMap(hashFunction); - - // Type parameter - try - { - // new MyListsHashMap, Object>(null); - // fail("MyListsHashMap type parameter is not bound to classes."); - } - catch (Exception e){} - - // Interface - Class[] interfaces = MyListsHashMap.class.getInterfaces(); - assertEquals(1, interfaces.length); - assertEquals(MyMap.class.getName(), interfaces[0].getName()); - } - - //////// - // Put - //////// - - @Test - public void H04_MyListsHashMapMapPutSingle() - { - Hash2IndexFct hashFunction = new Hash2IndexFct(13, 0); - MyListsHashMap hashMap = new MyListsHashMap(hashFunction); - - try - { - Field tableField = hashMap.getClass().getDeclaredField("table"); - tableField.setAccessible(true); - - assertEquals(null, hashMap.put("test", "value")); - int containerIndex = hashFunction.apply("test"); - - LinkedList>[] tableReference = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - - for (int i = 0; i < 13; i++) - { - tableReference[i] = new LinkedList>(); - } - - tableReference[containerIndex].add(new KeyValuePair("test", "value")); - - LinkedList>[] table = (LinkedList>[])tableField.get(hashMap); - tablesEqual(tableReference, table); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - - @Test - public void H04_MyListsHashMapPutReplace() - { - Hash2IndexFct hashFunction = new Hash2IndexFct(13, 0); - MyListsHashMap hashMap = new MyListsHashMap(hashFunction); - - try - { - Field tableField = hashMap.getClass().getDeclaredField("table"); - tableField.setAccessible(true); - - assertEquals(null, hashMap.put("test-replace", "value")); - int containerIndex = hashFunction.apply("test-replace"); - - LinkedList>[] tableReference = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - - for (int i = 0; i < 13; i++) - { - tableReference[i] = new LinkedList>(); - } - - tableReference[containerIndex].add(new KeyValuePair("test-replace", "value")); - - LinkedList>[] table = (LinkedList>[])tableField.get(hashMap); - tablesEqual(tableReference, table); - - assertEquals("value", hashMap.put("test-replace", "a")); - - tableReference[containerIndex].clear(); - tableReference[containerIndex].add(new KeyValuePair("test-replace", "a")); - - table = (LinkedList>[])tableField.get(hashMap); - tablesEqual(tableReference, table); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - @Test - public void H04_MyListsHashMapPutMultiple() - { - Hash2IndexFct hashFunction = new Hash2IndexFct(13, 0); - MyListsHashMap hashMap = new MyListsHashMap(hashFunction); - - try - { - int containerIndexTest = hashFunction.apply("test"); - int containerIndexE = hashFunction.apply("e"); - int containerIndexA = hashFunction.apply("a"); - - assertEquals(containerIndexTest, containerIndexE); - - Field tableField = hashMap.getClass().getDeclaredField("table"); - tableField.setAccessible(true); - - LinkedList>[] tableReference = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - - for (int i = 0; i < 13; i++) - { - tableReference[i] = new LinkedList>(); - } - - tableReference[containerIndexTest].addFirst(new KeyValuePair("test", "value")); - - assertEquals(null, hashMap.put("test", "value")); - LinkedList>[] table = (LinkedList>[])tableField.get(hashMap); - tablesEqual(tableReference, table); - - tableReference[containerIndexE].addFirst(new KeyValuePair("e", 2)); - - assertEquals(null, hashMap.put("e", 2)); - table = (LinkedList>[])tableField.get(hashMap); - tablesEqual(tableReference, table); - - tableReference[containerIndexA].addFirst(new KeyValuePair("a", "a")); - - assertEquals(null, hashMap.put("a", "a")); - table = (LinkedList>[])tableField.get(hashMap); - tablesEqual(tableReference, table); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - /////////// - // Remove - /////////// - @Test - public void H04_MyListsHashMapRemoveEmpty() - { - Hash2IndexFct hashFunction = new Hash2IndexFct(13, 0); - MyListsHashMap hashMap = new MyListsHashMap(hashFunction); - - try - { - Field tableField = hashMap.getClass().getDeclaredField("table"); - tableField.setAccessible(true); - - LinkedList>[] tableReference = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - - for (int i = 0; i < 13; i++) - { - tableReference[i] = new LinkedList>(); - } - - assertEquals(null, hashMap.remove("test")); - LinkedList>[] table = (LinkedList>[])tableField.get(hashMap); - tablesEqual(tableReference, table); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - @Test - public void H04_MyListsHashMapRemoveSingle() - { - Hash2IndexFct hashFunction = new Hash2IndexFct(13, 0); - MyListsHashMap hashMap = new MyListsHashMap(hashFunction); - - try - { - int containerIndex = hashFunction.apply("hallo"); - LinkedList>[] table = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - for (int i = 0; i < 13; i++) - { - table[i] = new LinkedList>(); - } - table[containerIndex].addFirst(new KeyValuePair("hallo", "wert")); - - Field tableField = hashMap.getClass().getDeclaredField("table"); - tableField.setAccessible(true); - tableField.set(hashMap, table); - - LinkedList>[] tableReference = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - for (int i = 0; i < 13; i++) - { - tableReference[i] = new LinkedList>(); - } - - assertEquals("wert", hashMap.remove("hallo")); - LinkedList>[] resultTable = (LinkedList>[])tableField.get(hashMap); - tablesEqual(tableReference, table); - } - catch (Exception e) - { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - fail(sw.toString()); - } - } - -@SuppressWarnings("unchecked") -@Test - public void H04_MyListsHashMapRemoveMultiple() - { - Hash2IndexFct hashFunction = new Hash2IndexFct(13, 0); - MyListsHashMap hashMap = new MyListsHashMap(hashFunction); - - try - { - int containerIndexTest = hashFunction.apply("test"); - int containerIndexA = hashFunction.apply("a"); - int containerIndexE = hashFunction.apply("e"); - - assertEquals(containerIndexTest, containerIndexE); - - LinkedList>[] table = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - for (int i = 0; i < 13; i++) - { - table[i] = new LinkedList>(); - } - table[containerIndexTest].addFirst(new KeyValuePair("test", "wert")); - table[containerIndexA].addFirst(new KeyValuePair("a", 12)); - table[containerIndexE].addFirst(new KeyValuePair("e", 10)); - - Field tableField = hashMap.getClass().getDeclaredField("table"); - tableField.setAccessible(true); - tableField.set(hashMap, table); - - LinkedList>[] tableReference = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - for (int i = 0; i < 13; i++) - { - tableReference[i] = new LinkedList>(); - } - tableReference[containerIndexA].addFirst(new KeyValuePair("a", 12)); - tableReference[containerIndexE].addFirst(new KeyValuePair("e", 10)); - - assertEquals("wert", hashMap.remove("test")); - LinkedList>[] tableResult = (LinkedList>[])tableField.get(hashMap); - tablesEqual(tableReference, tableResult); - - tableReference[containerIndexA].clear(); - assertEquals(12, hashMap.remove("a")); - tableResult = (LinkedList>[])tableField.get(hashMap); - tablesEqual(tableReference, tableResult); - - tableReference[containerIndexE].clear(); - assertEquals(10, hashMap.remove("e")); - tableResult = (LinkedList>[])tableField.get(hashMap); - tablesEqual(tableReference, tableResult); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - ///////////// - // Contains - ///////////// - @SuppressWarnings("unchecked") - @Test - public void H04_MyListsHashMapContainsKeyTrue() - { - Hash2IndexFct hashFunction = new Hash2IndexFct(13, 0); - MyListsHashMap hashMap = new MyListsHashMap(hashFunction); - - try - { - LinkedList>[] table = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - - for (int i = 0; i < 13; i++) - { - table[i] = new LinkedList>(); - } - - table[hashFunction.apply(10)].add(new KeyValuePair(10, 10)); - table[hashFunction.apply("test")].add(new KeyValuePair("test", 123)); - table[hashFunction.apply("key")].add(new KeyValuePair("key", "hallo")); - - Field tableField = hashMap.getClass().getDeclaredField("table"); - tableField.setAccessible(true); - tableField.set(hashMap, table); - - assertEquals(true, hashMap.containsKey("test")); - assertEquals(true, hashMap.containsKey("key")); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - @Test - public void H04_MyListsHashMapContainsKeyFalse() - { - Hash2IndexFct hashFunction = new Hash2IndexFct(13, 0); - MyListsHashMap hashMap = new MyListsHashMap(hashFunction); - - try - { - Field tableField = hashMap.getClass().getDeclaredField("table"); - tableField.setAccessible(true); - - LinkedList>[] table = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - - for (int i = 0; i < 13; i++) - { - table[i] = new LinkedList>(); - } - - table[hashFunction.apply("e")].add(new KeyValuePair("e", 10)); - table[hashFunction.apply("testen")].add(new KeyValuePair("testen", 123)); - table[hashFunction.apply(0)].add(new KeyValuePair(0, "hallo")); - - assertEquals(false, hashMap.containsKey("test")); - - tableField.set(hashMap, table); - - assertEquals(false, hashMap.containsKey("key")); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - ///////////// - // GetValue - ///////////// - - @Test - public void H04_MyIndexHoppingHashMapGetValuePositive() - { - Hash2IndexFct hashFunction = new Hash2IndexFct(13, 0); - MyListsHashMap hashMap = new MyListsHashMap(hashFunction); - - try - { - Field tableField = hashMap.getClass().getDeclaredField("table"); - tableField.setAccessible(true); - - LinkedList>[] table = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - - for (int i = 0; i < 13; i++) - { - table[i] = new LinkedList>(); - } - - table[hashFunction.apply("e")].add(new KeyValuePair("e", 10)); - table[hashFunction.apply("test")].add(new KeyValuePair("test", 123)); - table[hashFunction.apply(0)].add(new KeyValuePair(0, "hallo")); - - tableField.set(hashMap, table); - - assertEquals(10, hashMap.getValue("e")); - assertEquals(123, hashMap.getValue("test")); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - @Test - public void H04_MyIndexHoppingHashMapGetValueNegative() - { - Hash2IndexFct hashFunction = new Hash2IndexFct(13, 0); - MyListsHashMap hashMap = new MyListsHashMap(hashFunction); - - try - { - Field tableField = hashMap.getClass().getDeclaredField("table"); - tableField.setAccessible(true); - - LinkedList>[] table = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); - - for (int i = 0; i < 13; i++) - { - table[i] = new LinkedList>(); - } - - table[hashFunction.apply("e")].add(new KeyValuePair("e", 10)); - table[hashFunction.apply("test")].add(new KeyValuePair("test", 123)); - table[hashFunction.apply(0)].add(new KeyValuePair(0, "hallo")); - - tableField.set(hashMap, table); - - assertEquals(null, hashMap.getValue("b")); - assertEquals(null, hashMap.getValue("c")); - } - catch (Exception e) - { - fail(e.getMessage()); - } - } - - private void tablesEqual(LinkedList>[] referenceTable, LinkedList>[] table) - { - assertEquals(referenceTable.length, table.length, "Die Hashtabelle hat die Länge " + referenceTable.length + ", erwartet war: " + table.length); - - for (int i = 0; i < table.length; i++) - { - if (referenceTable[i].size() > 0) - assertNotEquals(null, table[i], "Liste in Container " + i + " entspricht nicht den Erwartungen (ist null)"); - - assertEquals(referenceTable[i].size(), table[i].size()); - for (int j = 0; j < table[i].size(); j++) - { - assertEquals(referenceTable[i].get(j).getKey(), table[i].get(j).getKey()); - assertEquals(referenceTable[i].get(j).getValue(), table[i].get(j).getValue()); - } - } - } -} diff --git a/src/grader/java/h06/H05_Tests.java b/src/grader/java/h06/H05_Tests.java deleted file mode 100644 index 472c91c..0000000 --- a/src/grader/java/h06/H05_Tests.java +++ /dev/null @@ -1,49 +0,0 @@ -package h06; - -import static org.junit.jupiter.api.Assertions.*; - -import java.util.Calendar; - -import org.junit.jupiter.api.Test; - -import h06.*; -import org.sourcegrade.jagr.api.rubric.TestForSubmission; - -@TestForSubmission("H06") -public class H05_Tests -{ - @Test - public void H05_MyDateBasic() - { - Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.MINUTE, 40); - calendar.set(Calendar.HOUR_OF_DAY, 10); - calendar.set(Calendar.DAY_OF_MONTH, 19); - calendar.set(Calendar.MONTH, Calendar.APRIL); - calendar.set(Calendar.YEAR, 2000); - MyDate date = new MyDate(calendar, true); - - assertEquals(40, date.getMinute()); - assertEquals(10, date.getHour()); - assertEquals(19, date.getDay()); - assertEquals(3, date.getMonth()); - assertEquals(2000, date.getYear()); - } - - @Test - public void H05_MyDateHash() - { - Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.MINUTE, 40); - calendar.set(Calendar.HOUR, 10); - calendar.set(Calendar.DAY_OF_MONTH, 19); - calendar.set(Calendar.MONTH, Calendar.APRIL); - calendar.set(Calendar.YEAR, 2000); - - MyDate dateFalse = new MyDate(calendar, false); - MyDate dateTrue = new MyDate(calendar, true); - - assertEquals(206157616, dateFalse.hashCode()); - assertEquals(470580413, dateTrue.hashCode()); - } -} diff --git a/src/grader/java/h06/H06_RubricProvider.java b/src/grader/java/h06/H06_RubricProvider.java index 80ec9de..6227e08 100644 --- a/src/grader/java/h06/H06_RubricProvider.java +++ b/src/grader/java/h06/H06_RubricProvider.java @@ -2,362 +2,41 @@ import org.sourcegrade.jagr.api.rubric.*; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.concurrent.Callable; + @RubricForSubmission("h06") public class H06_RubricProvider implements RubricProvider { - //////// - // H1 // - //////// - - public static final Criterion H1_Interfaces = Criterion.builder() - .shortDescription("Interfaces OtherToIntFunction and OtherAndIntToIntFunction") - .maxPoints(1) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H01_Tests.class.getMethod("H01_OtherToIntFunction"))) - .requirePass(JUnitTestRef.ofMethod(() -> H01_Tests.class.getMethod("H01_OtherAndIntToIntFunction"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H1_HashCodeTableIndexFct = Criterion.builder() - .shortDescription("Class HashCodeTableIndexFct") - .maxPoints(1) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsa.class.getMethod("H01_HashCodeTableIndexFctBasic"))) - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsa.class.getMethod("H01_HashCodeTableIndexFctTableSize"))) - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsa.class.getMethod("H01_HashCodeTableIndexFctApplyBasic"))) - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsa.class.getMethod("H01_HashCodeTableIndexFctApplyAdvanced"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H1_LinearProbingTableIndexFct = Criterion.builder() - .shortDescription("Class LinearProbingTableIndexFct") - .maxPoints(2) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsb.class.getMethod("H01_LinearProbingTableIndexFctBasic"))) - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsb.class.getMethod("H01_LinearProbingTableIndexFctTableSize"))) - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsb.class.getMethod("H01_LinearProbingTableIndexFctApplyBasic"))) - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsb.class.getMethod("H01_LinearProbingTableIndexFctApplyAdvanced"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H1_DoubleHashingTableIndexFct = Criterion.builder() - .shortDescription("Class DoubleHashingTableIndexFct") - .maxPoints(2) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsc.class.getMethod("H01_DoubleHashingTableIndexFctBasic"))) - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsc.class.getMethod("H01_DoubleHashingTableIndexFctTableSize"))) - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsc.class.getMethod("H01_DoubleHashingTableIndexFctApplyBasic"))) - .requirePass(JUnitTestRef.ofMethod(() -> H01_Testsc.class.getMethod("H01_DoubleHashingTableIndexFctApplyAdvanced"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H1 = Criterion.builder() - .shortDescription("H1") - .addChildCriteria( - //H1_Interfaces, - H1_HashCodeTableIndexFct, - H1_LinearProbingTableIndexFct, - H1_DoubleHashingTableIndexFct - ) // maxPoints and minPoints and grading is inferred from child criteria - .build(); - - //////// - // H2 // - //////// - - public static final Criterion H2_MyMap = Criterion.builder() - .shortDescription("Interface MyMap") - .maxPoints(1) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H02_Tests.class.getMethod("H02_MyMap"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H2 = Criterion.builder() - .shortDescription("H2") - .addChildCriteria( - H2_MyMap - ) // maxPoints and minPoints and grading is inferred from child criteria - .build(); - - //////// - // H3 // - //////// - - public static final Criterion H3_MyIndexHoppingHashMap = Criterion.builder() - .shortDescription("Class MyIndexHoppingHashMap") - .maxPoints(1) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapBasic"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H3_MyIndexHoppingHashMapPut = Criterion.builder() - .shortDescription("Class MyIndexHoppingHashMap - Method Put") - .maxPoints(3) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.descendingPriority( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapPutSingle"))) - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapPutMultiple"))) - .pointsPassed((testCycle, criterion) -> GradeResult.ofCorrect(1)) - .pointsFailed((testCycle, criterion) -> GradeResult.ofIncorrect(1)) - .build(), - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapPutReplace"))) - .pointsPassed((testCycle, criterion) -> GradeResult.ofCorrect(1)) - .pointsFailed((testCycle, criterion) -> GradeResult.ofIncorrect(1)) - .build(), - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapPutMultipleResize"))) - .pointsPassed((testCycle, criterion) -> GradeResult.ofCorrect(1)) - .pointsFailed((testCycle, criterion) -> GradeResult.ofIncorrect(1)) - .build() - ) - ).build(); - - public static final Criterion H3_MyIndexHoppingHashMapRemove = Criterion.builder() - .shortDescription("Class MyIndexHoppingHashMap - Method Remove") - .maxPoints(2) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapRemoveEmpty"))) - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapRemoveSingle"))) - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapRemoveMultiple"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H3_MyIndexHoppingHashMapContainsKey = Criterion.builder() - .shortDescription("Class MyIndexHoppingHashMap - Method ContainsKey") - .maxPoints(1) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapContainsKeyTrue"))) - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapContainsKeyFalse"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H3_MyIndexHoppingHashMapGetValue = Criterion.builder() - .shortDescription("Class MyIndexHoppingHashMap - Method GetValue") - .maxPoints(1) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapGetValuePositive"))) - .requirePass(JUnitTestRef.ofMethod(() -> H03_Tests.class.getMethod("H03_MyIndexHoppingHashMapGetValueNegative"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H3 = Criterion.builder() - .shortDescription("H3") - .addChildCriteria( - H3_MyIndexHoppingHashMap, - H3_MyIndexHoppingHashMapPut, - H3_MyIndexHoppingHashMapRemove, - H3_MyIndexHoppingHashMapContainsKey, - H3_MyIndexHoppingHashMapGetValue - ) // maxPoints and minPoints and grading is inferred from child criteria - .build(); - - //////// - // H4 // - //////// - - public static final Criterion H4_MyListsHashMap = Criterion.builder() - .shortDescription("Class MyListsHashMap") - .maxPoints(1) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H04_Tests.class.getMethod("H04_MyListsHashMapBasic"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H4_MyListsHashMapPut = Criterion.builder() - .shortDescription("Class MyListsHashMap - Method Put") - .maxPoints(2) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H04_Tests.class.getMethod("H04_MyListsHashMapMapPutSingle"))) - .requirePass(JUnitTestRef.ofMethod(() -> H04_Tests.class.getMethod("H04_MyListsHashMapPutReplace"))) - .requirePass(JUnitTestRef.ofMethod(() -> H04_Tests.class.getMethod("H04_MyListsHashMapPutMultiple"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - public static final Criterion H4_MyListsHashMapRemove = Criterion.builder() - .shortDescription("Class MyListsHashMap - Method Remove") - .maxPoints(2) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H04_Tests.class.getMethod("H04_MyListsHashMapRemoveEmpty"))) - .requirePass(JUnitTestRef.ofMethod(() -> H04_Tests.class.getMethod("H04_MyListsHashMapRemoveSingle"))) - .requirePass(JUnitTestRef.ofMethod(() -> H04_Tests.class.getMethod("H04_MyListsHashMapRemoveMultiple"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H4_MyListsHashMapContainsKey = Criterion.builder() - .shortDescription("Class MyListsHashMap - Method ContainsKey") - .maxPoints(1) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H04_Tests.class.getMethod("H04_MyListsHashMapContainsKeyTrue"))) - .requirePass(JUnitTestRef.ofMethod(() -> H04_Tests.class.getMethod("H04_MyListsHashMapContainsKeyFalse"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H4_MyListsHashMapGetValue = Criterion.builder() - .shortDescription("Class MyListsHashMap - Method GetValue") - .maxPoints(1) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H04_Tests.class.getMethod("H04_MyIndexHoppingHashMapGetValuePositive"))) - .requirePass(JUnitTestRef.ofMethod(() -> H04_Tests.class.getMethod("H04_MyIndexHoppingHashMapGetValueNegative"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H4 = Criterion.builder() - .shortDescription("H4") - .addChildCriteria( - H4_MyListsHashMap, - H4_MyListsHashMapPut, - H4_MyListsHashMapRemove, - H4_MyListsHashMapContainsKey, - H4_MyListsHashMapGetValue - ) // maxPoints and minPoints and grading is inferred from child criteria - .build(); - - //////// - // H5 // - //////// - - public static final Criterion H5_MyDate = Criterion.builder() - .shortDescription("Class MyDate") - .maxPoints(2) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H05_Tests.class.getMethod("H05_MyDateBasic"))) - .requirePass(JUnitTestRef.ofMethod(() -> H05_Tests.class.getMethod("H05_MyDateHash"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H5 = Criterion.builder() - .shortDescription("H5") - .addChildCriteria( - H5_MyDate - ) // maxPoints and minPoints and grading is inferred from child criteria - .build(); - - //////// - // H6 // - //////// - - public static final Criterion H06_RuntimeTestGenerateTestData = Criterion.builder() - .shortDescription("Class RuntimeTest - Method GenerateTestData") - .maxPoints(2) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H06_Tests.class.getMethod("H06_RuntimeTestGenerateTestData"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - public static final Criterion H06_RuntimeTestCreateTestSet = Criterion.builder() - .shortDescription("Class RuntimeTest - Method CreateTestSet") - .maxPoints(2) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H06_Tests.class.getMethod("H06_RuntimeTestGenerateTestData"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); - - - public static final Criterion H06_RuntimeTestExecuteTest = Criterion.builder() - .shortDescription("Class RuntimeTest - Method Test") - .maxPoints(1) // default maxPoints is 1 - .minPoints(0) // default minPoints is 0 - .grader( - Grader.testAwareBuilder() - .requirePass(JUnitTestRef.ofMethod(() -> H06_Tests.class.getMethod("H06_RuntimeTestExecuteTest"))) - .pointsPassedMax() // award maximum points if ALL tests passed - .pointsFailedMin() // award minimum points if ANY test failed - .build() - ).build(); + @Override + public Rubric getRubric() { + return null; + } - public static final Criterion H6 = Criterion.builder() - .shortDescription("H6") - .addChildCriteria( - H06_RuntimeTestGenerateTestData, - H06_RuntimeTestCreateTestSet, - H06_RuntimeTestExecuteTest - ) // maxPoints and minPoints and grading is inferred from child criteria - .build(); + @SafeVarargs + private static Criterion makeCriterion(String shortDescription, Callable... callables) { + return makeCriterion(shortDescription, 0, 1, callables); + } - public static final Rubric RUBRIC = Rubric.builder() - .title("Ü6 Tests") - .addChildCriteria(H1) - //.addChildCriteria(H2) - .addChildCriteria(H3) - .addChildCriteria(H4) - .addChildCriteria(H5) - .addChildCriteria(H6) - .build(); + @SafeVarargs + private static Criterion makeCriterion(String shortDescription, int minPoints, int maxPoints, Callable... callables) { + return Criterion.builder() + .shortDescription(shortDescription) + .minPoints(minPoints) + .maxPoints(maxPoints) + .grader(Grader.testAwareBuilder() + .requirePass(JUnitTestRef.and(Arrays.stream(callables).map(JUnitTestRef::ofMethod).toArray(JUnitTestRef[]::new))) + .pointsFailedMin() + .pointsPassedMax() + .build()) + .build(); + } - @Override - public Rubric getRubric() { - return RUBRIC; + private static Criterion makeCriterionFromChildCriteria(String shortDescription, Criterion... criteria) { + return Criterion.builder() + .shortDescription(shortDescription) + .addChildCriteria(criteria) + .build(); } } diff --git a/src/grader/java/h06/H06_Tests.java b/src/grader/java/h06/H06_Tests.java deleted file mode 100644 index 0603968..0000000 --- a/src/grader/java/h06/H06_Tests.java +++ /dev/null @@ -1,120 +0,0 @@ -package h06; - -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.sourcegrade.jagr.api.rubric.TestForSubmission; - -import java.lang.reflect.Field; -import java.util.LinkedList; - -import static org.junit.jupiter.api.Assertions.*; - -@TestForSubmission("H06") -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class H06_Tests { - private static MyDate[][] testData; - - final int TestDataSize = 1_000; - - @Test - @Order(1) - public void H06_RuntimeTestGenerateTestData() { - testData = RuntimeTest.generateTestdata(); - assertEquals(testData.length, 2, "Das Haupt-Array der Testdaten sollte die Länge 2 haben."); - assertEquals(testData[0].length, 1000, "Das Unter-Array 0 der Testdaten sollte die Länge 1000 haben."); - assertEquals(testData[1].length, 1000, "Das Unter-Array 1 der Testdaten sollte die Länge 1000 haben."); - - for (int i = 0; i < 1000; i++) { - checkDate(true, testData[0][i]); - checkDate(false, testData[1][i]); - } - } - - private void checkDate(boolean expectedTrue, MyDate date) { - assertTrue(date.getYear() <= 2022 && date.getYear() >= 1970, - "Das Jahr der generierten Testdaten sollte zwischen 1970 und 2022 liegen. Es wurde ein Datum mit folgendem Jahr gefunden: " + date.getYear()); - assertEquals(expectedTrue, date.getBool(), - "Es wurde erwartet, dass alle Daten im Unter-Array" + (expectedTrue ? " 0 " : "1 ") + "den Wert " + expectedTrue + " haben. Es wurde ein Datum mit Wert " + date.getBool() + " gefunden."); - } - - @Test - @Order(2) - public void H06_RuntimeTestCreateTestSet() { - for (int i = 1; i <= 2; i++) { - for (int j = 1; j <= 2; j++) { - for (int k = 1; k <= 2; k++) { - for (int l = 1; l <= 2; l++) { - try { - TestSet testSet = RuntimeTest.createTestSet(i, j, k, l, testData); - TestSet solutionTestSet = createTestSet(i, j, k, l, testData); - assertArrayEquals(testSet.getTestData(), solutionTestSet.getTestData()); - MyMap testSetHashTable = testSet.getHashTable(); - MyMap solutionTestSetHashTable = testSet.getHashTable(); - assertEquals(testSetHashTable.getClass(), solutionTestSetHashTable.getClass()); - - if (solutionTestSetHashTable.getClass().equals(MyIndexHoppingHashMap.class)){ - Field tableField = testSetHashTable.getClass().getDeclaredField("theKeys"); - Field solutionTableField = solutionTestSetHashTable.getClass().getDeclaredField("theKeys"); - tableField.setAccessible(true); - solutionTableField.setAccessible(true); - - assertEquals(((Object[])tableField.get(testSetHashTable)).length, - ((Object[])solutionTableField.get(solutionTestSetHashTable)).length); - } - else { - Field tableField = testSetHashTable.getClass().getDeclaredField("table"); - Field solutionTableField = solutionTestSetHashTable.getClass().getDeclaredField("table"); - tableField.setAccessible(true); - solutionTableField.setAccessible(true); - - assertEquals(((LinkedList>[])tableField.get(testSetHashTable)).length, - ((LinkedList>[])solutionTableField.get(solutionTestSetHashTable)).length); - } - } catch (Exception e) { - fail(e.getMessage()); - } - } - } - } - } - } - - private final static int TEST_SET_SIZE = 1_000; - - public static TestSet createTestSet(int i, int j, int k, int l, MyDate[][] testData) { - MyDate[] usedTestData = testData[i - 1]; - - int initialTableSize = (l == 1) ? ((j == 1) ? 4096 : TEST_SET_SIZE * 3) - : ((j == 1) ? 64 : TEST_SET_SIZE / 10); - - Hash2IndexFct basicHashFunction = new Hash2IndexFct(initialTableSize, 0); - - MyMap hashMap; - - if (j == 1) { - BinaryFct2Int hashFunction = (k == 1) ? new LinearProbing(basicHashFunction) : - new DoubleHashing(basicHashFunction, new Hash2IndexFct(initialTableSize, 42)); - hashMap = new MyIndexHoppingHashMap(initialTableSize, 2.0, 0.75, hashFunction); - } - else { - hashMap = new MyListsHashMap(basicHashFunction); - } - - return new TestSet(hashMap, usedTestData); - } - - @Test - @Order(3) - public void H06_RuntimeTestExecuteTest() { - Tutor_HashMap hashMap = new Tutor_HashMap<>(); - TestSet testSet = new TestSet<>(hashMap, testData[0]); - RuntimeTest.test(testSet); - - assertEquals(TestDataSize, hashMap.getContainsKeyCount(), "Test run() die Anzahl der Aufrufe für containsKey ist falsch."); - assertEquals(TestDataSize, hashMap.getGetValueCount(), "Test run() die Anzahl der Aufrufe für getValue ist falsch."); - assertEquals(TestDataSize * (3f/4f), hashMap.getPutCount(), "Test run() die Anzahl der Aufrufe für putCount ist falsch."); - assertEquals(TestDataSize, hashMap.getRemoveCount(), "Test run() die Anzahl der Aufrufe für removeCount ist falsch."); - } -} diff --git a/src/grader/java/h06/Tutor_HashMap.java b/src/grader/java/h06/Tutor_HashMap.java deleted file mode 100644 index e28f783..0000000 --- a/src/grader/java/h06/Tutor_HashMap.java +++ /dev/null @@ -1,49 +0,0 @@ -package h06; - -public class Tutor_HashMap implements MyMap { - - private int containsKeyCount; - private int getValueCount; - private int putCount; - private int removeCount; - - @Override - public boolean containsKey(K key) { - containsKeyCount++; - return false; - } - - @Override - public V getValue(K key) { - getValueCount++; - return null; - } - - @Override - public V put(K key, V value) { - putCount++; - return null; - } - - @Override - public V remove(K key) { - removeCount++; - return null; - } - - public int getContainsKeyCount() { - return containsKeyCount; - } - - public int getGetValueCount() { - return getValueCount; - } - - public int getPutCount() { - return putCount; - } - - public int getRemoveCount() { - return removeCount; - } -} diff --git a/src/grader/java/h06/Util_Tests.java b/src/grader/java/h06/Util_Tests.java deleted file mode 100644 index fc876e1..0000000 --- a/src/grader/java/h06/Util_Tests.java +++ /dev/null @@ -1,118 +0,0 @@ -package h06; - -import h06.*; -import org.junit.jupiter.api.Test; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.*; -import java.util.Arrays; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -public class Util_Tests -{ - @Test - public static Method checkMethod(Class klass, String methodName, Type returnType, Class... parameterTypes) - { - Method method = null; - // Search for method with correct name and parameters - try { method = klass.getDeclaredMethod(methodName, parameterTypes); } - catch (Exception e) { - // Search for method with correct parameters - Optional methodParameterSearch = Arrays.stream(klass.getDeclaredMethods()).filter(m -> m.getName().equals(methodName)).findFirst(); - if (methodParameterSearch.isPresent()) { - method = methodParameterSearch.get(); - } else { - // Search for method with correct name - Optional methodNameSearch = Arrays.stream(klass.getDeclaredMethods()).filter(m -> - (m.getParameterCount() == parameterTypes.length) && - (Arrays.deepEquals(m.getParameterTypes(), parameterTypes)) - ).findFirst(); - if (methodNameSearch.isPresent()) { - method = methodNameSearch.get(); - } else { - fail(klass.getName() + " enthält anders als erwartet nicht die Methode " + methodName); - } - } - } - - // Parameters - Parameter[] parameters = method.getParameters(); - assertEquals(parameterTypes.length, parameters.length, "Methode " + methodName + " hat anders als erwartet nicht 1 Parameter, sondern: " + parameterTypes.length); - for (int i = 0; i < parameters.length; i++) - assertEquals(parameterTypes[i], parameters[i].getType(), "Parameter " + (i + 1) + " der Methode " + methodName + " ist anders als erwartet vom Typ: " + parameters[0].getType()); - // Name check would be possible too (here) method signatur needs to change then though - - // Return Parameter - assertEquals(returnType, method.getReturnType(), "Der Rückgabetyp der Methode setTableSize ist anders als erwartet vom Typ: " + method.getReturnType()); - - return method; - } - - public static Class checkType(String typeName, String packageName, boolean isInterface, int numberOfMethods, int numberOfTypVariables) - { - Class klass = getClass(typeName, packageName); - - // Interface Check - assertEquals(isInterface, klass.isInterface(), typeName + " ist anders als erwartet " + (isInterface ? "kein" : "ein") + " Interface."); - - // Type Parameter Check - TypeVariable[] typeVariables = klass.getTypeParameters(); - assertEquals(numberOfTypVariables, typeVariables.length, (isInterface ? "Interface " : "Klasse ") + typeName + " hat anders als erwartet nicht " + numberOfTypVariables + " generische(n) Typparameter, sondern: " + typeVariables.length); - - // Method Check - if (numberOfMethods != -1) - { - Method[] methods = klass.getDeclaredMethods(); - assertEquals(numberOfMethods, methods.length, (isInterface ? "Interface " : "Klasse ") + typeName + " hat anders als erwartet nicht 3 Methoden, sondern: " + methods.length); - } - - return klass; - } - -// private static Set getClasses(String packageName) -// { -// InputStream stream = ClassLoader.getSystemClassLoader() -// .getResourceAsStream(packageName.replaceAll("[.]", "/")); -// BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); -// return reader.lines() -// .filter(line -> line.endsWith(".class") || line.endsWith(".java")) -// .map(line -> getClass(line, packageName)) -// .collect(Collectors.toSet()); -// } - - public static Class getClass(String className, String packageName) { - try { - return Class.forName(packageName + "." + className); - } catch (ClassNotFoundException e) { - // handle the exception - } - return null; - } - -// public static Constructor getConstructor(Class klass, Class... types) -// { -// Constructor constructor = null; -// try { constructor = klass.getConstructor(types);} -// catch (Exception e) { fail("Die Signatur des Konstruktur von Klasse " + klass.getName() + " entspricht nicht den Erwartung."); } -// return constructor; -// } -// -// public static void invokeConstructor(Class klass, Constructor constructor, Object... parameters) -// { -// try { -// constructor.setAccessible(true); // override access modifiers -// constructor.newInstance(parameters); -// } -// catch (Exception e) -// { -// fail("Der Konstruktor von Klasse " + klass.getName() + " hat eine unerwartete Exception zurückgeliefert: " + e.getMessage()); -// } -// } -} From b136a4d792f28271e7f0bf132d8c3ac29f33fc6c Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Wed, 22 Jun 2022 12:11:09 +0200 Subject: [PATCH 02/12] Remove illegal instruction --- src/main/java/h06/MyListsHashMap.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/h06/MyListsHashMap.java b/src/main/java/h06/MyListsHashMap.java index 1d83e2d..e2789d0 100644 --- a/src/main/java/h06/MyListsHashMap.java +++ b/src/main/java/h06/MyListsHashMap.java @@ -2,7 +2,6 @@ import org.jetbrains.annotations.Nullable; -import java.lang.reflect.Array; import java.util.LinkedList; import java.util.function.BiFunction; import java.util.function.Function; @@ -18,7 +17,7 @@ public class MyListsHashMap implements MyMap { @SuppressWarnings("unchecked") public MyListsHashMap(Fct2Int hashFunction) { this.hashFunction = hashFunction; - table = (LinkedList>[]) Array.newInstance(LinkedList.class, hashFunction.getTableSize()); + table = (LinkedList>[]) new LinkedList[hashFunction.getTableSize()]; for (int i = 0; i < table.length; i++) { table[i] = new LinkedList>(); } From ca91ea8f5a5fa860add8bcfc08f69742b8095772 Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Wed, 22 Jun 2022 12:15:34 +0200 Subject: [PATCH 03/12] Grader init --- src/grader/java/h06/Config.java | 11 ++ src/grader/java/h06/H06_RubricProvider.java | 4 +- .../java/h06/utils/ReflectionUtils.java | 113 ++++++++++++++++++ .../java/h06/utils/TutorAssertions.java | 42 +++++++ src/grader/resources/grader-info.json | 20 +++- 5 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 src/grader/java/h06/Config.java create mode 100644 src/grader/java/h06/utils/ReflectionUtils.java create mode 100644 src/grader/java/h06/utils/TutorAssertions.java diff --git a/src/grader/java/h06/Config.java b/src/grader/java/h06/Config.java new file mode 100644 index 0000000..fc342b4 --- /dev/null +++ b/src/grader/java/h06/Config.java @@ -0,0 +1,11 @@ +package h06; + +public final class Config { + + private Config() {} + + public static final long SEED = 0L; + public static final int STREAM_SIZE = 5; + + public static boolean REPLACE = true; +} diff --git a/src/grader/java/h06/H06_RubricProvider.java b/src/grader/java/h06/H06_RubricProvider.java index 6227e08..7655801 100644 --- a/src/grader/java/h06/H06_RubricProvider.java +++ b/src/grader/java/h06/H06_RubricProvider.java @@ -11,7 +11,9 @@ public class H06_RubricProvider implements RubricProvider { @Override public Rubric getRubric() { - return null; + return Rubric.builder() + .title("H6 | Hashtabellen") + .build(); } @SafeVarargs diff --git a/src/grader/java/h06/utils/ReflectionUtils.java b/src/grader/java/h06/utils/ReflectionUtils.java new file mode 100644 index 0000000..72a6de5 --- /dev/null +++ b/src/grader/java/h06/utils/ReflectionUtils.java @@ -0,0 +1,113 @@ +package h06.utils; + +import org.opentest4j.AssertionFailedError; + +import java.lang.reflect.Field; +import java.lang.reflect.InaccessibleObjectException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ReflectionUtils { + + private static final Map, Map> FIELD_CACHE = new HashMap<>(); + private static final Map, Map> METHOD_CACHE = new HashMap<>(); + + private static final Function GET_METHOD_SIGNATURE = method -> "%s(%s)".formatted(method.getName(), + Arrays.stream(method.getParameterTypes()) + .map(Class::getName) + .collect(Collectors.joining(", ")) + ); + + public static Field getField(Class clazz, String fieldName) { + return FIELD_CACHE.computeIfAbsent(clazz, c -> new HashMap<>()) + .computeIfAbsent(fieldName, s -> { + try { + Field field = Stream.concat(Arrays.stream(clazz.getFields()), Arrays.stream(clazz.getDeclaredFields())) + .filter(f -> f.getName().equals(fieldName)) + .findAny() + .orElseThrow(() -> + new AssertionFailedError("Unable to locate field %s in class %s".formatted(fieldName, clazz.getName()))); + field.setAccessible(true); + return field; + } catch (InaccessibleObjectException e) { + throw new AssertionFailedError("Unable to access field %s in class %s".formatted(fieldName, clazz.getName()), e); + } + }); + } + + public static R getFieldValue(String fieldName, T instance) { + return getFieldValue(getField(instance.getClass(), fieldName), instance); + } + + @SuppressWarnings("unchecked") + public static R getFieldValue(Field field, T instance) { + try { + return (R) field.get(instance); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static void setFieldValue(String fieldName, T instance, V value) { + setFieldValue(getField(instance.getClass(), fieldName), instance, value); + } + + public static void setFieldValue(Field field, T instance, V value) { + try { + field.set(instance, value); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static Method getMethodByName(Class clazz, String methodName) { + Method[] matchingCachedMethods = METHOD_CACHE.computeIfAbsent(clazz, c -> new HashMap<>()) + .entrySet() + .stream() + .filter(entry -> entry.getKey().startsWith(methodName)) + .map(Map.Entry::getValue) + .toArray(Method[]::new); + + if (matchingCachedMethods.length == 0) { + Method[] methods = Stream.concat(Arrays.stream(clazz.getMethods()), Arrays.stream(clazz.getDeclaredMethods())) + .filter(method -> method.getName().equals(methodName)) + .toArray(Method[]::new); + + if (methods.length == 0) { + throw new AssertionFailedError("No method with name \"%s\" found in class %s".formatted(methodName, clazz.getName())); + } else if (methods.length > 1) { + throw new AssertionFailedError("Cannot resolve ambiguity: Found %d methods in class %s with name %s" + .formatted(methods.length, clazz.getName(), methodName)); + } else { + METHOD_CACHE.get(clazz).put(GET_METHOD_SIGNATURE.apply(methods[0]), methods[0]); + return methods[0]; + } + } else if (matchingCachedMethods.length > 1) { + throw new RuntimeException("Method name %s is ambiguous; found %d method with the same name".formatted(methodName, + matchingCachedMethods.length)); + } else { + return matchingCachedMethods[0]; + } + } + + public static Method getMethod(Class clazz, String methodName, Class... argumentTypes) { + String signature = "%s(%s)".formatted(methodName, + Arrays.stream(argumentTypes) + .map(Class::getName) + .collect(Collectors.joining(", ")) + ); + + return METHOD_CACHE.computeIfAbsent(clazz, c -> new HashMap<>()) + .computeIfAbsent(signature, s -> Stream.concat(Arrays.stream(clazz.getMethods()), + Arrays.stream(clazz.getDeclaredMethods())) + .filter(method -> method.getName().equals(methodName) && Arrays.equals(method.getParameterTypes(), argumentTypes)) + .findAny() + .orElseThrow(() -> new RuntimeException("Could not find any methods matching signature " + signature)) + ); + } +} diff --git a/src/grader/java/h06/utils/TutorAssertions.java b/src/grader/java/h06/utils/TutorAssertions.java new file mode 100644 index 0000000..6090f5a --- /dev/null +++ b/src/grader/java/h06/utils/TutorAssertions.java @@ -0,0 +1,42 @@ +package h06.utils; + +import kotlin.Pair; +import org.jetbrains.annotations.Nullable; +import org.opentest4j.AssertionFailedError; + +import java.util.*; +import java.util.function.Supplier; + +public class TutorAssertions { + + public static void assertEquals(Object expected, @Nullable String expectedDesc, + Object actual, @Nullable String actualDesc, + String message, + Supplier>> inputsSupplier) { + if (!Objects.equals(expected, actual)) { + throw new AssertionFailedError( + getExceptionMessage(expected, expectedDesc, actual, actualDesc, message, inputsSupplier) + ); + } + } + + private static String getExceptionMessage(Object expected, @Nullable String expectedDesc, + Object actual, @Nullable String actualDesc, + String messageHead, + Supplier>> inputsSupplier) { + String exceptionMessageHead = "%s ==> expected: <%s> but was: <%s>".formatted( + messageHead, + expectedDesc == null ? Objects.toString(expected) : expectedDesc, + actualDesc == null ? Objects.toString(actual) : actualDesc + ); + StringBuilder builder = new StringBuilder(exceptionMessageHead).append("\n"); + List> inputsList = inputsSupplier.get(); + + if (inputsList.size() > 0) { + builder.append("Inputs:\n"); + inputsList.forEach(pair -> builder.append("%s: %s".formatted(pair.getFirst(), pair.getSecond())).append("\n")); + } + + return builder.toString(); + } +} diff --git a/src/grader/resources/grader-info.json b/src/grader/resources/grader-info.json index 82ca62b..b920bc0 100644 --- a/src/grader/resources/grader-info.json +++ b/src/grader/resources/grader-info.json @@ -7,13 +7,31 @@ { "name": "grader", "files": [ + "h06/utils/ReflectionUtils.java", + "h06/utils/TutorAssertions.java", + "h06/Config.java", "h06/H06_RubricProvider.java" ] }, { "name": "solution", "files": [ - "h06/Main.java" + "h06/BinaryFct2Int.java", + "h06/DoubleHashing.java", + "h06/Fct2Int.java", + "h06/Hash2IndexFct.java", + "h06/KeyValuePair.java", + "h06/LinearProbing.java", + "h06/Main.java", + "h06/ModuloUtil.java", + "h06/MyDate.java", + "h06/MyIndexHoppingHashMap.java", + "h06/MyListsHashMap.java", + "h06/MyMap.java", + "h06/RuntimeTest.java", + "h06/TestSet.java", + + "h06/ExampleJUnitTest.java" ] } ] From 8493b2bd54435ef8d38e00f693ea18219d6d8347 Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Wed, 22 Jun 2022 23:35:31 +0200 Subject: [PATCH 04/12] Add tests for H1 --- src/grader/java/h06/H06_RubricProvider.java | 34 +++++++ .../java/h06/h1/DoubleHashingTests.java | 88 +++++++++++++++++ .../java/h06/h1/Hash2IndexFctTests.java | 88 +++++++++++++++++ .../java/h06/h1/LinearProbingTests.java | 98 +++++++++++++++++++ .../Hash2IndexFctTransformer.java | 71 ++++++++++++++ src/grader/resources/grader-info.json | 4 + 6 files changed, 383 insertions(+) create mode 100644 src/grader/java/h06/h1/DoubleHashingTests.java create mode 100644 src/grader/java/h06/h1/Hash2IndexFctTests.java create mode 100644 src/grader/java/h06/h1/LinearProbingTests.java create mode 100644 src/grader/java/h06/transformers/Hash2IndexFctTransformer.java diff --git a/src/grader/java/h06/H06_RubricProvider.java b/src/grader/java/h06/H06_RubricProvider.java index 7655801..31a204d 100644 --- a/src/grader/java/h06/H06_RubricProvider.java +++ b/src/grader/java/h06/H06_RubricProvider.java @@ -1,6 +1,11 @@ package h06; +import h06.h1.DoubleHashingTests; +import h06.h1.Hash2IndexFctTests; +import h06.h1.LinearProbingTests; +import h06.transformers.Hash2IndexFctTransformer; import org.sourcegrade.jagr.api.rubric.*; +import org.sourcegrade.jagr.api.testing.RubricConfiguration; import java.lang.reflect.Method; import java.util.Arrays; @@ -13,6 +18,30 @@ public class H06_RubricProvider implements RubricProvider { public Rubric getRubric() { return Rubric.builder() .title("H6 | Hashtabellen") + .addChildCriteria( + makeCriterionFromChildCriteria("H1 | Basisdatenstrukturen", + makeCriterion("Methode [[[apply(T)]]] von Klasse [[[Hash2IndexFct]]] funktioniert wie beschrieben.", + () -> Hash2IndexFctTests.class + .getDeclaredMethod("testApply", int.class, int.class) + ), + makeCriterion("Methode [[[apply(T)]]] von Klasse [[[LinearProbing]]] funktioniert wie beschrieben.", + () -> LinearProbingTests.class + .getDeclaredMethod("testApply", int.class, int.class) + ), + makeCriterion("Methode [[[apply(T)]]] von Klasse [[[DoubleHashing]]] funktioniert wie beschrieben.", + () -> DoubleHashingTests.class + .getDeclaredMethod("testApply", Hash2IndexFct.class, int.class) + ), + makeCriterion("Alle drei Methoden vermeiden arithmetischen Überlauf.", 0, 2, + () -> Hash2IndexFctTests.class + .getDeclaredMethod("testApplyOverflow", int.class, int.class), + () -> LinearProbingTests.class + .getDeclaredMethod("testApplyOverflow", int.class, int.class), + () -> DoubleHashingTests.class + .getDeclaredMethod("testApplyOverflow", Hash2IndexFct.class, int.class) + ) + ) + ) .build(); } @@ -21,6 +50,11 @@ private static Criterion makeCriterion(String shortDescription, Callable return makeCriterion(shortDescription, 0, 1, callables); } + @Override + public void configure(RubricConfiguration configuration) { + configuration.addTransformer(new Hash2IndexFctTransformer()); + } + @SafeVarargs private static Criterion makeCriterion(String shortDescription, int minPoints, int maxPoints, Callable... callables) { return Criterion.builder() diff --git a/src/grader/java/h06/h1/DoubleHashingTests.java b/src/grader/java/h06/h1/DoubleHashingTests.java new file mode 100644 index 0000000..951a514 --- /dev/null +++ b/src/grader/java/h06/h1/DoubleHashingTests.java @@ -0,0 +1,88 @@ +package h06.h1; + +import h06.DoubleHashing; +import h06.Hash2IndexFct; +import h06.utils.TutorAssertions; +import kotlin.Pair; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.mockito.Answers; +import org.mockito.Mockito; +import org.sourcegrade.jagr.api.rubric.TestForSubmission; + +import java.util.List; +import java.util.Random; +import java.util.stream.Stream; + +import static h06.Config.SEED; +import static h06.Config.STREAM_SIZE; + +@TestForSubmission("h06") +public class DoubleHashingTests { + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testApply(Hash2IndexFct fct, int factor) { + DoubleHashing instance = new DoubleHashing<>(fct, fct); + Object object = new Object() { + @Override + public int hashCode() { + return 42; + } + }; + + int a = fct.apply(object); + int b = fct.apply(object); + b = (b % 2 == 0 ? b + 1 : b); + + TutorAssertions.assertEquals( + Math.floorMod(a + factor * b, fct.getTableSize()), null, + instance.apply(object, factor), null, + "Value returned by [[[apply(T, int)]]] in class [[[DoubleHashing]]]] did not equal expected value", () -> List.of( + new Pair<>("[[[fct]]]", "[[[Hash2IndexFct]]] reference implementation; [[[tableSize]]] == %d, [[[offset]]] == 0" + .formatted(fct.getTableSize())), + new Pair<>("[[[this]]]", "[[[new DoubleHashing<>(fct, fct)]]]"), + new Pair<>("Argument #1 - [[[key]]]", + "[[[new Object()]]] ([[[hashCode()]]] always returns %d)".formatted(object.hashCode())), + new Pair<>("Argument #2 - [[[factor]]]", String.valueOf(factor)) + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testApplyOverflow(Hash2IndexFct fct, int factor) { + DoubleHashing instance = new DoubleHashing<>(fct, fct); + Object object = new Object(); + + int a = fct.apply(object); + int b = fct.apply(object); + b = (b % 2 == 0 ? b + 1 : b); + + TutorAssertions.assertEquals( + Math.floorMod(a + (long) Integer.MAX_VALUE * b, fct.getTableSize()), null, + instance.apply(object, Integer.MAX_VALUE), null, + "Value returned by [[[apply(T, int)]]] in class [[[DoubleHashing]]]] did not equal expected value", () -> List.of( + new Pair<>("[[[fct]]]", "[[[Hash2IndexFct]]] reference implementation; [[[tableSize]]] == %d, [[[offset]]] == 0" + .formatted(fct.getTableSize())), + new Pair<>("[[[this]]]", "[[[new DoubleHashing<>(fct, fct)]]]"), + new Pair<>("Argument #1 - [[[key]]]", + "[[[new Object()]]] ([[[hashCode()]]] always returns %d)".formatted(object.hashCode())), + new Pair<>("Argument #2 - [[[factor]]]", "[[[Integer.MAX_VALUE]]]") + )); + } + + private static class Provider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) { + Random random = new Random(SEED); + + return Stream.generate(() -> new Object[] {new Hash2IndexFct<>(random.nextInt(10) + 1, 0), random.nextInt(10) + 1}) + .limit(STREAM_SIZE) + .map(Arguments::of); + } + } +} diff --git a/src/grader/java/h06/h1/Hash2IndexFctTests.java b/src/grader/java/h06/h1/Hash2IndexFctTests.java new file mode 100644 index 0000000..417d63a --- /dev/null +++ b/src/grader/java/h06/h1/Hash2IndexFctTests.java @@ -0,0 +1,88 @@ +package h06.h1; + +import h06.Hash2IndexFct; +import h06.transformers.Hash2IndexFctTransformer; +import h06.utils.TutorAssertions; +import kotlin.Pair; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.sourcegrade.jagr.api.rubric.TestForSubmission; + +import java.util.List; +import java.util.Random; +import java.util.stream.Stream; + +import static h06.Config.SEED; +import static h06.Config.STREAM_SIZE; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@TestForSubmission("h06") +public class Hash2IndexFctTests { + + @BeforeAll + public static void deactivateSurrogate() { + Hash2IndexFctTransformer.SURROGATE_ACTIVE = false; + } + + @AfterAll + public static void reactivateSurrogate() { + Hash2IndexFctTransformer.SURROGATE_ACTIVE = true; + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testApply(int tableSize, int offset) { + Hash2IndexFct instance = new Hash2IndexFct<>(tableSize, offset); + Object object = new Object() { + @Override + public int hashCode() { + return 42; + } + }; + + TutorAssertions.assertEquals( + Math.floorMod(object.hashCode() + offset, instance.getTableSize()), null, + instance.apply(object), null, + "Value returned by [[[apply(T)]]] in class [[[Hash2IndexFct]]] does not equal excepted value", () -> List.of( + new Pair<>("[[[this]]]", "[[[new Hash2IndexFct<>(%d, %d)]]]".formatted(tableSize, offset)), + new Pair<>("Argument #1 - [[[key]]]", "[[[new Object()]]] ([[[hashCode()]]] always returns 42)") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testApplyOverflow(int tableSize, int offset) { + Hash2IndexFct instance = new Hash2IndexFct<>(tableSize, offset); + Object object = new Object() { + @Override + public int hashCode() { + return Integer.MAX_VALUE; + } + }; + + TutorAssertions.assertEquals( + Math.floorMod((long) object.hashCode() + offset, instance.getTableSize()), null, + instance.apply(object), null, + "Value returned by [[[apply(T)]]] in class [[[Hash2IndexFct]]] does not equal expected value", () -> List.of( + new Pair<>("[[[this]]]", "[[[new Hash2IndexFct<>(%d, %d)]]]".formatted(tableSize, offset)), + new Pair<>("Argument #1 - [[[key]]]", "[[[new Object()]]] ([[[hashCode()]]] always returns Integer.MAX_VALUE)") + )); + } + + private static class Provider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) { + Random random = new Random(SEED); + + return Stream.generate(() -> new Integer[] {random.nextInt(10) + 1, random.nextInt(10) + 1}) + .limit(STREAM_SIZE) + .map(Arguments::of); + } + } +} diff --git a/src/grader/java/h06/h1/LinearProbingTests.java b/src/grader/java/h06/h1/LinearProbingTests.java new file mode 100644 index 0000000..34a32eb --- /dev/null +++ b/src/grader/java/h06/h1/LinearProbingTests.java @@ -0,0 +1,98 @@ +package h06.h1; + +import h06.Fct2Int; +import h06.LinearProbing; +import h06.utils.TutorAssertions; +import kotlin.Pair; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.sourcegrade.jagr.api.rubric.TestForSubmission; + +import java.util.List; +import java.util.Random; +import java.util.stream.Stream; + +import static h06.Config.SEED; +import static h06.Config.STREAM_SIZE; + +@TestForSubmission("h06") +public class LinearProbingTests { + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testApply(int i, int tableSize) { + Fct2Int fct = new Fct2IntImpl(tableSize, 0); + LinearProbing instance = new LinearProbing<>(fct); + Object object = new Object(); + + TutorAssertions.assertEquals( + Math.floorMod(fct.apply(object) + i, fct.getTableSize()), null, + instance.apply(object, i), null, + "Value returned by [[[apply(T, int)]]] in class [[[LinearProbing]]] did not equal expected value", () -> List.of( + new Pair<>("[[[this]]]", + "[[[new LinearProbing<>(fct)]]]; [[[fct.getTableSize()]]] == %d, [[[fct.apply(T)]]] always returns %d" + .formatted(fct.getTableSize(), fct.apply(null))), + new Pair<>("Argument #1 - [[[key]]]", "[[[new Object()]]]"), + new Pair<>("Argument #2 - [[[offset]]]", String.valueOf(i)) + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testApplyOverflow(int i, int tableSize) { + Fct2Int fct = new Fct2IntImpl(tableSize, Integer.MAX_VALUE); + LinearProbing instance = new LinearProbing<>(fct); + Object object = new Object(); + + TutorAssertions.assertEquals( + Math.floorMod((long) fct.apply(object) + i, fct.getTableSize()), null, + instance.apply(object, i), null, + "Value returned by [[[apply(T, int)]]] in class [[[LinearProbing]]] did not equal expected value", () -> List.of( + new Pair<>("[[[this]]]", ("[[[new LinearProbing<>(fct)]]]; [[[fct.getTableSize()]]] == %d, " + + "[[[fct.apply(T)]]] always returns Integer.MAX_VALUE").formatted(fct.getTableSize())), + new Pair<>("Argument #1 - [[[key]]]", "[[[new Object()]]]"), + new Pair<>("Argument #2 - [[[offset]]]", String.valueOf(i)) + )); + } + + private static class Provider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) { + Random random = new Random(SEED); + + return Stream.generate(() -> new Object[] {random.nextInt(10) + 1, random.nextInt(10) + 1}) + .limit(STREAM_SIZE) + .map(Arguments::of); + } + } + + private static class Fct2IntImpl implements Fct2Int { + + private int tableSize; + private final int applyReturnValue; + + private Fct2IntImpl(int tableSize, int applyReturnValue) { + this.tableSize = tableSize; + this.applyReturnValue = applyReturnValue; + } + + @Override + public int apply(Object x) { + return this.applyReturnValue; + } + + @Override + public int getTableSize() { + return this.tableSize; + } + + @Override + public void setTableSize(int tableSize) { + this.tableSize = tableSize; + } + } +} diff --git a/src/grader/java/h06/transformers/Hash2IndexFctTransformer.java b/src/grader/java/h06/transformers/Hash2IndexFctTransformer.java new file mode 100644 index 0000000..d6ef51a --- /dev/null +++ b/src/grader/java/h06/transformers/Hash2IndexFctTransformer.java @@ -0,0 +1,71 @@ +package h06.transformers; + +import org.objectweb.asm.*; +import org.sourcegrade.jagr.api.testing.ClassTransformer; + +public class Hash2IndexFctTransformer implements ClassTransformer { + + public static boolean SURROGATE_ACTIVE = true; + + @Override + public String getName() { + return "Hash2IndexFctTransformer"; + } + + @Override + public void transform(ClassReader reader, ClassWriter writer) { + if (reader.getClassName().equals("h06/Hash2IndexFct")) { + reader.accept(new ClassTransformer(writer), ClassReader.SKIP_DEBUG); + } else { + reader.accept(writer, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + } + } + + private static class ClassTransformer extends ClassVisitor { + + private ClassTransformer(ClassWriter classWriter) { + super(Opcodes.ASM9, classWriter); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + if (name.equals("apply") && descriptor.equals("(Ljava/lang/Object;)I")) { + return new MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) { + final Label label = new Label(); + + @Override + public void visitCode() { + super.visitFieldInsn(Opcodes.GETSTATIC, + "h06/transformers/Hash2IndexFctTransformer", + "SURROGATE_ACTIVE", + "Z"); + super.visitJumpInsn(Opcodes.IFNE, label); + super.visitCode(); + } + + @Override + public void visitEnd() { + super.visitLabel(label); + super.visitFrame(Opcodes.F_FULL, 2, new Object[] {"h06/Hash2IndexFct", "java/lang/Object"}, 0, + new Object[0]); + super.visitFieldInsn(Opcodes.GETSTATIC, + "java/lang/System", + "out", + "Ljava/io/PrintStream;"); + super.visitLdcInsn("Hello world!"); + super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/io/PrintStream", + "println", + "(Ljava/lang/String;)V", + false); + super.visitInsn(Opcodes.ICONST_0); + super.visitInsn(Opcodes.IRETURN); + super.visitEnd(); + } + }; + } else { + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + } + } +} diff --git a/src/grader/resources/grader-info.json b/src/grader/resources/grader-info.json index b920bc0..58adb4b 100644 --- a/src/grader/resources/grader-info.json +++ b/src/grader/resources/grader-info.json @@ -7,6 +7,10 @@ { "name": "grader", "files": [ + "h06/h1/DoubleHashingTests.java", + "h06/h1/Hash2IndexFctTests.java", + "h06/h1/LinearProbingTests.java", + "h06/transformers/Hash2IndexFctTransformer.java", "h06/utils/ReflectionUtils.java", "h06/utils/TutorAssertions.java", "h06/Config.java", From 8e4637719d356ce584190d8bbcd64f6ef0d9e1d0 Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Thu, 23 Jun 2022 14:06:49 +0200 Subject: [PATCH 05/12] Replace bytecode transformations with Mockito mocks --- build.gradle.kts | 1 + src/grader/java/h06/H06_RubricProvider.java | 7 --- .../java/h06/h1/DoubleHashingTests.java | 6 +-- .../java/h06/h1/Hash2IndexFctTests.java | 1 - src/grader/java/h06/mocks/Mock.java | 49 +++++++++++++++++++ src/grader/resources/grader-info.json | 1 + 6 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 src/grader/java/h06/mocks/Mock.java diff --git a/build.gradle.kts b/build.gradle.kts index b2e14b9..24bf12c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { exclude("org.jetbrains", "annotations") } testImplementation("org.junit.jupiter:junit-jupiter:5.8.2") + "graderImplementation"("org.mockito:mockito-core:4.5.1") } application { diff --git a/src/grader/java/h06/H06_RubricProvider.java b/src/grader/java/h06/H06_RubricProvider.java index 31a204d..675712c 100644 --- a/src/grader/java/h06/H06_RubricProvider.java +++ b/src/grader/java/h06/H06_RubricProvider.java @@ -3,9 +3,7 @@ import h06.h1.DoubleHashingTests; import h06.h1.Hash2IndexFctTests; import h06.h1.LinearProbingTests; -import h06.transformers.Hash2IndexFctTransformer; import org.sourcegrade.jagr.api.rubric.*; -import org.sourcegrade.jagr.api.testing.RubricConfiguration; import java.lang.reflect.Method; import java.util.Arrays; @@ -50,11 +48,6 @@ private static Criterion makeCriterion(String shortDescription, Callable return makeCriterion(shortDescription, 0, 1, callables); } - @Override - public void configure(RubricConfiguration configuration) { - configuration.addTransformer(new Hash2IndexFctTransformer()); - } - @SafeVarargs private static Criterion makeCriterion(String shortDescription, int minPoints, int maxPoints, Callable... callables) { return Criterion.builder() diff --git a/src/grader/java/h06/h1/DoubleHashingTests.java b/src/grader/java/h06/h1/DoubleHashingTests.java index 951a514..fd386dd 100644 --- a/src/grader/java/h06/h1/DoubleHashingTests.java +++ b/src/grader/java/h06/h1/DoubleHashingTests.java @@ -2,6 +2,7 @@ import h06.DoubleHashing; import h06.Hash2IndexFct; +import h06.mocks.Mock; import h06.utils.TutorAssertions; import kotlin.Pair; import org.junit.jupiter.api.extension.ExtensionContext; @@ -9,8 +10,6 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; -import org.mockito.Answers; -import org.mockito.Mockito; import org.sourcegrade.jagr.api.rubric.TestForSubmission; import java.util.List; @@ -80,7 +79,8 @@ private static class Provider implements ArgumentsProvider { public Stream provideArguments(ExtensionContext context) { Random random = new Random(SEED); - return Stream.generate(() -> new Object[] {new Hash2IndexFct<>(random.nextInt(10) + 1, 0), random.nextInt(10) + 1}) + return Stream.generate(() -> new Object[] {Mock.getMock(Hash2IndexFct.class, Mock.HASH_2_INDEX_FCT, + random.nextInt(10) + 1, 0), random.nextInt(10) + 1}) .limit(STREAM_SIZE) .map(Arguments::of); } diff --git a/src/grader/java/h06/h1/Hash2IndexFctTests.java b/src/grader/java/h06/h1/Hash2IndexFctTests.java index 417d63a..5e47255 100644 --- a/src/grader/java/h06/h1/Hash2IndexFctTests.java +++ b/src/grader/java/h06/h1/Hash2IndexFctTests.java @@ -19,7 +19,6 @@ import static h06.Config.SEED; import static h06.Config.STREAM_SIZE; -import static org.junit.jupiter.api.Assertions.assertEquals; @TestForSubmission("h06") public class Hash2IndexFctTests { diff --git a/src/grader/java/h06/mocks/Mock.java b/src/grader/java/h06/mocks/Mock.java new file mode 100644 index 0000000..45f1e0c --- /dev/null +++ b/src/grader/java/h06/mocks/Mock.java @@ -0,0 +1,49 @@ +package h06.mocks; + +import h06.DoubleHashing; +import h06.Hash2IndexFct; +import h06.LinearProbing; +import h06.utils.ReflectionUtils; +import org.mockito.Answers; +import org.mockito.Mockito; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.function.BiConsumer; + +@SuppressWarnings("rawtypes") +public class Mock { + + public static final BiConsumer HASH_2_INDEX_FCT = (instance, args) -> { + Field tableSizeField = ReflectionUtils.getField(Hash2IndexFct.class, "tableSize"); + int tableSize = (int) args[0]; + ReflectionUtils.setFieldValue(tableSizeField, instance, tableSize); + + Field offsetField = ReflectionUtils.getField(Hash2IndexFct.class, "offset"); + int offset = (int) args[1]; + ReflectionUtils.setFieldValue(offsetField, instance, offset); + + Mockito.doAnswer(invocation -> { + Method calledMethod = invocation.getMethod(); + if (calledMethod.getName().equals("apply") + && calledMethod.getParameterCount() == 1 + && calledMethod.getParameterTypes()[0].equals(Object.class)) { + return Math.floorMod(Math.abs((long) invocation.getArgument(0).hashCode()) + offset, tableSize); + } else { + return invocation.callRealMethod(); + } + }); + }; + public static final BiConsumer LINEAR_PROBING = (instance, args) -> { + + }; + public static final BiConsumer DOUBLE_HASHING = (instance, args) -> { + + }; + + public static T getMock(Class clazz, BiConsumer constructorSurrogate, Object... constructorArgs) { + T t = Mockito.mock(clazz, Answers.CALLS_REAL_METHODS); + constructorSurrogate.accept(t, constructorArgs); + return t; + } +} diff --git a/src/grader/resources/grader-info.json b/src/grader/resources/grader-info.json index 58adb4b..a9842c4 100644 --- a/src/grader/resources/grader-info.json +++ b/src/grader/resources/grader-info.json @@ -10,6 +10,7 @@ "h06/h1/DoubleHashingTests.java", "h06/h1/Hash2IndexFctTests.java", "h06/h1/LinearProbingTests.java", + "h06/mocks/Mock.java", "h06/transformers/Hash2IndexFctTransformer.java", "h06/utils/ReflectionUtils.java", "h06/utils/TutorAssertions.java", From b156dbaf3537deccee70d13f99bab3b20221515b Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Fri, 24 Jun 2022 21:58:40 +0200 Subject: [PATCH 06/12] Add tests for H3 --- src/grader/java/h06/Config.java | 1 + src/grader/java/h06/H06_RubricProvider.java | 49 +++ .../h06/h3/MyIndexHoppingHashMapTests.java | 394 ++++++++++++++++++ .../MyIndexHoppingHashMapTransformer.java | 58 +++ src/grader/resources/grader-info.json | 2 + 5 files changed, 504 insertions(+) create mode 100644 src/grader/java/h06/h3/MyIndexHoppingHashMapTests.java create mode 100644 src/grader/java/h06/transformers/MyIndexHoppingHashMapTransformer.java diff --git a/src/grader/java/h06/Config.java b/src/grader/java/h06/Config.java index fc342b4..bde0c4d 100644 --- a/src/grader/java/h06/Config.java +++ b/src/grader/java/h06/Config.java @@ -4,6 +4,7 @@ public final class Config { private Config() {} + public static final String ASSIGNMENT_ID = "h06"; public static final long SEED = 0L; public static final int STREAM_SIZE = 5; diff --git a/src/grader/java/h06/H06_RubricProvider.java b/src/grader/java/h06/H06_RubricProvider.java index 675712c..089fb51 100644 --- a/src/grader/java/h06/H06_RubricProvider.java +++ b/src/grader/java/h06/H06_RubricProvider.java @@ -3,7 +3,10 @@ import h06.h1.DoubleHashingTests; import h06.h1.Hash2IndexFctTests; import h06.h1.LinearProbingTests; +import h06.h3.MyIndexHoppingHashMapTests; +import h06.transformers.MyIndexHoppingHashMapTransformer; import org.sourcegrade.jagr.api.rubric.*; +import org.sourcegrade.jagr.api.testing.RubricConfiguration; import java.lang.reflect.Method; import java.util.Arrays; @@ -38,11 +41,57 @@ public Rubric getRubric() { () -> DoubleHashingTests.class .getDeclaredMethod("testApplyOverflow", Hash2IndexFct.class, int.class) ) + ), + makeCriterionFromChildCriteria("H3 | Mehrfachsondierung", + makeCriterion("[[[containsKey(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert in den internen Arrays vorhanden sind.", + () -> MyIndexHoppingHashMapTests.class + .getDeclaredMethod("testContainsKey", int.class, int.class, double.class, double.class)), + makeCriterion("[[[containsKey(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert nicht in den internen Arrays vorhanden sind.", + () -> MyIndexHoppingHashMapTests.class + .getDeclaredMethod("testContainsKeyForeign", int.class, int.class, double.class, double.class)), + makeCriterion("[[[getValue(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert in den internen Arrays vorhanden sind.", + () -> MyIndexHoppingHashMapTests.class + .getDeclaredMethod("testGetValue", int.class, int.class, double.class, double.class)), + makeCriterion("[[[getValue(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert nicht in den internen Arrays vorhanden sind.", + () -> MyIndexHoppingHashMapTests.class + .getDeclaredMethod("testGetValueForeign", int.class, int.class, double.class, double.class)), + makeCriterion("[[[put(K, V)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert nicht bereits in den internen Arrays vorhanden sind.", + () -> MyIndexHoppingHashMapTests.class + .getDeclaredMethod("testPut", int.class, int.class, double.class, double.class)), + makeCriterion("[[[put(K, V)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert bereits in den internen Arrays vorhanden sind.", + () -> MyIndexHoppingHashMapTests.class + .getDeclaredMethod("testPutDuplicate", int.class, int.class, double.class, double.class)), + makeCriterion("[[[remove(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert in den internen Arrays vorhanden sind.", + () -> MyIndexHoppingHashMapTests.class + .getDeclaredMethod("testRemove", int.class, int.class, double.class, double.class)), + makeCriterion("[[[remove(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert nicht in den internen Arrays vorhanden sind.", + () -> MyIndexHoppingHashMapTests.class + .getDeclaredMethod("testRemoveForeign", int.class, int.class, double.class, double.class)), + makeCriterion("[[[rehash()]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben.", + () -> MyIndexHoppingHashMapTests.class + .getDeclaredMethod("testRehash", int.class, int.class, double.class, double.class)), + makeCriterion("In [[[put(K, V)]]] in Klasse [[[MyIndexHoppingHashMap]]] wird [[[rehash()]]] aufgerufen, " + + "wenn der Schwellwert überschritten wurde.", + () -> MyIndexHoppingHashMapTests.class + .getDeclaredMethod("testRehashInPut", int.class, int.class, double.class, double.class)) ) ) .build(); } + @Override + public void configure(RubricConfiguration configuration) { + configuration.addTransformer(new MyIndexHoppingHashMapTransformer()); + } + @SafeVarargs private static Criterion makeCriterion(String shortDescription, Callable... callables) { return makeCriterion(shortDescription, 0, 1, callables); diff --git a/src/grader/java/h06/h3/MyIndexHoppingHashMapTests.java b/src/grader/java/h06/h3/MyIndexHoppingHashMapTests.java new file mode 100644 index 0000000..3988e0e --- /dev/null +++ b/src/grader/java/h06/h3/MyIndexHoppingHashMapTests.java @@ -0,0 +1,394 @@ +package h06.h3; + +import h06.MyIndexHoppingHashMap; +import h06.utils.ReflectionUtils; +import h06.utils.TutorAssertions; +import kotlin.Pair; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.sourcegrade.jagr.api.rubric.TestForSubmission; +import org.sourcegrade.jagr.api.testing.extension.JagrExecutionCondition; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Random; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static h06.Config.SEED; +import static h06.Config.STREAM_SIZE; + +@TestForSubmission("h06") +public class MyIndexHoppingHashMapTests { + + public static boolean REHASH_CALLED = false; + + @BeforeEach + public void reset() { + REHASH_CALLED = false; + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testContainsKey(int tableSize, int index, double resizeFactor, double resizeThreshold) { + BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); + MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, + binaryFct2Int); + Object key = new Object(); + Object value = new Object(); + ((Object[]) ReflectionUtils.getFieldValue("theKeys", instance))[index] = key; + ((Object[]) ReflectionUtils.getFieldValue("theValues", instance))[index] = value; + ((boolean[]) ReflectionUtils.getFieldValue("occupiedSinceLastRehash", instance))[index] = true; + ReflectionUtils.setFieldValue("occupiedCount", instance, 1); + + TutorAssertions.assertEquals( + true, null, + instance.containsKey(key), null, + "[[[containsKey(K)]]] did not return [[[true]]], even though the key is in [[[theKeys]]] at the correct position", + () -> List.of( + new Pair<>("[[[binaryFct2Int]]]", + "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( + tableSize, resizeFactor, resizeThreshold) + + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " + + "to simulate insertion operations") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testContainsKeyForeign(int tableSize, int index, double resizeFactor, double resizeThreshold) { + BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); + MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, + binaryFct2Int); + + TutorAssertions.assertEquals( + false, null, + instance.containsKey(new Object()), null, + "[[[containsKey(K)]]] did not return [[[false]]], even though the key is not in [[[theKeys]]]", () -> List.of( + new Pair<>("[[[binaryFct2Int]]]", + "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( + tableSize, resizeFactor, resizeThreshold) + + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " + + "to simulate insertion operations") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testGetValue(int tableSize, int index, double resizeFactor, double resizeThreshold) { + BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); + MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, + binaryFct2Int); + Object key = new Object(); + Object value = new Object(); + ((Object[]) ReflectionUtils.getFieldValue("theKeys", instance))[index] = key; + ((Object[]) ReflectionUtils.getFieldValue("theValues", instance))[index] = value; + ((boolean[]) ReflectionUtils.getFieldValue("occupiedSinceLastRehash", instance))[index] = true; + ReflectionUtils.setFieldValue("occupiedCount", instance, 1); + + TutorAssertions.assertEquals( + value, null, + instance.getValue(key), null, + "[[[getValue(K)]]] did not return the expected object, even though key and value are both at the correct position", + () -> List.of( + new Pair<>("[[[binaryFct2Int]]]", + "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( + tableSize, resizeFactor, resizeThreshold) + + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " + + "to simulate insertion operations") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testGetValueForeign(int tableSize, int index, double resizeFactor, double resizeThreshold) { + BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); + MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, + binaryFct2Int); + + TutorAssertions.assertEquals( + null, null, + instance.getValue(new Object()), null, + "[[[getValue(K)]]] did not return [[[null]]], even though key and value are not in [[[theKeys]]] and [[[theValues]]]", + () -> List.of( + new Pair<>("[[[binaryFct2Int]]]", + "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( + tableSize, resizeFactor, resizeThreshold) + + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " + + "to simulate insertion operations") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testPut(int tableSize, int index, double resizeFactor, double resizeThreshold) { + Supplier>> inputsListSupplier = () -> List.of( + new Pair<>("[[[binaryFct2Int]]]", + "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]]".formatted( + tableSize, resizeFactor, resizeThreshold)) + ); + BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); + MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, + binaryFct2Int); + Object key = new Object(); + Object value = new Object(); + + TutorAssertions.assertEquals( + null, null, + instance.put(key, value), null, + "[[[put(K, V)]]] did not return [[[null]]], even though key and value are not in [[[theKeys]]] and [[[theValues]]]", + inputsListSupplier); + TutorAssertions.assertEquals( + key, null, + ((Object[]) ReflectionUtils.getFieldValue("theKeys", instance))[index], null, + "[[[put(K, V)]]] did not update [[[theKeys]]] at index " + index, inputsListSupplier); + TutorAssertions.assertEquals( + value, null, + ((Object[]) ReflectionUtils.getFieldValue("theValues", instance))[index], null, + "[[[put(K, V)]]] did not update [[[theValues]]] at index " + index, inputsListSupplier); + TutorAssertions.assertEquals( + true, null, + ((boolean[]) ReflectionUtils.getFieldValue("occupiedSinceLastRehash", instance))[index], null, + "[[[put(K, V)]]] did not update [[[occupiedSinceLastRehash]]] at index " + index, inputsListSupplier); + TutorAssertions.assertEquals( + 1, null, + ReflectionUtils.getFieldValue("occupiedCount", instance), null, + "[[[put(K, V)]]] did not update [[[occupiedCount]]]", inputsListSupplier); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testPutDuplicate(int tableSize, int index, double resizeFactor, double resizeThreshold) { + Supplier>> inputsListSupplier = () -> List.of( + new Pair<>("[[[binaryFct2Int]]]", + "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( + tableSize, resizeFactor, resizeThreshold) + + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " + + "to simulate insertion operations prior to calling [[[put(K, V)]]]") + ); + BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); + MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, + binaryFct2Int); + Object key = new Object(); + Object value = new Object(); + Object newValue = new Object(); + ((Object[]) ReflectionUtils.getFieldValue("theKeys", instance))[index] = key; + ((Object[]) ReflectionUtils.getFieldValue("theValues", instance))[index] = value; + ((boolean[]) ReflectionUtils.getFieldValue("occupiedSinceLastRehash", instance))[index] = true; + ReflectionUtils.setFieldValue("occupiedCount", instance, 1); + + TutorAssertions.assertEquals( + value, null, + instance.put(key, newValue), null, + "[[[put(K, V)]]] did not return the old value associated with [[[key]]], " + + "even though key and value are both at the correct position", inputsListSupplier); + TutorAssertions.assertEquals( + key, null, + ((Object[]) ReflectionUtils.getFieldValue("theKeys", instance))[index], null, + "[[[put(K, V)]]] updated [[[theKeys]]] at index %d, but it should not have".formatted(index), inputsListSupplier); + TutorAssertions.assertEquals( + newValue, null, + ((Object[]) ReflectionUtils.getFieldValue("theValues", instance))[index], null, + "[[[put(K, V)]]] did not update [[[theValues]]] at index " + index, inputsListSupplier); + TutorAssertions.assertEquals( + true, null, + ((boolean[]) ReflectionUtils.getFieldValue("occupiedSinceLastRehash", instance))[index], null, + "[[[put(K, V)]]] updated [[[occupiedSinceLastRehash]]] at index %d, but it should not have".formatted(index), + inputsListSupplier); + TutorAssertions.assertEquals( + 1, null, + ReflectionUtils.getFieldValue("occupiedCount", instance), null, + "[[[put(K, V)]]] updated [[[occupiedCount]]], but it should not have", inputsListSupplier); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testRemove(int tableSize, int index, double resizeFactor, double resizeThreshold) { + Supplier>> inputsListSupplier = () -> List.of( + new Pair<>("[[[binaryFct2Int]]]", + "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( + tableSize, resizeFactor, resizeThreshold) + + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " + + "to simulate insertion operations prior to calling [[[remove(K)]]]") + ); + BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); + MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, + binaryFct2Int); + Object key = new Object(); + Object value = new Object(); + ((Object[]) ReflectionUtils.getFieldValue("theKeys", instance))[index] = key; + ((Object[]) ReflectionUtils.getFieldValue("theValues", instance))[index] = value; + ((boolean[]) ReflectionUtils.getFieldValue("occupiedSinceLastRehash", instance))[index] = true; + ReflectionUtils.setFieldValue("occupiedCount", instance, 1); + + TutorAssertions.assertEquals( + value, null, + instance.remove(key), null, + "[[[remove(K)]]] did not return the value associated with [[[key]]], " + + "even though key and value are both at the correct position", inputsListSupplier); + TutorAssertions.assertEquals( + null, null, + ((Object[]) ReflectionUtils.getFieldValue("theKeys", instance))[index], null, + "[[[remove(K)]]] did not remove key from [[[theKeys]]] at index " + index, inputsListSupplier); + TutorAssertions.assertEquals( + null, null, + ((Object[]) ReflectionUtils.getFieldValue("theValues", instance))[index], null, + "[[[remove(K)]]] did not remove value from [[[theValues]]] at index " + index, inputsListSupplier); + TutorAssertions.assertEquals( + true, null, + ((boolean[]) ReflectionUtils.getFieldValue("occupiedSinceLastRehash", instance))[index], null, + "[[[remove(K)]]] updated [[[occupiedSinceLastRehash]]] at index %d, but it should not have".formatted(index), + inputsListSupplier); + TutorAssertions.assertEquals( + 1, null, + ReflectionUtils.getFieldValue("occupiedCount", instance), null, + "[[[remove(K)]]] updated [[[occupiedCount]]], but it should not have", inputsListSupplier); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testRemoveForeign(int tableSize, int index, double resizeFactor, double resizeThreshold) { + Supplier>> inputsListSupplier = () -> List.of( + new Pair<>("[[[binaryFct2Int]]]", + "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]]".formatted( + tableSize, resizeFactor, resizeThreshold)) + ); + BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); + MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, + binaryFct2Int); + int newLength = (int) (tableSize * resizeFactor); + + try { + Method rehash = MyIndexHoppingHashMap.class.getDeclaredMethod("rehash"); + rehash.setAccessible(true); + rehash.invoke(instance); + } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } + + TutorAssertions.assertEquals( + newLength, null, + ((Object[]) ReflectionUtils.getFieldValue("theKeys", instance)).length, null, + "[[[rehash()]]] did not resize array [[[theKeys]]]", inputsListSupplier); + TutorAssertions.assertEquals( + newLength, null, + ((Object[]) ReflectionUtils.getFieldValue("theValues", instance)).length, null, + "[[[rehash()]]] did not resize array [[[theValues]]]", inputsListSupplier); + TutorAssertions.assertEquals( + newLength, null, + ((boolean[]) ReflectionUtils.getFieldValue("occupiedSinceLastRehash", instance)).length, null, + "[[[rehash()]]] did not resize array [[[occupiedSinceLastRehash]]]", inputsListSupplier); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testRehash(int tableSize, int index, double resizeFactor, double resizeThreshold) { + BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); + MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, + binaryFct2Int); + Object key = new Object(); + + TutorAssertions.assertEquals( + null, null, + instance.remove(key), null, + "[[[remove(K)]]] did not return [[[null]]], even though key and value are not in [[[theKeys]]] and [[[theValues]]]", + () -> List.of( + new Pair<>("[[[binaryFct2Int]]]", + "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]]".formatted( + tableSize, resizeFactor, resizeThreshold) + + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " + + "to simulate insertion operations") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + @ExtendWith(JagrExecutionCondition.class) + public void testRehashInPut(int tableSize, int index, double resizeFactor, double resizeThreshold) { + BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); + Object[] theKeys = new Object[tableSize]; + Object[] theValues = new Object[tableSize]; + boolean[] occupiedSinceLastRehash = new boolean[tableSize]; + + for (int i = 0; i < tableSize * resizeThreshold; i++) { + theKeys[i] = new Object(); + theValues[i] = new Object(); + occupiedSinceLastRehash[i] = true; + } + + MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, + binaryFct2Int); + ReflectionUtils.setFieldValue("theKeys", instance, theKeys); + ReflectionUtils.setFieldValue("theValues", instance, theValues); + ReflectionUtils.setFieldValue("occupiedSinceLastRehash", instance, occupiedSinceLastRehash); + ReflectionUtils.setFieldValue("occupiedCount", instance, (int) (tableSize * resizeThreshold)); + + instance.put(new Object(), new Object()); + + TutorAssertions.assertEquals( + true, null, + REHASH_CALLED, null, + "[[[put(K, V)]]] did not call [[[rehash()]]], even though [[[occupiedCount]]] exceeds threshold", + () -> List.of( + new Pair<>("[[[binaryFct2Int]]]", + "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]]".formatted( + tableSize, resizeFactor, resizeThreshold) + + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " + + "to simulate insertion operations prior to calling [[[put(K, V)]]]") + )); + } + + private static class Provider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) { + Random random = new Random(SEED); + int tableSize = random.nextInt(5, 25); + + return Stream.generate(() -> new Object[] {tableSize, random.nextInt(tableSize), 2d, 0.5}) + .limit(STREAM_SIZE) + .map(Arguments::of); + } + } + + private static class BinaryFct2IntImpl implements h06.BinaryFct2Int { + private int tableSize; + private final int applyReturnValue; + + private BinaryFct2IntImpl(int tableSize, int applyReturnValue) { + this.tableSize = tableSize; + this.applyReturnValue = applyReturnValue; + } + + @Override + public int apply(Object x, int i) { + return (this.applyReturnValue + i) % tableSize; + } + + @Override + public int getTableSize() { + return this.tableSize; + } + + @Override + public void setTableSize(int tableSize) { + this.tableSize = tableSize; + } + } +} diff --git a/src/grader/java/h06/transformers/MyIndexHoppingHashMapTransformer.java b/src/grader/java/h06/transformers/MyIndexHoppingHashMapTransformer.java new file mode 100644 index 0000000..9218ee4 --- /dev/null +++ b/src/grader/java/h06/transformers/MyIndexHoppingHashMapTransformer.java @@ -0,0 +1,58 @@ +package h06.transformers; + +import org.objectweb.asm.*; +import org.sourcegrade.jagr.api.testing.ClassTransformer; + +import static h06.Config.ASSIGNMENT_ID; + +public class MyIndexHoppingHashMapTransformer implements ClassTransformer { + + @Override + public String getName() { + return "MyIndexHoppingHashMapTransformer"; + } + + @Override + public void transform(ClassReader reader, ClassWriter writer) { + if (reader.getClassName().equals(ASSIGNMENT_ID + "/MyIndexHoppingHashMap")) { + reader.accept(new ClassTransformer(writer), ClassReader.SKIP_DEBUG); + } else { + reader.accept(writer, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + } + } + + @Override + public int getWriterFlags() { + return ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES; + } + + private static class ClassTransformer extends ClassVisitor { + + public ClassTransformer(ClassWriter classWriter) { + super(Opcodes.ASM9, classWriter); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + if (name.equals("put") && descriptor.equals("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")) { + return new MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) { + @Override + public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { + if (owner.equals(ASSIGNMENT_ID + "/MyIndexHoppingHashMap") + && name.equals("rehash") + && descriptor.equals("()V")) { + super.visitInsn(Opcodes.ICONST_1); + super.visitFieldInsn(Opcodes.PUTSTATIC, + ASSIGNMENT_ID + "/h3/MyIndexHoppingHashMapTests", + "REHASH_CALLED", + "Z"); + } + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + } + }; + } else { + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + } + } +} diff --git a/src/grader/resources/grader-info.json b/src/grader/resources/grader-info.json index a9842c4..64834bc 100644 --- a/src/grader/resources/grader-info.json +++ b/src/grader/resources/grader-info.json @@ -10,8 +10,10 @@ "h06/h1/DoubleHashingTests.java", "h06/h1/Hash2IndexFctTests.java", "h06/h1/LinearProbingTests.java", + "h06/h3/MyIndexHoppingHashMapTests.java", "h06/mocks/Mock.java", "h06/transformers/Hash2IndexFctTransformer.java", + "h06/transformers/MyIndexHoppingHashMapTransformer.java", "h06/utils/ReflectionUtils.java", "h06/utils/TutorAssertions.java", "h06/Config.java", From eb5fc5c751f0d8447cd3d87316f03eae3654fe35 Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Sun, 26 Jun 2022 14:22:27 +0200 Subject: [PATCH 07/12] Add tests for H4 --- src/grader/java/h06/H06_RubricProvider.java | 35 +++ .../java/h06/h4/MyListsHashMapTests.java | 243 ++++++++++++++++++ src/grader/resources/grader-info.json | 1 + 3 files changed, 279 insertions(+) create mode 100644 src/grader/java/h06/h4/MyListsHashMapTests.java diff --git a/src/grader/java/h06/H06_RubricProvider.java b/src/grader/java/h06/H06_RubricProvider.java index 089fb51..6fe6005 100644 --- a/src/grader/java/h06/H06_RubricProvider.java +++ b/src/grader/java/h06/H06_RubricProvider.java @@ -4,6 +4,7 @@ import h06.h1.Hash2IndexFctTests; import h06.h1.LinearProbingTests; import h06.h3.MyIndexHoppingHashMapTests; +import h06.h4.MyListsHashMapTests; import h06.transformers.MyIndexHoppingHashMapTransformer; import org.sourcegrade.jagr.api.rubric.*; import org.sourcegrade.jagr.api.testing.RubricConfiguration; @@ -82,6 +83,40 @@ public Rubric getRubric() { + "wenn der Schwellwert überschritten wurde.", () -> MyIndexHoppingHashMapTests.class .getDeclaredMethod("testRehashInPut", int.class, int.class, double.class, double.class)) + ), + makeCriterionFromChildCriteria("H4 | Hashtabelle von Listen", + makeCriterion("[[[containsKey(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert in [[[table]]] vorhanden sind.", + () -> MyListsHashMapTests.class + .getDeclaredMethod("testContainsKey", MyListsHashMap.class, int.class)), + makeCriterion("[[[containsKey(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert nicht in [[[table]]] vorhanden sind.", + () -> MyListsHashMapTests.class + .getDeclaredMethod("testContainsKeyDisjoint", MyListsHashMap.class, int.class)), + makeCriterion("[[[getValue(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert in [[[table]]] vorhanden sind.", + () -> MyListsHashMapTests.class + .getDeclaredMethod("testGetValue", MyListsHashMap.class, int.class)), + makeCriterion("[[[getValue(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert nicht in [[[table]]] vorhanden sind.", + () -> MyListsHashMapTests.class + .getDeclaredMethod("testGetValueDisjoint", MyListsHashMap.class, int.class)), + makeCriterion("[[[put(K, V)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert nicht bereits in [[[table]]] vorhanden sind.", + () -> MyListsHashMapTests.class + .getDeclaredMethod("testPut", MyListsHashMap.class, int.class)), + makeCriterion("[[[put(K, V)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert bereits in [[[table]]] vorhanden sind.", + () -> MyListsHashMapTests.class + .getDeclaredMethod("testPutDuplicate", MyListsHashMap.class, int.class)), + makeCriterion("[[[remove(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert in [[[table]]] vorhanden sind.", + () -> MyListsHashMapTests.class + .getDeclaredMethod("testRemove", MyListsHashMap.class, int.class)), + makeCriterion("[[[remove(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " + + "wenn Schlüssel und zugeordneter Wert nicht in [[[table]]] vorhanden sind.", + () -> MyListsHashMapTests.class + .getDeclaredMethod("testRemoveDisjoint", MyListsHashMap.class, int.class)) ) ) .build(); diff --git a/src/grader/java/h06/h4/MyListsHashMapTests.java b/src/grader/java/h06/h4/MyListsHashMapTests.java new file mode 100644 index 0000000..233b571 --- /dev/null +++ b/src/grader/java/h06/h4/MyListsHashMapTests.java @@ -0,0 +1,243 @@ +package h06.h4; + +import h06.Fct2Int; +import h06.KeyValuePair; +import h06.MyListsHashMap; +import h06.utils.ReflectionUtils; +import h06.utils.TutorAssertions; +import kotlin.Pair; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.sourcegrade.jagr.api.rubric.TestForSubmission; + +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.stream.Stream; + +import static h06.Config.SEED; +import static h06.Config.STREAM_SIZE; + +@TestForSubmission("h06") +@SuppressWarnings("unchecked") +public class MyListsHashMapTests { + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testContainsKey(MyListsHashMap instance, int index) { + Object key = new Object(); + Object value = new Object(); + ((LinkedList>[]) ReflectionUtils.getFieldValue("table", instance))[index] + .addFirst(new KeyValuePair<>(key, value)); + + TutorAssertions.assertEquals( + true, null, + instance.containsKey(key), null, + "[[[containsKey(K)]]] did not return [[[true]]], even though the key-value-pair is at the correct position in " + + "[[[table]]]", () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]; [[[table]]] has been modified to simulate " + + "insertion operations") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testContainsKeyDisjoint(MyListsHashMap instance, int index) { + TutorAssertions.assertEquals( + false, null, + instance.containsKey(new Object()), null, + "[[[containsKey(K)]]] did not return [[[false]]], even though the given key is not in [[[table]]]", + () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testGetValue(MyListsHashMap instance, int index) { + Object key = new Object(); + Object value = new Object(); + ((LinkedList>[]) ReflectionUtils.getFieldValue("table", instance))[index] + .addFirst(new KeyValuePair<>(key, value)); + + TutorAssertions.assertEquals( + value, null, + instance.getValue(key), null, + "[[[getValue(K)]]] did not return the expected value, even though the key-value-pair is at the correct position in " + + "[[[table]]]", () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]; [[[table]]] has been modified to simulate " + + "insertion operations") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testGetValueDisjoint(MyListsHashMap instance, int index) { + TutorAssertions.assertEquals( + null, null, + instance.getValue(new Object()), null, + "[[[getValue(K)]]] did not return [[[null]]], even though the given key is not in [[[table]]]", + () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testPut(MyListsHashMap instance, int index) { + LinkedList>[] table = ReflectionUtils.getFieldValue("table", instance); + Object key = new Object(); + Object value = new Object(); + + TutorAssertions.assertEquals( + null, null, + instance.put(key, value), null, + "[[[put(K, V)]]] did not return [[[null]]], even though the key-value-pair does not exist in [[[table]]] yet", + () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]") + )); + for (int i = 0; i < table.length; i++) { + if (i == index) { + TutorAssertions.assertEquals( + 1, null, + table[i].size(), null, + "[[[put(K, V)]]] did not create a new [[[KeyValuePair]]] object at the correct index (%d)".formatted(index), + () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]") + )); + } else { + TutorAssertions.assertEquals( + 0, null, + table[i].size(), null, + "[[[put(K, V)]]] created a new [[[KeyValuePair]]] object at the wrong index (%d)".formatted(index), + () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]") + )); + } + } + TutorAssertions.assertEquals( + table[index].getFirst().getKey(), null, + key, null, + "[[[put(K, V)]]] did not create a new [[[KeyValuePair]]] object with the correct key", () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]") + )); + TutorAssertions.assertEquals( + table[index].getFirst().getValue(), null, + value, null, + "[[[put(K, V)]]] did not create a new [[[KeyValuePair]]] object with the correct value", () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testPutDuplicate(MyListsHashMap instance, int index) { + Object key = new Object(); + Object value = new Object(); + Object newValue = new Object(); + ((LinkedList>[]) ReflectionUtils.getFieldValue("table", instance))[index] + .addFirst(new KeyValuePair<>(key, value)); + + TutorAssertions.assertEquals( + value, null, + instance.put(key, newValue), null, + "[[[put(K, V)]]] did not return the old value that was associated with the given key", () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]; [[[table]]] has been modified to simulate " + + "insertion operations") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testRemove(MyListsHashMap instance, int index) { + LinkedList>[] table = ReflectionUtils.getFieldValue("table", instance); + Object key = new Object(); + Object value = new Object(); + ((LinkedList>[]) ReflectionUtils.getFieldValue("table", instance))[index] + .addFirst(new KeyValuePair<>(key, value)); + + TutorAssertions.assertEquals( + value, null, + instance.remove(key), null, + "[[[remove(K)]]] did not return the expected value, even though the key-value-pair is at the correct position in " + + "[[[table]]]", () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]; [[[table]]] has been modified to simulate " + + "insertion operations") + )); + TutorAssertions.assertEquals( + 0, null, + table[index].size(), null, + "[[[remove(K)]]] did not remove the [[[KeyValuePair]]] object from [[[table]]] at index " + index, () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]; [[[table]]] has been modified to simulate " + + "insertion operations") + )); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testRemoveDisjoint(MyListsHashMap instance, int index) { + TutorAssertions.assertEquals( + null, null, + instance.remove(new Object()), null, + "[[[remove(K)]]] did not return [[[null]]] for a given key that has no value associated with it", () -> List.of( + new Pair<>("[[[fct2Int]]]", "[[[Fct2Int]]] reference implementation; [[[apply(T)]]] always returns " + index), + new Pair<>("[[[this]]]", "[[[new MyListsHashMap<>(fct2Int)]]]") + )); + } + + private static class Provider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) { + Random random = new Random(SEED); + int tableSize = random.nextInt(5, 25); + + return Stream.generate(() -> { + int index = random.nextInt(tableSize); + return new Object[] {new MyListsHashMap<>(new Fct2IntImpl(tableSize, index)), index}; + }) + .limit(STREAM_SIZE) + .map(Arguments::of); + } + } + + private static class Fct2IntImpl implements Fct2Int { + private int tableSize; + private final int applyReturnValue; + + private Fct2IntImpl(int tableSize, int applyReturnValue) { + this.tableSize = tableSize; + this.applyReturnValue = applyReturnValue; + } + + @Override + public int apply(Object x) { + return applyReturnValue; + } + + @Override + public int getTableSize() { + return this.tableSize; + } + + @Override + public void setTableSize(int tableSize) { + this.tableSize = tableSize; + } + } +} diff --git a/src/grader/resources/grader-info.json b/src/grader/resources/grader-info.json index 64834bc..80bab8c 100644 --- a/src/grader/resources/grader-info.json +++ b/src/grader/resources/grader-info.json @@ -11,6 +11,7 @@ "h06/h1/Hash2IndexFctTests.java", "h06/h1/LinearProbingTests.java", "h06/h3/MyIndexHoppingHashMapTests.java", + "h06/h4/MyListsHashMapTests.java", "h06/mocks/Mock.java", "h06/transformers/Hash2IndexFctTransformer.java", "h06/transformers/MyIndexHoppingHashMapTransformer.java", From f1e3bc3a8e501c2fd5c7a96a612c4ac1767b364e Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Mon, 27 Jun 2022 15:27:43 +0200 Subject: [PATCH 08/12] Move class names to criterion description --- src/grader/java/h06/H06_RubricProvider.java | 73 ++++++++++----------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/src/grader/java/h06/H06_RubricProvider.java b/src/grader/java/h06/H06_RubricProvider.java index 6fe6005..81b9719 100644 --- a/src/grader/java/h06/H06_RubricProvider.java +++ b/src/grader/java/h06/H06_RubricProvider.java @@ -43,78 +43,77 @@ public Rubric getRubric() { .getDeclaredMethod("testApplyOverflow", Hash2IndexFct.class, int.class) ) ), - makeCriterionFromChildCriteria("H3 | Mehrfachsondierung", - makeCriterion("[[[containsKey(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert in den internen Arrays vorhanden sind.", + makeCriterionFromChildCriteria("H3 | Mehrfachsondierung ([[[MyIndexHoppingHashMap]]])", + makeCriterion("[[[containsKey(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert in den internen Arrays vorhanden sind.", () -> MyIndexHoppingHashMapTests.class .getDeclaredMethod("testContainsKey", int.class, int.class, double.class, double.class)), - makeCriterion("[[[containsKey(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert nicht in den internen Arrays vorhanden sind.", + makeCriterion("[[[containsKey(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert nicht in den internen Arrays vorhanden sind.", () -> MyIndexHoppingHashMapTests.class .getDeclaredMethod("testContainsKeyForeign", int.class, int.class, double.class, double.class)), - makeCriterion("[[[getValue(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert in den internen Arrays vorhanden sind.", + makeCriterion("[[[getValue(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert in den internen Arrays vorhanden sind.", () -> MyIndexHoppingHashMapTests.class .getDeclaredMethod("testGetValue", int.class, int.class, double.class, double.class)), - makeCriterion("[[[getValue(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert nicht in den internen Arrays vorhanden sind.", + makeCriterion("[[[getValue(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert nicht in den internen Arrays vorhanden sind.", () -> MyIndexHoppingHashMapTests.class .getDeclaredMethod("testGetValueForeign", int.class, int.class, double.class, double.class)), - makeCriterion("[[[put(K, V)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert nicht bereits in den internen Arrays vorhanden sind.", + makeCriterion("[[[put(K, V)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert nicht bereits in den internen Arrays vorhanden sind.", () -> MyIndexHoppingHashMapTests.class .getDeclaredMethod("testPut", int.class, int.class, double.class, double.class)), - makeCriterion("[[[put(K, V)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert bereits in den internen Arrays vorhanden sind.", + makeCriterion("[[[put(K, V)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert bereits in den internen Arrays vorhanden sind.", () -> MyIndexHoppingHashMapTests.class .getDeclaredMethod("testPutDuplicate", int.class, int.class, double.class, double.class)), - makeCriterion("[[[remove(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert in den internen Arrays vorhanden sind.", + makeCriterion("[[[remove(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert in den internen Arrays vorhanden sind.", () -> MyIndexHoppingHashMapTests.class .getDeclaredMethod("testRemove", int.class, int.class, double.class, double.class)), - makeCriterion("[[[remove(K)]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert nicht in den internen Arrays vorhanden sind.", + makeCriterion("[[[remove(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert nicht in den internen Arrays vorhanden sind.", () -> MyIndexHoppingHashMapTests.class .getDeclaredMethod("testRemoveForeign", int.class, int.class, double.class, double.class)), - makeCriterion("[[[rehash()]]] in Klasse [[[MyIndexHoppingHashMap]]] funktioniert wie beschrieben.", + makeCriterion("[[[rehash()]]] funktioniert wie beschrieben.", () -> MyIndexHoppingHashMapTests.class .getDeclaredMethod("testRehash", int.class, int.class, double.class, double.class)), - makeCriterion("In [[[put(K, V)]]] in Klasse [[[MyIndexHoppingHashMap]]] wird [[[rehash()]]] aufgerufen, " - + "wenn der Schwellwert überschritten wurde.", + makeCriterion("In [[[put(K, V)]]] wird [[[rehash()]]] aufgerufen, wenn der Schwellwert überschritten wurde.", () -> MyIndexHoppingHashMapTests.class .getDeclaredMethod("testRehashInPut", int.class, int.class, double.class, double.class)) ), - makeCriterionFromChildCriteria("H4 | Hashtabelle von Listen", - makeCriterion("[[[containsKey(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert in [[[table]]] vorhanden sind.", + makeCriterionFromChildCriteria("H4 | Hashtabelle von Listen ([[[MyListsHashMap]]])", + makeCriterion("[[[containsKey(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert in [[[table]]] vorhanden sind.", () -> MyListsHashMapTests.class .getDeclaredMethod("testContainsKey", MyListsHashMap.class, int.class)), - makeCriterion("[[[containsKey(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert nicht in [[[table]]] vorhanden sind.", + makeCriterion("[[[containsKey(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert nicht in [[[table]]] vorhanden sind.", () -> MyListsHashMapTests.class .getDeclaredMethod("testContainsKeyDisjoint", MyListsHashMap.class, int.class)), - makeCriterion("[[[getValue(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert in [[[table]]] vorhanden sind.", + makeCriterion("[[[getValue(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert in [[[table]]] vorhanden sind.", () -> MyListsHashMapTests.class .getDeclaredMethod("testGetValue", MyListsHashMap.class, int.class)), - makeCriterion("[[[getValue(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert nicht in [[[table]]] vorhanden sind.", + makeCriterion("[[[getValue(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert nicht in [[[table]]] vorhanden sind.", () -> MyListsHashMapTests.class .getDeclaredMethod("testGetValueDisjoint", MyListsHashMap.class, int.class)), - makeCriterion("[[[put(K, V)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert nicht bereits in [[[table]]] vorhanden sind.", + makeCriterion("[[[put(K, V)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert nicht bereits in [[[table]]] vorhanden sind.", () -> MyListsHashMapTests.class .getDeclaredMethod("testPut", MyListsHashMap.class, int.class)), - makeCriterion("[[[put(K, V)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert bereits in [[[table]]] vorhanden sind.", + makeCriterion("[[[put(K, V)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert bereits in [[[table]]] vorhanden sind.", () -> MyListsHashMapTests.class .getDeclaredMethod("testPutDuplicate", MyListsHashMap.class, int.class)), - makeCriterion("[[[remove(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert in [[[table]]] vorhanden sind.", + makeCriterion("[[[remove(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert in [[[table]]] vorhanden sind.", () -> MyListsHashMapTests.class .getDeclaredMethod("testRemove", MyListsHashMap.class, int.class)), - makeCriterion("[[[remove(K)]]] in Klasse [[[MyListsHashMap]]] funktioniert wie beschrieben, " - + "wenn Schlüssel und zugeordneter Wert nicht in [[[table]]] vorhanden sind.", + makeCriterion("[[[remove(K)]]] funktioniert wie beschrieben, wenn Schlüssel und " + + "zugeordneter Wert nicht in [[[table]]] vorhanden sind.", () -> MyListsHashMapTests.class .getDeclaredMethod("testRemoveDisjoint", MyListsHashMap.class, int.class)) ) From c1a28b16828ec4980f14cf81396aec359148b4e8 Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Tue, 28 Jun 2022 00:03:58 +0200 Subject: [PATCH 09/12] Add tests for H5 --- src/grader/java/h06/H06_RubricProvider.java | 15 ++ src/grader/java/h06/h5/MyDateTests.java | 180 +++++++++++++ .../h06/transformers/MyDateTransformer.java | 247 ++++++++++++++++++ src/grader/resources/grader-info.json | 2 + 4 files changed, 444 insertions(+) create mode 100644 src/grader/java/h06/h5/MyDateTests.java create mode 100644 src/grader/java/h06/transformers/MyDateTransformer.java diff --git a/src/grader/java/h06/H06_RubricProvider.java b/src/grader/java/h06/H06_RubricProvider.java index 81b9719..2be2106 100644 --- a/src/grader/java/h06/H06_RubricProvider.java +++ b/src/grader/java/h06/H06_RubricProvider.java @@ -5,12 +5,15 @@ import h06.h1.LinearProbingTests; import h06.h3.MyIndexHoppingHashMapTests; import h06.h4.MyListsHashMapTests; +import h06.h5.MyDateTests; +import h06.transformers.MyDateTransformer; import h06.transformers.MyIndexHoppingHashMapTransformer; import org.sourcegrade.jagr.api.rubric.*; import org.sourcegrade.jagr.api.testing.RubricConfiguration; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.Calendar; import java.util.concurrent.Callable; @RubricForSubmission("h06") @@ -116,6 +119,17 @@ public Rubric getRubric() { + "zugeordneter Wert nicht in [[[table]]] vorhanden sind.", () -> MyListsHashMapTests.class .getDeclaredMethod("testRemoveDisjoint", MyListsHashMap.class, int.class)) + ), + makeCriterionFromChildCriteria("H5 | Eigene hashCode-Implementation ([[[MyDate]]])", + makeCriterion("Der Konstruktor funktioniert wie beschrieben.", + () -> MyDateTests.class + .getDeclaredMethod("testConstructor", Calendar.class, boolean.class)), + makeCriterion("[[[hashCode()]]] funktioniert wie beschrieben.", + () -> MyDateTests.class + .getDeclaredMethod("testHashCode", Calendar.class, boolean.class)), + makeCriterion("[[[equals(Object)]]] funktioniert wie beschrieben.", + () -> MyDateTests.class + .getDeclaredMethod("testEquals")) ) ) .build(); @@ -124,6 +138,7 @@ public Rubric getRubric() { @Override public void configure(RubricConfiguration configuration) { configuration.addTransformer(new MyIndexHoppingHashMapTransformer()); + configuration.addTransformer(new MyDateTransformer()); } @SafeVarargs diff --git a/src/grader/java/h06/h5/MyDateTests.java b/src/grader/java/h06/h5/MyDateTests.java new file mode 100644 index 0000000..4d11d04 --- /dev/null +++ b/src/grader/java/h06/h5/MyDateTests.java @@ -0,0 +1,180 @@ +package h06.h5; + +import h06.MyDate; +import h06.transformers.MyDateTransformer; +import h06.utils.ReflectionUtils; +import h06.utils.TutorAssertions; +import kotlin.Pair; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.sourcegrade.jagr.api.rubric.TestForSubmission; +import org.sourcegrade.jagr.api.testing.extension.JagrExecutionCondition; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Calendar; +import java.util.List; +import java.util.Random; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static h06.Config.SEED; +import static h06.Config.STREAM_SIZE; + +@TestForSubmission("h06") +public class MyDateTests { + + @BeforeEach + public void resetSurrogateFlag() { + MyDateTransformer.HASH_CODE_SURROGATE_ACTIVE = false; + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + public void testConstructor(Calendar calendar, boolean bool) { + Supplier>> inputsSupplier = () -> List.of( + new Pair<>("Argument #1 - [[[date]]]", ("[[[Calendar]]] object with year = %d, month = %d, day_of_month = %d, " + + "hour_of_day = %d, minute = %d").formatted(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE))), + new Pair<>("Argument #2 - [[[randomBoolean]]]", String.valueOf(bool)) + ); + MyDate instance = new MyDate((Calendar) calendar.clone(), bool); + + TutorAssertions.assertEquals( + calendar.get(Calendar.YEAR), null, + ReflectionUtils.getFieldValue("year", instance), null, + "Field [[[year]]] does not have expected value", inputsSupplier); + TutorAssertions.assertEquals( + calendar.get(Calendar.MONTH), null, + ReflectionUtils.getFieldValue("month", instance), null, + "Field [[[month]]] does not have expected value", inputsSupplier); + TutorAssertions.assertEquals( + calendar.get(Calendar.DAY_OF_MONTH), null, + ReflectionUtils.getFieldValue("day", instance), null, + "Field [[[day]]] does not have expected value", inputsSupplier); + TutorAssertions.assertEquals( + calendar.get(Calendar.HOUR_OF_DAY), null, + ReflectionUtils.getFieldValue("hour", instance), null, + "Field [[[hour]]] does not have expected value", inputsSupplier); + TutorAssertions.assertEquals( + calendar.get(Calendar.MINUTE), null, + ReflectionUtils.getFieldValue("minute", instance), null, + "Field [[[minute]]] does not have expected value", inputsSupplier); + TutorAssertions.assertEquals( + bool, null, + ReflectionUtils.getFieldValue("randomBoolean", instance), null, + "Field [[[randomBoolean]]] does not have expected value", inputsSupplier); + } + + @ParameterizedTest + @ArgumentsSource(Provider.class) + @ExtendWith(JagrExecutionCondition.class) + public void testHashCode(Calendar calendar, boolean bool) throws NoSuchMethodException, InvocationTargetException, + InstantiationException, IllegalAccessException { + MyDate instance = MyDate.class + .getDeclaredConstructor(Calendar.class, boolean.class, int.class) + .newInstance(calendar.clone(), bool, 0); + + TutorAssertions.assertEquals( + hashCodeRefImpl(calendar, bool), null, + instance.hashCode(), null, + "[[[hashCode()]]] did not return the expected value", () -> List.of( + new Pair<>("Argument #1 - [[[date]]]", ("[[[Calendar]]] object with year = %d, month = %d, day_of_month = %d, " + + "hour_of_day = %d, minute = %d").formatted(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE))), + new Pair<>("Argument #2 - [[[randomBoolean]]]", String.valueOf(bool)) + )); + } + + @Test + @ExtendWith(JagrExecutionCondition.class) + public void testEquals() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + MyDateTransformer.HASH_CODE_SURROGATE_ACTIVE = true; + Calendar calendar = Calendar.getInstance(); + calendar.set(1970, Calendar.JANUARY, 1, 0, 0); + Supplier>> inputsSupplier = () -> List.of( + new Pair<>("Argument #1 - [[[date]]]", ("[[[Calendar]]] object with year = %d, month = %d, day_of_month = %d, " + + "hour_of_day = %d, minute = %d").formatted(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE))), + new Pair<>("Argument #2 - [[[randomBoolean]]]", String.valueOf(true)) + ); + + Constructor injectedConstructor = MyDate.class.getDeclaredConstructor(Calendar.class, boolean.class, int.class); + MyDate instance1 = injectedConstructor.newInstance(calendar, true, 0); + MyDate instance2 = injectedConstructor.newInstance(calendar, true, 0); + MyDate instance3 = injectedConstructor.newInstance(calendar, true, 1); + + TutorAssertions.assertEquals( + true, null, + instance1.equals(instance2), null, + "[[[equals(Object)]]] did not return [[[true]]], even though both objects have the same hashCode", inputsSupplier); + TutorAssertions.assertEquals( + false, null, + instance1.equals(instance3), null, + "[[[equals(Object)]]] did not return [[[true]]], even though both objects have the same hashCode", inputsSupplier); + } + + private static class Provider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) { + Random random = new Random(SEED); + + return Stream.generate(() -> { + Calendar calendar = Calendar.getInstance(); + calendar.set( + random.nextInt(1970, 2021 + 1), + random.nextInt(0, 11 + 1), + random.nextInt(1, 28 + 1), + random.nextInt(0, 24 + 1), + random.nextInt(0, 60 + 1) + ); + return new Object[] {calendar, random.nextBoolean()}; + }) + .limit(STREAM_SIZE) + .map(Arguments::of); + } + } + + private static int hashCodeRefImpl(Calendar date, boolean randomBoolean) { + int year = date.get(Calendar.YEAR); + int month = date.get(Calendar.MONTH); + int day = date.get(Calendar.DAY_OF_MONTH); + int hour = date.get(Calendar.HOUR_OF_DAY); + int minute = date.get(Calendar.MINUTE); + + if (randomBoolean) { + long coefficientYear = 4563766470487201L; + long coefficientMonth = 73232L; + long coefficientDay = 4L; + long coefficientHour = 1234L; + long coefficientMinute = 99998L; + +// return Math.floorMod( +// Math.floorMod(coefficientYear * year, (long) Integer.MAX_VALUE) +// + Math.floorMod(coefficientMonth * month, Integer.MAX_VALUE) +// + Math.floorMod(coefficientDay * day, Integer.MAX_VALUE) +// + Math.floorMod(coefficientHour * hour, Integer.MAX_VALUE) +// + Math.floorMod(coefficientMinute * minute, Integer.MAX_VALUE), +// Integer.MAX_VALUE); + return (int) Math.floorMod( + Math.floorMod((coefficientYear * year), (long)Integer.MAX_VALUE) + + Math.floorMod((coefficientMonth * month), Integer.MAX_VALUE) + + Math.floorMod((coefficientDay * day), Integer.MAX_VALUE) + + Math.floorMod((coefficientHour * hour), Integer.MAX_VALUE) + + Math.floorMod((coefficientMinute * minute), Integer.MAX_VALUE) + , Integer.MAX_VALUE); + } else { + long coefficientSum = 98924L; + +// return Math.floorMod(((year + month + day + hour + minute) * coefficientSum), Integer.MAX_VALUE); + return (int)Math.floorMod(((year + month + day + hour + minute) * coefficientSum), Integer.MAX_VALUE); + } + } +} diff --git a/src/grader/java/h06/transformers/MyDateTransformer.java b/src/grader/java/h06/transformers/MyDateTransformer.java new file mode 100644 index 0000000..69a719a --- /dev/null +++ b/src/grader/java/h06/transformers/MyDateTransformer.java @@ -0,0 +1,247 @@ +package h06.transformers; + +import org.objectweb.asm.*; +import org.sourcegrade.jagr.api.testing.ClassTransformer; + +public class MyDateTransformer implements ClassTransformer { + + public static boolean HASH_CODE_SURROGATE_ACTIVE = true; + + @Override + public String getName() { + return "MyDateTransformer"; + } + + @Override + public void transform(ClassReader reader, ClassWriter writer) { + if (reader.getClassName().equals("h06/MyDate")) { + reader.accept(new ClassTransformer(writer), ClassReader.SKIP_DEBUG); + } else { + reader.accept(writer, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + } + } + + private static class ClassTransformer extends ClassVisitor { + + private ClassTransformer(ClassWriter classWriter) { + super(Opcodes.ASM9, classWriter); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + if (name.equals("hashCode") && descriptor.equals("()I")) { + return new MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) { + final Label label = new Label(); + + @Override + public void visitCode() { + super.visitFieldInsn(Opcodes.GETSTATIC, + "h06/transformers/MyDateTransformer", + "HASH_CODE_SURROGATE_ACTIVE", + "Z"); + super.visitJumpInsn(Opcodes.IFNE, label); + super.visitCode(); + } + + @Override + public void visitEnd() { + super.visitLabel(label); + super.visitFrame(Opcodes.F_FULL, 1, new Object[] {"h06/MyDate"}, 0, + new Object[0]); + super.visitVarInsn(Opcodes.ALOAD, 0); + super.visitFieldInsn(Opcodes.GETFIELD, + "h06/MyDate", + "hashCodeReturnValue", + "I"); + super.visitInsn(Opcodes.IRETURN); + super.visitEnd(); + } + }; + } else if (name.equals("") && descriptor.equals("(Ljava/util/Calendar;ZI)V")) { + return null; + } else { + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + } + + @Override + public void visitEnd() { + super.visitField(Opcodes.ACC_PUBLIC, + "hashCodeReturnValue", + "I", + null, + null); + + MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC, + "", + "(Ljava/util/Calendar;ZI)V", + null, + null); + injectConstructorCode(mv); + + super.visitEnd(); + } + + private static void injectConstructorCode(MethodVisitor mv) { + // super() + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, + "java/lang/Object", + "", + "()V", + false); + + // assign coefficientYear + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitLdcInsn(4563766470487201L); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "coefficientYear", + "J"); + + // assign coefficientMonth + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitLdcInsn(73232L); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "coefficientMonth", + "J"); + + // assign coefficientDay + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitLdcInsn(4L); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "coefficientDay", + "J"); + + // assign coefficientHour + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitLdcInsn(1234L); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "coefficientHour", + "J"); + + // assign coefficientMinute + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitLdcInsn(99998L); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "coefficientMinute", + "J"); + + // assign coefficientSum + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitLdcInsn(98924L); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "coefficientSum", + "J"); + + // assign year + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitFieldInsn(Opcodes.GETSTATIC, + "java/util/Calendar", + "YEAR", + "I"); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/util/Calendar", + "get", + "(I)I", + false); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "year", + "I"); + + // assign month + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitFieldInsn(Opcodes.GETSTATIC, + "java/util/Calendar", + "MONTH", + "I"); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/util/Calendar", + "get", + "(I)I", + false); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "month", + "I"); + + // assign day + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitFieldInsn(Opcodes.GETSTATIC, + "java/util/Calendar", + "DAY_OF_MONTH", + "I"); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/util/Calendar", + "get", + "(I)I", + false); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "day", + "I"); + + // assign hour + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitFieldInsn(Opcodes.GETSTATIC, + "java/util/Calendar", + "HOUR_OF_DAY", + "I"); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/util/Calendar", + "get", + "(I)I", + false); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "hour", + "I"); + + // assign minute + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitFieldInsn(Opcodes.GETSTATIC, + "java/util/Calendar", + "MINUTE", + "I"); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, + "java/util/Calendar", + "get", + "(I)I", + false); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "minute", + "I"); + + // assign randomBoolean + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ILOAD, 2); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "randomBoolean", + "Z"); + + // assign hashCodeReturnValue + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ILOAD, 3); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "hashCodeReturnValue", + "I"); + + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(3, 4); + } + } +} diff --git a/src/grader/resources/grader-info.json b/src/grader/resources/grader-info.json index 80bab8c..17c6090 100644 --- a/src/grader/resources/grader-info.json +++ b/src/grader/resources/grader-info.json @@ -12,8 +12,10 @@ "h06/h1/LinearProbingTests.java", "h06/h3/MyIndexHoppingHashMapTests.java", "h06/h4/MyListsHashMapTests.java", + "h06/h5/MyDateTests.java", "h06/mocks/Mock.java", "h06/transformers/Hash2IndexFctTransformer.java", + "h06/transformers/MyDateTransformer.java", "h06/transformers/MyIndexHoppingHashMapTransformer.java", "h06/utils/ReflectionUtils.java", "h06/utils/TutorAssertions.java", From a5fe4885681359f3c4a7e51f16c3a543f8acb502 Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Tue, 28 Jun 2022 12:54:21 +0200 Subject: [PATCH 10/12] Add rubrics for H6 --- src/grader/java/h06/H06_RubricProvider.java | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/grader/java/h06/H06_RubricProvider.java b/src/grader/java/h06/H06_RubricProvider.java index 2be2106..e8f0770 100644 --- a/src/grader/java/h06/H06_RubricProvider.java +++ b/src/grader/java/h06/H06_RubricProvider.java @@ -130,6 +130,28 @@ public Rubric getRubric() { makeCriterion("[[[equals(Object)]]] funktioniert wie beschrieben.", () -> MyDateTests.class .getDeclaredMethod("testEquals")) + ), + makeCriterionFromChildCriteria("H6 | Tests ([[[RuntimeTests]]])", + Criterion.builder() + .shortDescription("Zufallszahlen werden in [[[generateTestdata()]]] wie beschrieben generiert. " + + "(kein Filtern, sondern Modulo-Bildung)") + .build(), + Criterion.builder() + .shortDescription("Die Dimensionen des von [[[generateTestdata()]]] zurückgegebenen Arrays sind korrekt.") + .build(), + Criterion.builder() + .shortDescription("Die Elemente in dem von [[[generateTestdata()]]] zurückgegebenen Array wurden korrekt " + + "initialisiert.") + .build(), + Criterion.builder() + .shortDescription("Methode [[[createTestSet(int, int, int, int, MyDate[][])]]] funktioniert wie " + + "beschrieben.") + .build(), + Criterion.builder() + .shortDescription("Methode [[[test(TestSet)]]] funktioniert wie beschrieben.") + .minPoints(0) + .maxPoints(2) + .build() ) ) .build(); From a6eb8ab356520c349e63b34c61c793645a9eb458 Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Fri, 1 Jul 2022 15:58:09 +0200 Subject: [PATCH 11/12] Add tests for H6 (some criteria need manual grading) --- src/grader/java/h06/H06_RubricProvider.java | 23 +-- src/grader/java/h06/h6/RuntimeTestTests.java | 184 ++++++++++++++++++ .../h06/transformers/MyDateTransformer.java | 52 +++-- src/grader/resources/grader-info.json | 1 + 4 files changed, 237 insertions(+), 23 deletions(-) create mode 100644 src/grader/java/h06/h6/RuntimeTestTests.java diff --git a/src/grader/java/h06/H06_RubricProvider.java b/src/grader/java/h06/H06_RubricProvider.java index e8f0770..868ca7f 100644 --- a/src/grader/java/h06/H06_RubricProvider.java +++ b/src/grader/java/h06/H06_RubricProvider.java @@ -6,6 +6,7 @@ import h06.h3.MyIndexHoppingHashMapTests; import h06.h4.MyListsHashMapTests; import h06.h5.MyDateTests; +import h06.h6.RuntimeTestTests; import h06.transformers.MyDateTransformer; import h06.transformers.MyIndexHoppingHashMapTransformer; import org.sourcegrade.jagr.api.rubric.*; @@ -136,22 +137,22 @@ public Rubric getRubric() { .shortDescription("Zufallszahlen werden in [[[generateTestdata()]]] wie beschrieben generiert. " + "(kein Filtern, sondern Modulo-Bildung)") .build(), - Criterion.builder() - .shortDescription("Die Dimensionen des von [[[generateTestdata()]]] zurückgegebenen Arrays sind korrekt.") - .build(), - Criterion.builder() - .shortDescription("Die Elemente in dem von [[[generateTestdata()]]] zurückgegebenen Array wurden korrekt " - + "initialisiert.") - .build(), + makeCriterion("Die Dimensionen des von [[[generateTestdata()]]] zurückgegebenen Arrays sind korrekt.", + () -> RuntimeTestTests.class + .getDeclaredMethod("testGenerateTestdataDimensions")), + makeCriterion("Die Elemente in dem von [[[generateTestdata()]]] zurückgegebenen Array wurden korrekt " + + "initialisiert.", + () -> RuntimeTestTests.class + .getDeclaredMethod("testGenerateTestdataElementInit")), Criterion.builder() .shortDescription("Methode [[[createTestSet(int, int, int, int, MyDate[][])]]] funktioniert wie " + "beschrieben.") - .build(), - Criterion.builder() - .shortDescription("Methode [[[test(TestSet)]]] funktioniert wie beschrieben.") .minPoints(0) .maxPoints(2) - .build() + .build(), + makeCriterion("Methode [[[test(TestSet)]]] funktioniert wie beschrieben.", + () -> RuntimeTestTests.class + .getDeclaredMethod("testTest")) ) ) .build(); diff --git a/src/grader/java/h06/h6/RuntimeTestTests.java b/src/grader/java/h06/h6/RuntimeTestTests.java new file mode 100644 index 0000000..b17b82a --- /dev/null +++ b/src/grader/java/h06/h6/RuntimeTestTests.java @@ -0,0 +1,184 @@ +package h06.h6; + +import h06.MyDate; +import h06.MyMap; +import h06.RuntimeTest; +import h06.TestSet; +import h06.transformers.MyDateTransformer; +import h06.utils.ReflectionUtils; +import h06.utils.TutorAssertions; +import kotlin.Pair; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.sourcegrade.jagr.api.rubric.TestForSubmission; +import org.sourcegrade.jagr.api.testing.extension.JagrExecutionCondition; + +import java.lang.reflect.Field; +import java.text.DateFormat; +import java.time.Instant; +import java.util.*; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static h06.Config.SEED; +import static h06.Config.STREAM_SIZE; +import static org.junit.jupiter.api.Assertions.fail; + +@TestForSubmission("h06") +public class RuntimeTestTests { + + private static final int TEST_SET_SIZE = ReflectionUtils.getFieldValue( + ReflectionUtils.getField(RuntimeTest.class, "TEST_SET_SIZE"), null); + + private static MyDate[][] MY_DATES_TEST_SET; + + @BeforeAll + public static void setup() { + MyDateTransformer.CONSTRUCTOR_SURROGATE_ACTIVE = true; + MyDateTransformer.HASH_CODE_SURROGATE_ACTIVE = true; + MY_DATES_TEST_SET = RuntimeTest.generateTestdata(); + } + + @AfterAll + public static void resetFlags() { + MyDateTransformer.CONSTRUCTOR_SURROGATE_ACTIVE = false; + MyDateTransformer.HASH_CODE_SURROGATE_ACTIVE = false; + } + + @Test + @ExtendWith(JagrExecutionCondition.class) + public void testGenerateTestdataDimensions() { + TutorAssertions.assertEquals( + 2, null, + MY_DATES_TEST_SET.length, null, + "The outer array returned by [[[generateTestdata()]]] did not have the expected size", List::of); + TutorAssertions.assertEquals( + TEST_SET_SIZE, null, + MY_DATES_TEST_SET[0].length, null, + "The inner array returned by [[[generateTestdata()]]] did not have the expected size at index 0", List::of); + TutorAssertions.assertEquals( + TEST_SET_SIZE, null, + MY_DATES_TEST_SET[1].length, null, + "The inner array returned by [[[generateTestdata()]]] did not have the expected size at index 1", List::of); + } + + @Test + @ExtendWith(JagrExecutionCondition.class) + public void testGenerateTestdataElementInit() { + if (MY_DATES_TEST_SET.length != 2 && MY_DATES_TEST_SET[0].length != TEST_SET_SIZE && MY_DATES_TEST_SET[1].length != TEST_SET_SIZE) { + fail("The array returned by [[[generateTestdata()]]] did not have the expected dimensions"); + } + + Field randomBooleanField = ReflectionUtils.getField(MyDate.class, "randomBoolean"); + + for (int i = 0; i < TEST_SET_SIZE; i++) { + TutorAssertions.assertEquals( + true, null, + ReflectionUtils.getFieldValue(randomBooleanField, MY_DATES_TEST_SET[0][i]), null, + ("The [[[MyDate]]] object at index [0][%d] in the array returned by [[[generateTestdata()]]] was not initialized " + + " correctly").formatted(i), List::of); + TutorAssertions.assertEquals( + false, null, + ReflectionUtils.getFieldValue(randomBooleanField, MY_DATES_TEST_SET[1][i]), null, + ("The [[[MyDate]]] object at index [1][%d] in the array returned by [[[generateTestdata()]]] was not initialized " + + " correctly").formatted(i), List::of); + } + } + + @Test + @ExtendWith(JagrExecutionCondition.class) + public void testTest() { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(0); + MyMapImpl myMap = new MyMapImpl(calendar); + MyDate[] testData = new MyDate[1000]; + MyDate testDatum = new MyDate(calendar, false); + Arrays.fill(testData, testDatum); + TestSet testSet = new TestSet<>(myMap, testData); + Supplier>> inputsSupplier = () -> List.of( + new Pair<>("[[[calendar]]]", ("[[[Calendar]]] object with year = %d, month = %d, day_of_month = %d, " + + "hour_of_day = %d, minute = %d").formatted(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE))), + new Pair<>("[[[myMap]]]", "[[[MyMap]]] reference implementation"), + new Pair<>("[[[testData]]]", "[[[MyDate]]] array with size 1000, filled with valid [[[MyDate]]] objects " + + "([[[new MyDate(calendar, false)]]]; Constructor has been replaced to ensure correct initialization)"), + new Pair<>("[[[testSet]]]", "[[[new TestSet<>(myMap, testData)]]]") + ); + + RuntimeTest.test(testSet); + + TutorAssertions.assertEquals( + 750, null, + myMap.putInvocationCounter, null, + "[[[put(K, V)]]] was not called 750 times by [[[RuntimeTest.test(testSet)]]]", inputsSupplier); + TutorAssertions.assertEquals( + testData.length, null, + myMap.containsKeyInvocationCounter, null, + "[[[containsKey(K)]]] was not called %d times by [[[RuntimeTest.test(testSet)]]]".formatted(testData.length), + inputsSupplier); + TutorAssertions.assertEquals( + testData.length, null, + myMap.getValueInvocationCounter, null, + "[[[getValue(K)]]] was not called %d times by [[[RuntimeTest.test(testSet)]]]".formatted(testData.length), + inputsSupplier); + TutorAssertions.assertEquals( + testData.length, null, + myMap.removeInvocationCounter, null, + "[[[remove(K)]]] was not called %d times by [[[RuntimeTest.test(testSet)]]]".formatted(testData.length), + inputsSupplier); + } + + private static class Provider implements ArgumentsProvider { + + @Override + public Stream provideArguments(ExtensionContext context) { + Random random = new Random(SEED); + + return Stream.generate(() -> null) + .limit(STREAM_SIZE) + .map(Arguments::of); + } + } + + private static class MyMapImpl implements MyMap { + private final MyDate myDateReturnValue; + private int containsKeyInvocationCounter = 0; + private int getValueInvocationCounter = 0; + private int putInvocationCounter = 0; + private int removeInvocationCounter = 0; + + private MyMapImpl(Calendar calendar) { + this.myDateReturnValue = new MyDate(calendar, false); + } + + @Override + public boolean containsKey(MyDate key) { + containsKeyInvocationCounter++; + return false; + } + + @Override + public @Nullable MyDate getValue(MyDate key) { + getValueInvocationCounter++; + return myDateReturnValue; + } + + @Override + public @Nullable MyDate put(MyDate key, MyDate value) { + putInvocationCounter++; + return myDateReturnValue; + } + + @Override + public @Nullable MyDate remove(MyDate key) { + removeInvocationCounter++; + return myDateReturnValue; + } + } +} diff --git a/src/grader/java/h06/transformers/MyDateTransformer.java b/src/grader/java/h06/transformers/MyDateTransformer.java index 69a719a..495664f 100644 --- a/src/grader/java/h06/transformers/MyDateTransformer.java +++ b/src/grader/java/h06/transformers/MyDateTransformer.java @@ -5,6 +5,7 @@ public class MyDateTransformer implements ClassTransformer { + public static boolean CONSTRUCTOR_SURROGATE_ACTIVE = false; public static boolean HASH_CODE_SURROGATE_ACTIVE = true; @Override @@ -21,6 +22,11 @@ public void transform(ClassReader reader, ClassWriter writer) { } } + @Override + public int getWriterFlags() { + return ClassWriter.COMPUTE_MAXS; + } + private static class ClassTransformer extends ClassVisitor { private ClassTransformer(ClassWriter classWriter) { @@ -29,7 +35,32 @@ private ClassTransformer(ClassWriter classWriter) { @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { - if (name.equals("hashCode") && descriptor.equals("()I")) { + if (name.equals("") && descriptor.equals("(Ljava/util/Calendar;Z)V")) { + return new MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) { + final Label label = new Label(); + + @Override + public void visitCode() { + super.visitFieldInsn(Opcodes.GETSTATIC, + "h06/transformers/MyDateTransformer", + "CONSTRUCTOR_SURROGATE_ACTIVE", + "Z"); + super.visitJumpInsn(Opcodes.IFNE, label); + super.visitCode(); + } + + @Override + public void visitEnd() { + super.visitLabel(label); + super.visitFrame(Opcodes.F_FULL, + 3, new Object[] {Opcodes.UNINITIALIZED_THIS, "java/util/Calendar", Opcodes.INTEGER}, + 0, new Object[0]); + injectConstructorCode(this); + super.visitInsn(Opcodes.RETURN); + super.visitEnd(); + } + }; + } else if (name.equals("hashCode") && descriptor.equals("()I")) { return new MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) { final Label label = new Label(); @@ -78,6 +109,14 @@ public void visitEnd() { null, null); injectConstructorCode(mv); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitVarInsn(Opcodes.ILOAD, 3); + mv.visitFieldInsn(Opcodes.PUTFIELD, + "h06/MyDate", + "hashCodeReturnValue", + "I"); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(3, 4); super.visitEnd(); } @@ -231,17 +270,6 @@ private static void injectConstructorCode(MethodVisitor mv) { "h06/MyDate", "randomBoolean", "Z"); - - // assign hashCodeReturnValue - mv.visitVarInsn(Opcodes.ALOAD, 0); - mv.visitVarInsn(Opcodes.ILOAD, 3); - mv.visitFieldInsn(Opcodes.PUTFIELD, - "h06/MyDate", - "hashCodeReturnValue", - "I"); - - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(3, 4); } } } diff --git a/src/grader/resources/grader-info.json b/src/grader/resources/grader-info.json index 17c6090..a536d16 100644 --- a/src/grader/resources/grader-info.json +++ b/src/grader/resources/grader-info.json @@ -13,6 +13,7 @@ "h06/h3/MyIndexHoppingHashMapTests.java", "h06/h4/MyListsHashMapTests.java", "h06/h5/MyDateTests.java", + "h06/h6/RuntimeTestTests.java", "h06/mocks/Mock.java", "h06/transformers/Hash2IndexFctTransformer.java", "h06/transformers/MyDateTransformer.java", From cedc1854d91585cc3739580e3568d241f694dd7a Mon Sep 17 00:00:00 2001 From: Daniel Mangold Date: Fri, 1 Jul 2022 16:28:19 +0200 Subject: [PATCH 12/12] Cleanup --- src/grader/java/h06/Config.java | 2 - .../java/h06/h1/DoubleHashingTests.java | 29 +++++++- .../java/h06/h1/Hash2IndexFctTests.java | 13 ---- .../h06/h3/MyIndexHoppingHashMapTests.java | 68 +++++++----------- src/grader/java/h06/h5/MyDateTests.java | 24 +++---- src/grader/java/h06/h6/RuntimeTestTests.java | 24 +------ src/grader/java/h06/mocks/Mock.java | 49 ------------- .../Hash2IndexFctTransformer.java | 71 ------------------- .../java/h06/utils/ReflectionUtils.java | 55 -------------- src/grader/resources/grader-info.json | 2 - 10 files changed, 64 insertions(+), 273 deletions(-) delete mode 100644 src/grader/java/h06/mocks/Mock.java delete mode 100644 src/grader/java/h06/transformers/Hash2IndexFctTransformer.java diff --git a/src/grader/java/h06/Config.java b/src/grader/java/h06/Config.java index bde0c4d..94a7ac4 100644 --- a/src/grader/java/h06/Config.java +++ b/src/grader/java/h06/Config.java @@ -7,6 +7,4 @@ private Config() {} public static final String ASSIGNMENT_ID = "h06"; public static final long SEED = 0L; public static final int STREAM_SIZE = 5; - - public static boolean REPLACE = true; } diff --git a/src/grader/java/h06/h1/DoubleHashingTests.java b/src/grader/java/h06/h1/DoubleHashingTests.java index fd386dd..4111e57 100644 --- a/src/grader/java/h06/h1/DoubleHashingTests.java +++ b/src/grader/java/h06/h1/DoubleHashingTests.java @@ -2,7 +2,7 @@ import h06.DoubleHashing; import h06.Hash2IndexFct; -import h06.mocks.Mock; +import h06.utils.ReflectionUtils; import h06.utils.TutorAssertions; import kotlin.Pair; import org.junit.jupiter.api.extension.ExtensionContext; @@ -10,8 +10,12 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; import org.sourcegrade.jagr.api.rubric.TestForSubmission; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.List; import java.util.Random; import java.util.stream.Stream; @@ -79,8 +83,27 @@ private static class Provider implements ArgumentsProvider { public Stream provideArguments(ExtensionContext context) { Random random = new Random(SEED); - return Stream.generate(() -> new Object[] {Mock.getMock(Hash2IndexFct.class, Mock.HASH_2_INDEX_FCT, - random.nextInt(10) + 1, 0), random.nextInt(10) + 1}) + return Stream.generate(() -> { + int tableSize = random.nextInt(10) + 1; + int offset = 0; + Answer answer = invocation -> { + Method calledMethod = invocation.getMethod(); + if (calledMethod.getName().equals("apply") + && calledMethod.getParameterCount() == 1 + && calledMethod.getParameterTypes()[0].equals(Object.class)) { + return Math.floorMod(Math.abs((long) invocation.getArgument(0).hashCode()) + offset, tableSize); + } else { + return invocation.callRealMethod(); + } + }; + Hash2IndexFct instance = Mockito.mock(Hash2IndexFct.class, answer); + Field tableSizeField = ReflectionUtils.getField(Hash2IndexFct.class, "tableSize"); + ReflectionUtils.setFieldValue(tableSizeField, instance, tableSize); + Field offsetField = ReflectionUtils.getField(Hash2IndexFct.class, "offset"); + ReflectionUtils.setFieldValue(offsetField, instance, offset); + + return new Object[] {instance, random.nextInt(10) + 1}; + }) .limit(STREAM_SIZE) .map(Arguments::of); } diff --git a/src/grader/java/h06/h1/Hash2IndexFctTests.java b/src/grader/java/h06/h1/Hash2IndexFctTests.java index 5e47255..8d850b9 100644 --- a/src/grader/java/h06/h1/Hash2IndexFctTests.java +++ b/src/grader/java/h06/h1/Hash2IndexFctTests.java @@ -1,11 +1,8 @@ package h06.h1; import h06.Hash2IndexFct; -import h06.transformers.Hash2IndexFctTransformer; import h06.utils.TutorAssertions; import kotlin.Pair; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -23,16 +20,6 @@ @TestForSubmission("h06") public class Hash2IndexFctTests { - @BeforeAll - public static void deactivateSurrogate() { - Hash2IndexFctTransformer.SURROGATE_ACTIVE = false; - } - - @AfterAll - public static void reactivateSurrogate() { - Hash2IndexFctTransformer.SURROGATE_ACTIVE = true; - } - @ParameterizedTest @ArgumentsSource(Provider.class) public void testApply(int tableSize, int offset) { diff --git a/src/grader/java/h06/h3/MyIndexHoppingHashMapTests.java b/src/grader/java/h06/h3/MyIndexHoppingHashMapTests.java index 3988e0e..0d512cd 100644 --- a/src/grader/java/h06/h3/MyIndexHoppingHashMapTests.java +++ b/src/grader/java/h06/h3/MyIndexHoppingHashMapTests.java @@ -25,6 +25,7 @@ import static h06.Config.STREAM_SIZE; @TestForSubmission("h06") +@SuppressWarnings("DuplicatedCode") public class MyIndexHoppingHashMapTests { public static boolean REHASH_CALLED = false; @@ -37,9 +38,7 @@ public void reset() { @ParameterizedTest @ArgumentsSource(Provider.class) public void testContainsKey(int tableSize, int index, double resizeFactor, double resizeThreshold) { - BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); - MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, - binaryFct2Int); + MyIndexHoppingHashMap instance = getHashFunction(tableSize, index, resizeFactor, resizeThreshold); Object key = new Object(); Object value = new Object(); ((Object[]) ReflectionUtils.getFieldValue("theKeys", instance))[index] = key; @@ -50,10 +49,10 @@ public void testContainsKey(int tableSize, int index, double resizeFactor, doubl TutorAssertions.assertEquals( true, null, instance.containsKey(key), null, - "[[[containsKey(K)]]] did not return [[[true]]], even though the key is in [[[theKeys]]] at the correct position", + "[[[containsKey(K)]]] did not return [[[true]]], even though the key is at the correct position in [[[theKeys]]]", () -> List.of( new Pair<>("[[[binaryFct2Int]]]", - "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + "[[[BinaryFct2Int]]] reference implementation; [[[apply(T, int)]]] always returns " + index), new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( tableSize, resizeFactor, resizeThreshold) + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " @@ -64,16 +63,14 @@ public void testContainsKey(int tableSize, int index, double resizeFactor, doubl @ParameterizedTest @ArgumentsSource(Provider.class) public void testContainsKeyForeign(int tableSize, int index, double resizeFactor, double resizeThreshold) { - BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); - MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, - binaryFct2Int); + MyIndexHoppingHashMap instance = getHashFunction(tableSize, index, resizeFactor, resizeThreshold); TutorAssertions.assertEquals( false, null, instance.containsKey(new Object()), null, "[[[containsKey(K)]]] did not return [[[false]]], even though the key is not in [[[theKeys]]]", () -> List.of( new Pair<>("[[[binaryFct2Int]]]", - "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + "[[[BinaryFct2Int]]] reference implementation; [[[apply(T, int)]]] always returns " + index), new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( tableSize, resizeFactor, resizeThreshold) + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " @@ -84,9 +81,7 @@ public void testContainsKeyForeign(int tableSize, int index, double resizeFactor @ParameterizedTest @ArgumentsSource(Provider.class) public void testGetValue(int tableSize, int index, double resizeFactor, double resizeThreshold) { - BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); - MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, - binaryFct2Int); + MyIndexHoppingHashMap instance = getHashFunction(tableSize, index, resizeFactor, resizeThreshold); Object key = new Object(); Object value = new Object(); ((Object[]) ReflectionUtils.getFieldValue("theKeys", instance))[index] = key; @@ -100,7 +95,7 @@ public void testGetValue(int tableSize, int index, double resizeFactor, double r "[[[getValue(K)]]] did not return the expected object, even though key and value are both at the correct position", () -> List.of( new Pair<>("[[[binaryFct2Int]]]", - "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + "[[[BinaryFct2Int]]] reference implementation; [[[apply(T, int)]]] always returns " + index), new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( tableSize, resizeFactor, resizeThreshold) + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " @@ -111,9 +106,7 @@ public void testGetValue(int tableSize, int index, double resizeFactor, double r @ParameterizedTest @ArgumentsSource(Provider.class) public void testGetValueForeign(int tableSize, int index, double resizeFactor, double resizeThreshold) { - BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); - MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, - binaryFct2Int); + MyIndexHoppingHashMap instance = getHashFunction(tableSize, index, resizeFactor, resizeThreshold); TutorAssertions.assertEquals( null, null, @@ -121,7 +114,7 @@ public void testGetValueForeign(int tableSize, int index, double resizeFactor, d "[[[getValue(K)]]] did not return [[[null]]], even though key and value are not in [[[theKeys]]] and [[[theValues]]]", () -> List.of( new Pair<>("[[[binaryFct2Int]]]", - "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + "[[[BinaryFct2Int]]] reference implementation; [[[apply(T, int)]]] always returns " + index), new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( tableSize, resizeFactor, resizeThreshold) + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " @@ -134,13 +127,11 @@ public void testGetValueForeign(int tableSize, int index, double resizeFactor, d public void testPut(int tableSize, int index, double resizeFactor, double resizeThreshold) { Supplier>> inputsListSupplier = () -> List.of( new Pair<>("[[[binaryFct2Int]]]", - "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + "[[[BinaryFct2Int]]] reference implementation; [[[apply(T, int)]]] always returns " + index), new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]]".formatted( tableSize, resizeFactor, resizeThreshold)) ); - BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); - MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, - binaryFct2Int); + MyIndexHoppingHashMap instance = getHashFunction(tableSize, index, resizeFactor, resizeThreshold); Object key = new Object(); Object value = new Object(); @@ -172,15 +163,13 @@ public void testPut(int tableSize, int index, double resizeFactor, double resize public void testPutDuplicate(int tableSize, int index, double resizeFactor, double resizeThreshold) { Supplier>> inputsListSupplier = () -> List.of( new Pair<>("[[[binaryFct2Int]]]", - "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + "[[[BinaryFct2Int]]] reference implementation; [[[apply(T, int)]]] always returns " + index), new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( tableSize, resizeFactor, resizeThreshold) + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " + "to simulate insertion operations prior to calling [[[put(K, V)]]]") ); - BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); - MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, - binaryFct2Int); + MyIndexHoppingHashMap instance = getHashFunction(tableSize, index, resizeFactor, resizeThreshold); Object key = new Object(); Object value = new Object(); Object newValue = new Object(); @@ -218,15 +207,13 @@ public void testPutDuplicate(int tableSize, int index, double resizeFactor, doub public void testRemove(int tableSize, int index, double resizeFactor, double resizeThreshold) { Supplier>> inputsListSupplier = () -> List.of( new Pair<>("[[[binaryFct2Int]]]", - "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + "[[[BinaryFct2Int]]] reference implementation; [[[apply(T, int)]]] always returns " + index), new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]] ".formatted( tableSize, resizeFactor, resizeThreshold) + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " + "to simulate insertion operations prior to calling [[[remove(K)]]]") ); - BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); - MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, - binaryFct2Int); + MyIndexHoppingHashMap instance = getHashFunction(tableSize, index, resizeFactor, resizeThreshold); Object key = new Object(); Object value = new Object(); ((Object[]) ReflectionUtils.getFieldValue("theKeys", instance))[index] = key; @@ -263,13 +250,11 @@ public void testRemove(int tableSize, int index, double resizeFactor, double res public void testRemoveForeign(int tableSize, int index, double resizeFactor, double resizeThreshold) { Supplier>> inputsListSupplier = () -> List.of( new Pair<>("[[[binaryFct2Int]]]", - "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + "[[[BinaryFct2Int]]] reference implementation; [[[apply(T, int)]]] always returns " + index), new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]]".formatted( tableSize, resizeFactor, resizeThreshold)) ); - BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); - MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, - binaryFct2Int); + MyIndexHoppingHashMap instance = getHashFunction(tableSize, index, resizeFactor, resizeThreshold); int newLength = (int) (tableSize * resizeFactor); try { @@ -297,9 +282,7 @@ public void testRemoveForeign(int tableSize, int index, double resizeFactor, dou @ParameterizedTest @ArgumentsSource(Provider.class) public void testRehash(int tableSize, int index, double resizeFactor, double resizeThreshold) { - BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); - MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, - binaryFct2Int); + MyIndexHoppingHashMap instance = getHashFunction(tableSize, index, resizeFactor, resizeThreshold); Object key = new Object(); TutorAssertions.assertEquals( @@ -308,7 +291,7 @@ public void testRehash(int tableSize, int index, double resizeFactor, double res "[[[remove(K)]]] did not return [[[null]]], even though key and value are not in [[[theKeys]]] and [[[theValues]]]", () -> List.of( new Pair<>("[[[binaryFct2Int]]]", - "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + "[[[BinaryFct2Int]]] reference implementation; [[[apply(T, int)]]] always returns " + index), new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]]".formatted( tableSize, resizeFactor, resizeThreshold) + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " @@ -320,7 +303,6 @@ public void testRehash(int tableSize, int index, double resizeFactor, double res @ArgumentsSource(Provider.class) @ExtendWith(JagrExecutionCondition.class) public void testRehashInPut(int tableSize, int index, double resizeFactor, double resizeThreshold) { - BinaryFct2IntImpl binaryFct2Int = new BinaryFct2IntImpl(tableSize, index); Object[] theKeys = new Object[tableSize]; Object[] theValues = new Object[tableSize]; boolean[] occupiedSinceLastRehash = new boolean[tableSize]; @@ -331,8 +313,7 @@ public void testRehashInPut(int tableSize, int index, double resizeFactor, doubl occupiedSinceLastRehash[i] = true; } - MyIndexHoppingHashMap instance = new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, - binaryFct2Int); + MyIndexHoppingHashMap instance = getHashFunction(tableSize, index, resizeFactor, resizeThreshold); ReflectionUtils.setFieldValue("theKeys", instance, theKeys); ReflectionUtils.setFieldValue("theValues", instance, theValues); ReflectionUtils.setFieldValue("occupiedSinceLastRehash", instance, occupiedSinceLastRehash); @@ -346,7 +327,7 @@ public void testRehashInPut(int tableSize, int index, double resizeFactor, doubl "[[[put(K, V)]]] did not call [[[rehash()]]], even though [[[occupiedCount]]] exceeds threshold", () -> List.of( new Pair<>("[[[binaryFct2Int]]]", - "[[[BinaryFct2Int]]] reference implementation; [[[apply(Object, int)]]] always returns " + index), + "[[[BinaryFct2Int]]] reference implementation; [[[apply(T, int)]]] always returns " + index), new Pair<>("[[[this]]]", "[[[new MyIndexHoppingHashMap<>(%d, %f, %f, binaryFct2Int)]]]".formatted( tableSize, resizeFactor, resizeThreshold) + "[[[theKeys]]], [[[theValues]]], [[[occupiedSinceLastRehash]]] and [[[occupiedCount]]] have been modified " @@ -354,6 +335,11 @@ public void testRehashInPut(int tableSize, int index, double resizeFactor, doubl )); } + private static MyIndexHoppingHashMap getHashFunction(int tableSize, int index, double resizeFactor, + double resizeThreshold) { + return new MyIndexHoppingHashMap<>(tableSize, resizeFactor, resizeThreshold, new BinaryFct2IntImpl(tableSize, index)); + } + private static class Provider implements ArgumentsProvider { @Override diff --git a/src/grader/java/h06/h5/MyDateTests.java b/src/grader/java/h06/h5/MyDateTests.java index 4d11d04..2676c13 100644 --- a/src/grader/java/h06/h5/MyDateTests.java +++ b/src/grader/java/h06/h5/MyDateTests.java @@ -156,25 +156,17 @@ private static int hashCodeRefImpl(Calendar date, boolean randomBoolean) { long coefficientHour = 1234L; long coefficientMinute = 99998L; -// return Math.floorMod( -// Math.floorMod(coefficientYear * year, (long) Integer.MAX_VALUE) -// + Math.floorMod(coefficientMonth * month, Integer.MAX_VALUE) -// + Math.floorMod(coefficientDay * day, Integer.MAX_VALUE) -// + Math.floorMod(coefficientHour * hour, Integer.MAX_VALUE) -// + Math.floorMod(coefficientMinute * minute, Integer.MAX_VALUE), -// Integer.MAX_VALUE); - return (int) Math.floorMod( - Math.floorMod((coefficientYear * year), (long)Integer.MAX_VALUE) - + Math.floorMod((coefficientMonth * month), Integer.MAX_VALUE) - + Math.floorMod((coefficientDay * day), Integer.MAX_VALUE) - + Math.floorMod((coefficientHour * hour), Integer.MAX_VALUE) - + Math.floorMod((coefficientMinute * minute), Integer.MAX_VALUE) - , Integer.MAX_VALUE); + return Math.floorMod( + Math.floorMod(coefficientYear * year, (long) Integer.MAX_VALUE) + + Math.floorMod(coefficientMonth * month, Integer.MAX_VALUE) + + Math.floorMod(coefficientDay * day, Integer.MAX_VALUE) + + Math.floorMod(coefficientHour * hour, Integer.MAX_VALUE) + + Math.floorMod(coefficientMinute * minute, Integer.MAX_VALUE), + Integer.MAX_VALUE); } else { long coefficientSum = 98924L; -// return Math.floorMod(((year + month + day + hour + minute) * coefficientSum), Integer.MAX_VALUE); - return (int)Math.floorMod(((year + month + day + hour + minute) * coefficientSum), Integer.MAX_VALUE); + return Math.floorMod(((year + month + day + hour + minute) * coefficientSum), Integer.MAX_VALUE); } } } diff --git a/src/grader/java/h06/h6/RuntimeTestTests.java b/src/grader/java/h06/h6/RuntimeTestTests.java index b17b82a..4eb99a5 100644 --- a/src/grader/java/h06/h6/RuntimeTestTests.java +++ b/src/grader/java/h06/h6/RuntimeTestTests.java @@ -13,21 +13,15 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.ArgumentsProvider; import org.sourcegrade.jagr.api.rubric.TestForSubmission; import org.sourcegrade.jagr.api.testing.extension.JagrExecutionCondition; import java.lang.reflect.Field; -import java.text.DateFormat; -import java.time.Instant; -import java.util.*; +import java.util.Arrays; +import java.util.Calendar; +import java.util.List; import java.util.function.Supplier; -import java.util.stream.Stream; -import static h06.Config.SEED; -import static h06.Config.STREAM_SIZE; import static org.junit.jupiter.api.Assertions.fail; @TestForSubmission("h06") @@ -134,18 +128,6 @@ public void testTest() { inputsSupplier); } - private static class Provider implements ArgumentsProvider { - - @Override - public Stream provideArguments(ExtensionContext context) { - Random random = new Random(SEED); - - return Stream.generate(() -> null) - .limit(STREAM_SIZE) - .map(Arguments::of); - } - } - private static class MyMapImpl implements MyMap { private final MyDate myDateReturnValue; private int containsKeyInvocationCounter = 0; diff --git a/src/grader/java/h06/mocks/Mock.java b/src/grader/java/h06/mocks/Mock.java deleted file mode 100644 index 45f1e0c..0000000 --- a/src/grader/java/h06/mocks/Mock.java +++ /dev/null @@ -1,49 +0,0 @@ -package h06.mocks; - -import h06.DoubleHashing; -import h06.Hash2IndexFct; -import h06.LinearProbing; -import h06.utils.ReflectionUtils; -import org.mockito.Answers; -import org.mockito.Mockito; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.function.BiConsumer; - -@SuppressWarnings("rawtypes") -public class Mock { - - public static final BiConsumer HASH_2_INDEX_FCT = (instance, args) -> { - Field tableSizeField = ReflectionUtils.getField(Hash2IndexFct.class, "tableSize"); - int tableSize = (int) args[0]; - ReflectionUtils.setFieldValue(tableSizeField, instance, tableSize); - - Field offsetField = ReflectionUtils.getField(Hash2IndexFct.class, "offset"); - int offset = (int) args[1]; - ReflectionUtils.setFieldValue(offsetField, instance, offset); - - Mockito.doAnswer(invocation -> { - Method calledMethod = invocation.getMethod(); - if (calledMethod.getName().equals("apply") - && calledMethod.getParameterCount() == 1 - && calledMethod.getParameterTypes()[0].equals(Object.class)) { - return Math.floorMod(Math.abs((long) invocation.getArgument(0).hashCode()) + offset, tableSize); - } else { - return invocation.callRealMethod(); - } - }); - }; - public static final BiConsumer LINEAR_PROBING = (instance, args) -> { - - }; - public static final BiConsumer DOUBLE_HASHING = (instance, args) -> { - - }; - - public static T getMock(Class clazz, BiConsumer constructorSurrogate, Object... constructorArgs) { - T t = Mockito.mock(clazz, Answers.CALLS_REAL_METHODS); - constructorSurrogate.accept(t, constructorArgs); - return t; - } -} diff --git a/src/grader/java/h06/transformers/Hash2IndexFctTransformer.java b/src/grader/java/h06/transformers/Hash2IndexFctTransformer.java deleted file mode 100644 index d6ef51a..0000000 --- a/src/grader/java/h06/transformers/Hash2IndexFctTransformer.java +++ /dev/null @@ -1,71 +0,0 @@ -package h06.transformers; - -import org.objectweb.asm.*; -import org.sourcegrade.jagr.api.testing.ClassTransformer; - -public class Hash2IndexFctTransformer implements ClassTransformer { - - public static boolean SURROGATE_ACTIVE = true; - - @Override - public String getName() { - return "Hash2IndexFctTransformer"; - } - - @Override - public void transform(ClassReader reader, ClassWriter writer) { - if (reader.getClassName().equals("h06/Hash2IndexFct")) { - reader.accept(new ClassTransformer(writer), ClassReader.SKIP_DEBUG); - } else { - reader.accept(writer, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); - } - } - - private static class ClassTransformer extends ClassVisitor { - - private ClassTransformer(ClassWriter classWriter) { - super(Opcodes.ASM9, classWriter); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { - if (name.equals("apply") && descriptor.equals("(Ljava/lang/Object;)I")) { - return new MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) { - final Label label = new Label(); - - @Override - public void visitCode() { - super.visitFieldInsn(Opcodes.GETSTATIC, - "h06/transformers/Hash2IndexFctTransformer", - "SURROGATE_ACTIVE", - "Z"); - super.visitJumpInsn(Opcodes.IFNE, label); - super.visitCode(); - } - - @Override - public void visitEnd() { - super.visitLabel(label); - super.visitFrame(Opcodes.F_FULL, 2, new Object[] {"h06/Hash2IndexFct", "java/lang/Object"}, 0, - new Object[0]); - super.visitFieldInsn(Opcodes.GETSTATIC, - "java/lang/System", - "out", - "Ljava/io/PrintStream;"); - super.visitLdcInsn("Hello world!"); - super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, - "java/io/PrintStream", - "println", - "(Ljava/lang/String;)V", - false); - super.visitInsn(Opcodes.ICONST_0); - super.visitInsn(Opcodes.IRETURN); - super.visitEnd(); - } - }; - } else { - return super.visitMethod(access, name, descriptor, signature, exceptions); - } - } - } -} diff --git a/src/grader/java/h06/utils/ReflectionUtils.java b/src/grader/java/h06/utils/ReflectionUtils.java index 72a6de5..0df8233 100644 --- a/src/grader/java/h06/utils/ReflectionUtils.java +++ b/src/grader/java/h06/utils/ReflectionUtils.java @@ -4,24 +4,14 @@ import java.lang.reflect.Field; import java.lang.reflect.InaccessibleObjectException; -import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; import java.util.stream.Stream; public class ReflectionUtils { private static final Map, Map> FIELD_CACHE = new HashMap<>(); - private static final Map, Map> METHOD_CACHE = new HashMap<>(); - - private static final Function GET_METHOD_SIGNATURE = method -> "%s(%s)".formatted(method.getName(), - Arrays.stream(method.getParameterTypes()) - .map(Class::getName) - .collect(Collectors.joining(", ")) - ); public static Field getField(Class clazz, String fieldName) { return FIELD_CACHE.computeIfAbsent(clazz, c -> new HashMap<>()) @@ -65,49 +55,4 @@ public static void setFieldValue(Field field, T instance, V value) { } } - public static Method getMethodByName(Class clazz, String methodName) { - Method[] matchingCachedMethods = METHOD_CACHE.computeIfAbsent(clazz, c -> new HashMap<>()) - .entrySet() - .stream() - .filter(entry -> entry.getKey().startsWith(methodName)) - .map(Map.Entry::getValue) - .toArray(Method[]::new); - - if (matchingCachedMethods.length == 0) { - Method[] methods = Stream.concat(Arrays.stream(clazz.getMethods()), Arrays.stream(clazz.getDeclaredMethods())) - .filter(method -> method.getName().equals(methodName)) - .toArray(Method[]::new); - - if (methods.length == 0) { - throw new AssertionFailedError("No method with name \"%s\" found in class %s".formatted(methodName, clazz.getName())); - } else if (methods.length > 1) { - throw new AssertionFailedError("Cannot resolve ambiguity: Found %d methods in class %s with name %s" - .formatted(methods.length, clazz.getName(), methodName)); - } else { - METHOD_CACHE.get(clazz).put(GET_METHOD_SIGNATURE.apply(methods[0]), methods[0]); - return methods[0]; - } - } else if (matchingCachedMethods.length > 1) { - throw new RuntimeException("Method name %s is ambiguous; found %d method with the same name".formatted(methodName, - matchingCachedMethods.length)); - } else { - return matchingCachedMethods[0]; - } - } - - public static Method getMethod(Class clazz, String methodName, Class... argumentTypes) { - String signature = "%s(%s)".formatted(methodName, - Arrays.stream(argumentTypes) - .map(Class::getName) - .collect(Collectors.joining(", ")) - ); - - return METHOD_CACHE.computeIfAbsent(clazz, c -> new HashMap<>()) - .computeIfAbsent(signature, s -> Stream.concat(Arrays.stream(clazz.getMethods()), - Arrays.stream(clazz.getDeclaredMethods())) - .filter(method -> method.getName().equals(methodName) && Arrays.equals(method.getParameterTypes(), argumentTypes)) - .findAny() - .orElseThrow(() -> new RuntimeException("Could not find any methods matching signature " + signature)) - ); - } } diff --git a/src/grader/resources/grader-info.json b/src/grader/resources/grader-info.json index a536d16..542da79 100644 --- a/src/grader/resources/grader-info.json +++ b/src/grader/resources/grader-info.json @@ -14,8 +14,6 @@ "h06/h4/MyListsHashMapTests.java", "h06/h5/MyDateTests.java", "h06/h6/RuntimeTestTests.java", - "h06/mocks/Mock.java", - "h06/transformers/Hash2IndexFctTransformer.java", "h06/transformers/MyDateTransformer.java", "h06/transformers/MyIndexHoppingHashMapTransformer.java", "h06/utils/ReflectionUtils.java",