diff --git a/README.md b/README.md index 1874d2c952ff47ea5c3358b9b699263f7b5d7de8..dc63023c9182a9e50b1f3de0409931a80965418c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Zu diesem Zweck werden wir Git und GitLab einsetzten. Sollten Sie mehr Informati In diesem Dokument finden Sie die folgenden Informationen: -- [Fachliche Projektbeschreibung](#fachliche-projectbeschreibung) +- [Fachliche Projektbeschreibung](#fachliche-projektbeschreibung) - [Technische Projektbeschreibung](#technische-projektbeschreibung) - [Hinweis zur Autorenschaft](#hinweis-zur-autorenschaft) diff --git a/src/main/java/de/hda/fbi/db2/api/Lab01Data.java b/src/main/java/de/hda/fbi/db2/api/Lab01Data.java index 4893b5f32f6daf9cbfd39f5bbfc3a1c9b092bffa..4cb716ca4090edd99f9c842148eb9a9fbcd2843a 100644 --- a/src/main/java/de/hda/fbi/db2/api/Lab01Data.java +++ b/src/main/java/de/hda/fbi/db2/api/Lab01Data.java @@ -25,9 +25,10 @@ public abstract class Lab01Data { public abstract List<?> getCategories(); /** - * Save the csv data in appropriate objects. + * Save the CSV data in appropriate objects. * - * @param additionalCsvLines is the csv data + * @param csvLines CSV lines, each line is a String array consisting of the columns of the line. + * The first line consists of the headers of the CSV columns. */ - public abstract void loadCsvFile(List<String[]> additionalCsvLines); + public abstract void loadCsvFile(List<String[]> csvLines); } diff --git a/src/main/java/de/hda/fbi/db2/api/Lab02EntityManager.java b/src/main/java/de/hda/fbi/db2/api/Lab02EntityManager.java index 990b54a68ce0292cd4051435c207eae3b4a004eb..a353624684b2114e19c5a560de072dc3610647f4 100644 --- a/src/main/java/de/hda/fbi/db2/api/Lab02EntityManager.java +++ b/src/main/java/de/hda/fbi/db2/api/Lab02EntityManager.java @@ -16,11 +16,11 @@ public abstract class Lab02EntityManager { protected Lab01Data lab01Data; /** - * Setter for Lab01Data; Don't overwrite, you will break the automation. + * Setter for Lab01Data. Do not touch this method. * * @param lab01Data lab01Data */ - public void setLab01Data(Lab01Data lab01Data) { + public final void setLab01Data(Lab01Data lab01Data) { this.lab01Data = lab01Data; } diff --git a/src/main/java/de/hda/fbi/db2/api/Lab03Game.java b/src/main/java/de/hda/fbi/db2/api/Lab03Game.java index a69ae81e465428ba742868adecc41279e49bfa74..316741a53ddd0ddfbef8d34e798b5130c4b8fb9a 100644 --- a/src/main/java/de/hda/fbi/db2/api/Lab03Game.java +++ b/src/main/java/de/hda/fbi/db2/api/Lab03Game.java @@ -26,16 +26,16 @@ public abstract class Lab03Game { * * @param lab01Data lab01Data */ - public void setLab01Data(Lab01Data lab01Data) { + public final void setLab01Data(Lab01Data lab01Data) { this.lab01Data = lab01Data; } /** - * Setter fro Lab02EntityManager. Do not touch this method. + * Setter for Lab02EntityManager. Do not touch this method. * * @param lab02EntityManager lab02EntityManager */ - public void setLab02EntityManager(Lab02EntityManager lab02EntityManager) { + public final void setLab02EntityManager(Lab02EntityManager lab02EntityManager) { this.lab02EntityManager = lab02EntityManager; } @@ -48,7 +48,7 @@ public abstract class Lab03Game { * </p> * * <p>This function is primarily used for testing. There exists a version with user interaction - * which shall be used from the menu + * which shall be used from the menu. * </p> * * @param playerName The name for the new Player. @@ -67,7 +67,7 @@ public abstract class Lab03Game { * </p> * * <p>This function is primarily used for user interaction. There exists a version used for - * testing, @see getOrCreatePlayer</p> + * testing, {@link #getOrCreatePlayer(String)}.</p> * * @return Player object which was created or retrieved. * @see Lab03Game#getOrCreatePlayer(String) @@ -116,7 +116,7 @@ public abstract class Lab03Game { * * @param player The Player which shall play the game. * @param questions The Questions which shall be asked during the game. - * @return A Game Object which contains an unplayed game + * @return A Game object which contains an unplayed game * for the given player with the given questions. */ public abstract Object createGame(Object player, List<?> questions); @@ -127,7 +127,7 @@ public abstract class Lab03Game { * <p>There is also an interactive version of this function which shall be called from the menu. * </p> * - * @param game The Game Object which shall be played. + * @param game The Game object which shall be played. * @see Lab03Game#interactivePlayGame(Object) */ public abstract void playGame(Object game); @@ -138,15 +138,15 @@ public abstract class Lab03Game { * <p>This is the function that should be called from the menu. Here you can implement the * necessary user interaction for the playing of the game.</p> * - * @param game The Game Object which shall be played. + * @param game The Game object which shall be played. * @see Lab03Game#playGame(Object) */ public abstract void interactivePlayGame(Object game); /** - * Persists a played game, including the player which played it. + * Persists a played game, including the player who played it. * - * @param game The Game Object to be persisted. + * @param game The Game object to be persisted. */ public abstract void persistGame(Object game); } diff --git a/src/main/java/de/hda/fbi/db2/api/Lab04MassData.java b/src/main/java/de/hda/fbi/db2/api/Lab04MassData.java index 7ec83528b7a2ff0e2265b111e6da714d4682d416..656899a0ac3c0998f32fa820f6d0064e653377f3 100644 --- a/src/main/java/de/hda/fbi/db2/api/Lab04MassData.java +++ b/src/main/java/de/hda/fbi/db2/api/Lab04MassData.java @@ -25,29 +25,29 @@ public abstract class Lab04MassData { protected Lab03Game lab03Game; /** - * Setter for Lab01Data. + * Setter for Lab01Data. Do not touch this method. * * @param lab01Data lab01Data */ - public void setLab01Data(Lab01Data lab01Data) { + public final void setLab01Data(Lab01Data lab01Data) { this.lab01Data = lab01Data; } /** - * Setter fro Lab02EntityManager. + * Setter for Lab02EntityManager. * * @param lab02EntityManager lab02EntityManager */ - public void setLab02EntityManager(Lab02EntityManager lab02EntityManager) { + public final void setLab02EntityManager(Lab02EntityManager lab02EntityManager) { this.lab02EntityManager = lab02EntityManager; } /** - * Setter fro lab03Game. + * Setter for lab03Game. * * @param lab03Game lab03Game */ - public void setLab03Game(Lab03Game lab03Game) { + public final void setLab03Game(Lab03Game lab03Game) { this.lab03Game = lab03Game; } diff --git a/src/main/java/de/hda/fbi/db2/api/package-info.java b/src/main/java/de/hda/fbi/db2/api/package-info.java index cb415459e259aa9ba613d2334056e5ef9426010b..41e09594edf0ecdab1b5f3a90fcbc0d9dc0d3b36 100644 --- a/src/main/java/de/hda/fbi/db2/api/package-info.java +++ b/src/main/java/de/hda/fbi/db2/api/package-info.java @@ -1,6 +1,4 @@ /** - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Package containing abstract classes to be implemented by students. */ package de.hda.fbi.db2.api; diff --git a/src/main/java/de/hda/fbi/db2/controller/Controller.java b/src/main/java/de/hda/fbi/db2/controller/Controller.java index a10654d1edb1eee911083b36169e59c7b67fd0c1..79909a38f15cab2ec89da7b919c2c3325d94b17e 100644 --- a/src/main/java/de/hda/fbi/db2/controller/Controller.java +++ b/src/main/java/de/hda/fbi/db2/controller/Controller.java @@ -6,6 +6,8 @@ import de.hda.fbi.db2.api.Lab03Game; import de.hda.fbi.db2.api.Lab04MassData; import java.io.File; import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; @@ -51,30 +53,50 @@ public class Controller { findImplementations(); } - @SuppressWarnings("deprecation") + private static <T> T createInstance(Class<?> c) throws Exception { + if (c.isInterface()) { + throw new IllegalArgumentException( + "Class " + c.getSimpleName() + " must not be an interface"); + } + if (Modifier.isAbstract(c.getModifiers())) { + throw new IllegalArgumentException("Class " + c.getSimpleName() + " must not be abstract"); + } + + Constructor<?> constructor; + try { + constructor = c.getConstructor(); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException( + "Class " + c.getSimpleName() + " has no default constructor", e); + } + + // Trust that the caller has verified class already + @SuppressWarnings("unchecked") + T result = (T) constructor.newInstance(); + return result; + } + private void findImplementations() { try { Enumeration<URL> elements = Thread.currentThread().getContextClassLoader() - .getResources(IMPL_PACKAGE_NAME.replace(".", "/")); + .getResources(IMPL_PACKAGE_NAME.replace('.', '/')); if (!elements.hasMoreElements()) { return; } - String test = elements.nextElement().getFile(); - List<Class> classes = new ArrayList<>(findClasses(new File(test), IMPL_PACKAGE_NAME)); - for (Class clazz : classes) { - Class interfaces = clazz.getSuperclass(); - if (interfaces != null) { - if (interfaces.getSimpleName().equals(Lab01Data.class.getSimpleName())) { - lab01Data = (Lab01Data) clazz.newInstance(); - } else if (interfaces.getSimpleName() - .equals(Lab02EntityManager.class.getSimpleName())) { - lab02EntityManager = (Lab02EntityManager) clazz.newInstance(); - } else if (interfaces.getSimpleName() - .equals(Lab03Game.class.getSimpleName())) { - lab03Game = (Lab03Game) clazz.newInstance(); - } else if (interfaces.getSimpleName() - .equals(Lab04MassData.class.getSimpleName())) { - lab04MassData = (Lab04MassData) clazz.newInstance(); + // Convert URL -> URI -> File to properly handle special characters or spaces in file path + File directory = new File(elements.nextElement().toURI()); + List<Class<?>> classes = new ArrayList<>(findClasses(directory, IMPL_PACKAGE_NAME)); + for (Class<?> clazz : classes) { + Class<?> superclass = clazz.getSuperclass(); + if (superclass != null) { + if (superclass == Lab01Data.class) { + lab01Data = createInstance(clazz); + } else if (superclass == Lab02EntityManager.class) { + lab02EntityManager = createInstance(clazz); + } else if (superclass == Lab03Game.class) { + lab03Game = createInstance(clazz); + } else if (superclass == Lab04MassData.class) { + lab04MassData = createInstance(clazz); } } } @@ -105,13 +127,13 @@ public class Controller { lab04MassData.setLab03Game(lab03Game); lab04MassData.init(); } catch (Exception e) { - e.printStackTrace(); + throw new IllegalStateException("API classes are not implemented correctly", e); } } - private List<Class> findClasses(File directory, String packageName) + private List<Class<?>> findClasses(File directory, String packageName) throws ClassNotFoundException { - List<Class> classes = new ArrayList<>(); + List<Class<?>> classes = new ArrayList<>(); if (!directory.exists()) { return classes; } @@ -144,7 +166,7 @@ public class Controller { lab01Data.loadCsvFile(additionalCsvLines); } } catch (URISyntaxException | IOException e) { - e.printStackTrace(); + throw new RuntimeException("Failed reading CSV data", e); } isCsvRead = true; } diff --git a/src/main/java/de/hda/fbi/db2/controller/CsvDataReader.java b/src/main/java/de/hda/fbi/db2/controller/CsvDataReader.java index e41ec7163b8e1836fac043fc7c1ff30fe6528675..2c89531b8f7a4a15bd2e93c65ef58041ab970e83 100644 --- a/src/main/java/de/hda/fbi/db2/controller/CsvDataReader.java +++ b/src/main/java/de/hda/fbi/db2/controller/CsvDataReader.java @@ -1,10 +1,13 @@ package de.hda.fbi.db2.controller; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URISyntaxException; import java.net.URL; -import java.nio.file.Files; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; @@ -25,7 +28,7 @@ public class CsvDataReader { /** * Lists all available csv files in the resource folder. - * <strong>WARNING:</strong> May won't work on your machine + * <strong>WARNING:</strong> May not work on your machine * * @return list of csv filenames * @throws IOException ioException @@ -45,8 +48,8 @@ public class CsvDataReader { fileList = Arrays.stream(filenames) .filter(File::isFile) - .filter(file -> file.getName().endsWith(".csv")) .map(File::getName) + .filter(name -> name.endsWith(".csv")) .collect(Collectors.toList()); } @@ -56,51 +59,50 @@ public class CsvDataReader { /** * Reads the given embedded file and returns its content in an accessible form. * - * @param otherFile filename of csv file in resource folder + * @param resourceName filename of CSV file in resource folder * @return content of the related file as a list of split strings including the CSV-header at * first position. - * @throws URISyntaxException uRISyntaxException + * @throws URISyntaxException uriSyntaxException * @throws IOException iOException */ - public static List<String[]> read(String otherFile) throws IOException, URISyntaxException { + public static List<String[]> read(String resourceName) throws IOException, URISyntaxException { try { - if (!getAvailableFiles().contains(otherFile)) { + if (!getAvailableFiles().contains(resourceName)) { throw new IOException("File not found in Resources."); } } catch (IOException ioe) { throw ioe; } catch (Exception e) { - log.warning("CsvDataReader.getAvailableFiles() threw a exception." + log.warning("CsvDataReader.getAvailableFiles() threw an exception." + " Skipping file verification."); } - final URL resource = CsvDataReader.class.getResource("/" + otherFile); - if (resource == null) { - throw new IllegalStateException("Unable to find the csv file."); - } - return readFile(new File(resource.toURI())); + return readCsvResource("/" + resourceName); } /** - * Reads the embedded <code>Wissenstest_sample200.csv</code> file and returns its content in an + * Reads the embedded {@code Wissenstest_sample200.csv} file and returns its content in an * accessible form. * * @return content of the related file as a list of split strings including the CSV-header at * first position. - * @throws URISyntaxException uRISyntaxException + * @throws URISyntaxException uriSyntaxException * @throws IOException iOException */ public static List<String[]> read() throws URISyntaxException, IOException { - final URL resource = CsvDataReader.class.getResource("/Wissenstest_sample200.csv"); - if (resource == null) { - throw new IllegalStateException("Unable to find the csv file."); - } - return readFile(new File(resource.toURI())); + return readCsvResource("/Wissenstest_sample200.csv"); } - private static List<String[]> readFile(File file) throws IOException { - final List<String> lines = Files.readAllLines(file.toPath()); - return lines.stream() - .map((line) -> line.split(SPLIT_CHARACTER)) - .collect(Collectors.toList()); + private static List<String[]> readCsvResource(String resourcePath) throws IOException { + InputStream inputStream = CsvDataReader.class.getResourceAsStream(resourcePath); + if (inputStream == null) { + throw new IllegalArgumentException("Resource '" + resourcePath + "' does not exist"); + } + try (inputStream; BufferedReader reader = new BufferedReader( + new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + + return reader.lines() + .map(line -> line.split(SPLIT_CHARACTER)) + .collect(Collectors.toList()); + } } } diff --git a/src/main/java/de/hda/fbi/db2/controller/MenuController.java b/src/main/java/de/hda/fbi/db2/controller/MenuController.java index 5d141b1cc6be919c97b5e6259b5da5bd55e991f2..64f00a080476154e48eac6d69f44a21a452ac2c0 100644 --- a/src/main/java/de/hda/fbi/db2/controller/MenuController.java +++ b/src/main/java/de/hda/fbi/db2/controller/MenuController.java @@ -11,7 +11,7 @@ import java.util.List; */ public class MenuController { - private Controller controller; + private final Controller controller; public MenuController(Controller controller) { this.controller = controller; diff --git a/src/main/java/de/hda/fbi/db2/controller/package-info.java b/src/main/java/de/hda/fbi/db2/controller/package-info.java index e404848f963fdb5340a4ba87b3d55311a0c55297..53f47bd79306fec36b99c75f5bac4918cadf1dcf 100644 --- a/src/main/java/de/hda/fbi/db2/controller/package-info.java +++ b/src/main/java/de/hda/fbi/db2/controller/package-info.java @@ -1,6 +1,6 @@ /** - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Package containing internal implementation of this lab project. + * + * <p><b>MUST NOT BE CHANGED BY STUDENTS</b></p> */ package de.hda.fbi.db2.controller; diff --git a/src/main/java/de/hda/fbi/db2/package-info.java b/src/main/java/de/hda/fbi/db2/package-info.java deleted file mode 100644 index e1f0f0808efc6f88447dc024e09e67cca10f8199..0000000000000000000000000000000000000000 --- a/src/main/java/de/hda/fbi/db2/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package de.hda.fbi.db2; diff --git a/src/main/java/de/hda/fbi/db2/stud/entity/package-info.java b/src/main/java/de/hda/fbi/db2/stud/entity/package-info.java index cb077ff5dc7d8c0c06075d47cc2deda38d5f78ad..95017d7df8100a976fbcc11d09b100ed2357368b 100644 --- a/src/main/java/de/hda/fbi/db2/stud/entity/package-info.java +++ b/src/main/java/de/hda/fbi/db2/stud/entity/package-info.java @@ -1,6 +1,4 @@ /** - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Package for student defined entity classes. */ package de.hda.fbi.db2.stud.entity; diff --git a/src/main/java/de/hda/fbi/db2/stud/impl/package-info.java b/src/main/java/de/hda/fbi/db2/stud/impl/package-info.java index a1c9d39cf003e8b59f4fadcadb166574a35f9454..5e5347885960e8ace7193471c1fccd4d8efff7d0 100644 --- a/src/main/java/de/hda/fbi/db2/stud/impl/package-info.java +++ b/src/main/java/de/hda/fbi/db2/stud/impl/package-info.java @@ -1,6 +1,4 @@ /** - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Package for student defined implementation logic. */ package de.hda.fbi.db2.stud.impl; diff --git a/src/test/java/de/hda/fbi/db2/test/Lab01Test.java b/src/test/java/de/hda/fbi/db2/test/Lab01Test.java index 0a9b63213d9b5de45891fa435bdd126de32c6471..738251eb66bc7d7a96072b8b292fc11e5b2abd46 100644 --- a/src/test/java/de/hda/fbi/db2/test/Lab01Test.java +++ b/src/test/java/de/hda/fbi/db2/test/Lab01Test.java @@ -1,9 +1,13 @@ package de.hda.fbi.db2.test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import de.hda.fbi.db2.api.Lab01Data; import de.hda.fbi.db2.controller.Controller; -import org.junit.Assert; +import java.util.List; import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; @@ -23,10 +27,17 @@ public class Lab01Test { @BeforeClass public static void init() { controller = Controller.getInstance(); - if (controller.getLab01Data() == null) { - Assert.fail("Could not find Lab01Data Implementation"); - System.exit(-1); + Lab01Data impl = controller.getLab01Data(); + if (impl == null) { + fail("Could not find Lab01Data implementation"); } + + String expectedPackage = "de.hda.fbi.db2.stud.impl"; + // Check startsWith to also allow subpackages + if (!impl.getClass().getName().startsWith(expectedPackage + '.')) { + fail("Implementation class should be in package " + expectedPackage); + } + if (!controller.isCsvRead()) { controller.readCsv(); } @@ -34,27 +45,41 @@ public class Lab01Test { @Test public void test1CategorySize() { - assertEquals("There should be 51 categories", 51, - controller.getLab01Data().getCategories().size()); + List<?> categories = controller.getLab01Data().getCategories(); + assertNotNull("Categories must not be null", categories); + assertEquals("There should be 51 categories", 51, categories.size()); } @Test public void test2QuestionSize() { - assertEquals("There should be 200 questions", 200, - controller.getLab01Data().getQuestions().size()); + List<?> questions = controller.getLab01Data().getQuestions(); + assertNotNull("Questions must not be null", questions); + assertEquals("There should be 200 questions", 200, questions.size()); } @Test public void test3CategoryObject() { - Object testObject = controller.getLab01Data().getCategories().get(0); - assertEquals("Category object should be named 'Category'", "Category", + List<?> categories = controller.getLab01Data().getCategories(); + assertNotNull("Categories must not be null", categories); + assertFalse("Categories must not be empty", categories.isEmpty()); + + Object testObject = categories.get(0); + assertEquals("Category class should be named 'Category'", "Category", testObject.getClass().getSimpleName()); + assertEquals("Category class should be in correct package", "de.hda.fbi.db2.stud.entity", + testObject.getClass().getPackageName()); } @Test public void test4QuestionObject() { - Object testObject = controller.getLab01Data().getQuestions().get(0); - assertEquals("Question object should be named 'Question'", "Question", + List<?> questions = controller.getLab01Data().getQuestions(); + assertNotNull("Questions must not be null", questions); + assertFalse("Questions must not be empty", questions.isEmpty()); + + Object testObject = questions.get(0); + assertEquals("Question class should be named 'Question'", "Question", testObject.getClass().getSimpleName()); + assertEquals("Question class should be in correct package", "de.hda.fbi.db2.stud.entity", + testObject.getClass().getPackageName()); } } diff --git a/src/test/java/de/hda/fbi/db2/test/Lab02Test.java b/src/test/java/de/hda/fbi/db2/test/Lab02Test.java index 0160eca76b9923fce2c9c095eac99efc337c6de3..791e9983c92ec7b996099b6520786364ad4e370a 100644 --- a/src/test/java/de/hda/fbi/db2/test/Lab02Test.java +++ b/src/test/java/de/hda/fbi/db2/test/Lab02Test.java @@ -1,20 +1,27 @@ package de.hda.fbi.db2.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeNotNull; + +import de.hda.fbi.db2.api.Lab02EntityManager; import de.hda.fbi.db2.controller.Controller; -import java.lang.reflect.Method; +import java.util.List; +import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.Metamodel; import javax.persistence.metamodel.PluralAttribute; import javax.persistence.metamodel.SingularAttribute; import javax.persistence.metamodel.Type; import org.eclipse.persistence.internal.jpa.metamodel.AttributeImpl; -import org.eclipse.persistence.internal.jpa.metamodel.EntityTypeImpl; import org.eclipse.persistence.internal.jpa.metamodel.SingularAttributeImpl; import org.eclipse.persistence.mappings.AggregateCollectionMapping; import org.eclipse.persistence.mappings.DatabaseMapping; import org.eclipse.persistence.mappings.DirectCollectionMapping; import org.eclipse.persistence.mappings.DirectMapMapping; -import org.junit.Assert; import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; @@ -28,11 +35,11 @@ public class Lab02Test { private static Metamodel metaData; - private static EntityTypeImpl categoryEntity; + private static EntityType<?> categoryEntity; - private static EntityTypeImpl questionEntity; + private static EntityType<?> questionEntity; - private static EntityTypeImpl answerEntity; + private static EntityType<?> answerEntity; private static Controller controller; @@ -40,15 +47,15 @@ public class Lab02Test { Lab02Test.metaData = metaData; } - private static synchronized void setCategoryEntity(EntityTypeImpl categoryEntity) { + private static synchronized void setCategoryEntity(EntityType<?> categoryEntity) { Lab02Test.categoryEntity = categoryEntity; } - private static synchronized void setQuestionEntity(EntityTypeImpl questionEntity) { + private static synchronized void setQuestionEntity(EntityType<?> questionEntity) { Lab02Test.questionEntity = questionEntity; } - private static synchronized void setAnswerEntity(EntityTypeImpl answerEntity) { + private static synchronized void setAnswerEntity(EntityType<?> answerEntity) { Lab02Test.answerEntity = answerEntity; } @@ -58,13 +65,18 @@ public class Lab02Test { @BeforeClass public static void init() { controller = Controller.getInstance(); - if (controller.getLab01Data() == null) { - Assert.fail("Could not find Lab01Data Implementation"); - System.exit(-1); + // Skip test class if students have not implemented lab01 yet + assumeNotNull(controller.getLab01Data()); + + Lab02EntityManager impl = controller.getLab02EntityManager(); + if (impl == null) { + fail("Could not find Lab02EntityManager implementation"); } - if (controller.getLab02EntityManager() == null) { - Assert.fail("Could not find Lab02EntityManager Implementation"); - System.exit(-1); + + String expectedPackage = "de.hda.fbi.db2.stud.impl"; + // Check startsWith to also allow subpackages + if (!impl.getClass().getName().startsWith(expectedPackage + '.')) { + fail("Implementation class should be in package " + expectedPackage); } if (!controller.isCsvRead()) { @@ -80,56 +92,72 @@ public class Lab02Test { public void test1EntityManager() { try { if (controller.getLab02EntityManager().getEntityManager() == null) { - Assert.fail("Lab02EntityManager.getEntityManager() returns null"); + fail("Lab02EntityManager.getEntityManager() returns null"); } setMetaData(controller.getLab02EntityManager().getEntityManager().getMetamodel()); } catch (Exception e) { - Assert.fail("Exception during entityManager creation"); + fail("Exception during entityManager creation"); } } @Test public void test2FindCategory() { if (metaData == null) { - Assert.fail("No MetaModel"); + fail("No MetaModel"); } - for (EntityType current : metaData.getEntities()) { - if (current.getName().toLowerCase().equals("category")) { - setCategoryEntity((EntityTypeImpl) current); + for (EntityType<?> current : metaData.getEntities()) { + if (current.getName().equalsIgnoreCase("category")) { + setCategoryEntity(current); + + List<?> categories = controller.getLab01Data().getCategories(); + if (categories != null && !categories.isEmpty()) { + Class<?> javaType = current.getJavaType(); + assertSame("Category class used by Lab01Data should be same as entity type", + categories.get(0).getClass(), javaType); + } + return; } } - Assert.fail("Could not find category entity"); + fail("Could not find Category entity"); } @Test public void test3FindQuestion() { if (metaData == null) { - Assert.fail("No MetaModel"); + fail("No MetaModel"); } - for (EntityType current : metaData.getEntities()) { - if (current.getName().toLowerCase().equals("question")) { - setQuestionEntity((EntityTypeImpl) current); + for (EntityType<?> current : metaData.getEntities()) { + if (current.getName().equalsIgnoreCase("question")) { + setQuestionEntity(current); + + List<?> questions = controller.getLab01Data().getQuestions(); + if (questions != null && !questions.isEmpty()) { + Class<?> javaType = current.getJavaType(); + assertSame("Question class used by Lab01Data should be same as entity type", + questions.get(0).getClass(), javaType); + } + return; } } - Assert.fail("Could not find question entity"); + fail("Could not find Question entity"); } @Test public void test4FindAnswer() { if (metaData == null) { - Assert.fail("No MetaModel"); + fail("No MetaModel"); } if (questionEntity == null) { - Assert.fail("Could not find questionEntity"); + fail("Could not find questionEntity"); } - for (Object member : questionEntity.getAttributes()) { - DatabaseMapping mapping = ((AttributeImpl) member).getMapping(); + for (Attribute<?, ?> member : questionEntity.getAttributes()) { + DatabaseMapping mapping = ((AttributeImpl<?, ?>) member).getMapping(); if (mapping instanceof DirectMapMapping) { return; } @@ -140,46 +168,46 @@ public class Lab02Test { return; } } - Assert.fail("Could not find a possible answer constellation in question"); + fail("Could not find a possible answer constellation in Question"); } @Test public void test5AnswerStringsInQuestion() { if (metaData == null) { - Assert.fail("No MetaModel"); + fail("No MetaModel"); } if (questionEntity == null) { - Assert.fail("Could not find questionEntity"); + fail("Could not find questionEntity"); } int stringCount = 0; for (Object member : questionEntity.getAttributes()) { if (member instanceof SingularAttribute) { - if (((SingularAttribute) member).getType().getJavaType() == String.class) { + if (((SingularAttribute<?, ?>) member).getType().getJavaType() == String.class) { stringCount = stringCount + 1; } } } if (stringCount > 2) { - Assert.fail("Found more then 2 Strings in Question!"); + fail("Found more then 2 Strings in Question!"); } } @Test public void test6QuestionInCategory() { if (metaData == null) { - Assert.fail("No MetaModel"); + fail("No MetaModel"); } if (questionEntity == null || categoryEntity == null) { - Assert.fail("Could not find questionEntity or categoryEntity"); + fail("Could not find questionEntity or categoryEntity"); } for (Object member : categoryEntity.getAttributes()) { try { - Type type = ((PluralAttribute) member).getElementType(); + Type<?> type = ((PluralAttribute<?, ?, ?>) member).getElementType(); if (type == questionEntity) { return; } @@ -188,115 +216,124 @@ public class Lab02Test { } } - Assert.fail("Could not find questions in category"); + fail("Could not find questions in category"); + } + + private static boolean hasEqualsMethod(Class<?> c) { + try { + c.getDeclaredMethod("equals", Object.class); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } + + // Explicitly want to test `equals` implementation; ignore IntelliJ warnings about `equals` misuse + @SuppressWarnings({"SimplifiableAssertion", "EqualsWithItself", "ConstantConditions"}) + private static void assertEqualsImplementation(Object a, Object b) { + String className = a.getClass().getSimpleName(); + assertTrue(className + " equals method should return true for `this`", a.equals(a)); + assertFalse(className + " equals method should return false for `null`", + a.equals(null)); + assertFalse(className + " equals method should return false for different objects", + a.equals(b)); } @Test - public void test7EqualsFunction() { + public void test7EqualsMethod() { if (metaData == null) { - Assert.fail("No MetaModel"); + fail("No MetaModel"); } if (questionEntity == null || categoryEntity == null) { - Assert.fail("Could not find questionEntity or categoryEntity"); + fail("Could not find questionEntity or categoryEntity"); } - boolean foundEquals = false; - for (Method method : questionEntity.getJavaType().getMethods()) { - if (method.getName().equals("equals") && !method.getDeclaringClass().getSimpleName() - .equals("Object")) { - foundEquals = true; - break; - } + if (!hasEqualsMethod(questionEntity.getJavaType())) { + fail("Could not find equals method in Question entity"); } - if (!foundEquals) { - Assert.fail("Could not find equals method in question entity"); - } + List<?> questions = controller.getLab01Data().getQuestions(); + Object question1 = questions.get(0); + Object question2 = questions.get(1); + assertEqualsImplementation(question1, question2); - foundEquals = false; - for (Method method : categoryEntity.getJavaType().getMethods()) { - if (method.getName().equals("equals") && !method.getDeclaringClass().getSimpleName() - .equals("Object")) { - foundEquals = true; - break; - } + if (!hasEqualsMethod(categoryEntity.getJavaType())) { + fail("Could not find equals method in Category entity"); } - if (!foundEquals) { - Assert.fail("Could not find equals method in category entity"); - } + List<?> categories = controller.getLab01Data().getCategories(); + Object category1 = categories.get(0); + Object category2 = categories.get(1); + assertEqualsImplementation(category1, category2); if (answerEntity != null) { - foundEquals = false; - for (Method method : answerEntity.getJavaType().getMethods()) { - if (method.getName().equals("equals") && !method.getDeclaringClass().getSimpleName() - .equals("Object")) { - foundEquals = true; - break; - } + if (!hasEqualsMethod(answerEntity.getJavaType())) { + fail("Could not find equals method in Answer entity"); } + } + } + + private static boolean hasHashCodeMethod(Class<?> c) { + try { + c.getDeclaredMethod("hashCode"); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } - if (!foundEquals) { - Assert.fail("Could not find equals method in answer entity"); + private static void assertHashCodeImplementation(List<?> objects) { + assertFalse(objects.isEmpty()); + + Object obj = objects.get(0); + String className = obj.getClass().getSimpleName(); + assertEquals(className + " hashCode method should return consistent results", + obj.hashCode(), obj.hashCode()); + + // Check if student implementation just calls `super.hashCode()` + // Because super.hashCode() could coincidentally be the same as properly implemented hashCode() + // reduce likelihood for false positive test failure by checking all objects + boolean differsFromIdentityHashCode = false; + for (Object o : objects) { + if (o.hashCode() != System.identityHashCode(o)) { + differsFromIdentityHashCode = true; + break; } } - Object question1 = controller.getLab01Data().getQuestions().get(0); - Object question2 = controller.getLab01Data().getQuestions().get(1); - if (question1.equals(question2)) { - Assert.fail("Two different questions should be equal"); + if (!differsFromIdentityHashCode) { + fail(className + " hashCode method seems to call super.hashCode()"); } } @Test - public void test8HashCodeFunction() { + public void test8HashCodeMethod() { if (metaData == null) { - Assert.fail("No MetaModel"); + fail("No MetaModel"); } if (questionEntity == null || categoryEntity == null) { - Assert.fail("Could not find questionEntity or categoryEntity"); + fail("Could not find questionEntity or categoryEntity"); } - boolean foundEquals = false; - for (Method method : questionEntity.getJavaType().getMethods()) { - if (method.getName().equals("hashCode") && !method.getDeclaringClass().getSimpleName() - .equals("Object")) { - foundEquals = true; - break; - } + if (!hasHashCodeMethod(questionEntity.getJavaType())) { + fail("Could not find hashCode method in Question entity"); } - if (!foundEquals) { - Assert.fail("Could not find hashCode method in question entity"); - } + List<?> questions = controller.getLab01Data().getQuestions(); + assertHashCodeImplementation(questions); - foundEquals = false; - for (Method method : categoryEntity.getJavaType().getMethods()) { - if (method.getName().equals("hashCode") && !method.getDeclaringClass().getSimpleName() - .equals("Object")) { - foundEquals = true; - break; - } + if (!hasHashCodeMethod(categoryEntity.getJavaType())) { + fail("Could not find hashCode method in Category entity"); } - if (!foundEquals) { - Assert.fail("Could not find hashCode method in category entity"); - } + List<?> categories = controller.getLab01Data().getCategories(); + assertHashCodeImplementation(categories); if (answerEntity != null) { - foundEquals = false; - for (Method method : answerEntity.getJavaType().getMethods()) { - if (method.getName().equals("hashCode") && !method.getDeclaringClass().getSimpleName() - .equals("Object")) { - foundEquals = true; - break; - } - } - - if (!foundEquals) { - Assert.fail("Could not find hashCode method in answer entity"); + if (!hasHashCodeMethod(answerEntity.getJavaType())) { + fail("Could not find hashCode method in Answer entity"); } } } @@ -304,21 +341,21 @@ public class Lab02Test { @Test public void test9CategoryNameUnique() { if (metaData == null) { - Assert.fail("No MetaModel"); + fail("No MetaModel"); } if (categoryEntity == null) { - Assert.fail("Could not find categoryEntity"); + fail("Could not find categoryEntity"); } - for (Object member : categoryEntity.getAttributes()) { - System.out.println(); + for (Attribute<?, ?> member : categoryEntity.getAttributes()) { try { - String name = ((SingularAttributeImpl) member).getName(); + SingularAttributeImpl<?, ?> attribute = (SingularAttributeImpl<?, ?>) member; + String name = attribute.getName(); if (name.equals("name")) { - boolean isUnique = ((SingularAttributeImpl) member).getMapping().getField().isUnique(); + boolean isUnique = attribute.getMapping().getField().isUnique(); if (!isUnique) { - Assert.fail("Name in Category is not unique"); + fail("Attribute 'name' in Category is not unique"); } else { return; } @@ -327,6 +364,6 @@ public class Lab02Test { //This is expected } } - Assert.fail("Could not find name in category"); + fail("Could not find attribute 'name' in Category"); } } diff --git a/src/test/java/de/hda/fbi/db2/test/Lab03Test.java b/src/test/java/de/hda/fbi/db2/test/Lab03Test.java index 2456633e3ec3ee99ff31d6678419ed394ec465e9..7f77dc49c2869e5d15e9dcc475a7a50902a24605 100644 --- a/src/test/java/de/hda/fbi/db2/test/Lab03Test.java +++ b/src/test/java/de/hda/fbi/db2/test/Lab03Test.java @@ -1,5 +1,11 @@ package de.hda.fbi.db2.test; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeNotNull; + import de.hda.fbi.db2.api.Lab01Data; import de.hda.fbi.db2.api.Lab03Game; import de.hda.fbi.db2.controller.Controller; @@ -7,7 +13,6 @@ import java.util.ArrayList; import java.util.List; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.Metamodel; -import org.junit.Assert; import org.junit.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Test; @@ -35,17 +40,18 @@ public class Lab03Test { @BeforeClass public static void init() { controller = Controller.getInstance(); - if (controller.getLab01Data() == null) { - Assert.fail("Could not find Lab01Data Implementation"); - System.exit(-1); - } - if (controller.getLab02EntityManager() == null) { - Assert.fail("Could not find Lab02EntityManager Implementation"); - System.exit(-1); + // Skip test class if students have not implemented lab01 or lab02 yet + assumeNotNull(controller.getLab01Data(), controller.getLab02EntityManager()); + + Lab03Game impl = controller.getLab03Game(); + if (impl == null) { + fail("Could not find Lab02EntityManager implementation"); } - if (controller.getLab03Game() == null) { - Assert.fail("Could not find Lab03Game Implementation"); - System.exit(-1); + + String expectedPackage = "de.hda.fbi.db2.stud.impl"; + // Check startsWith to also allow subpackages + if (!impl.getClass().getName().startsWith(expectedPackage + '.')) { + fail("Implementation class should be in package " + expectedPackage); } if (!controller.isCsvRead()) { @@ -58,18 +64,18 @@ public class Lab03Test { try { if (controller.getLab02EntityManager().getEntityManager() == null) { - Assert.fail("Lab02EntityManager.getEntityManager() returns null"); + fail("Lab02EntityManager.getEntityManager() returns null"); } metaData = controller.getLab02EntityManager().getEntityManager().getMetamodel(); } catch (Exception e) { - Assert.fail("Exception during entityManager creation"); + fail("Exception during entityManager creation"); } } @Test public void test1Functionality() { if (metaData == null) { - Assert.fail("No MetaModel"); + fail("No MetaModel"); } Lab03Game gameController = controller.getLab03Game(); @@ -77,9 +83,16 @@ public class Lab03Test { List<Object> categories = new ArrayList<>(); categories.add(lab01Data.getCategories().get(0)); categories.add(lab01Data.getCategories().get(1)); + List<?> questions = gameController.getQuestions(categories, 2); + assertNotNull(questions); + assertFalse("Questions for categories should not be empty", questions.isEmpty()); + assertTrue("Should at most return 2 questions", questions.size() <= 2); + Object player = gameController.getOrCreatePlayer("PlayerName"); + assertNotNull(player); Object game = gameController.createGame(player, questions); + assertNotNull(game); gameController.playGame(game); gameController.persistGame(game); } @@ -87,7 +100,7 @@ public class Lab03Test { @Test public void test2FindGameEntity() { if (metaData == null) { - Assert.fail("No MetaModel"); + fail("No MetaModel"); } Lab03Game gameController = controller.getLab03Game(); @@ -95,41 +108,49 @@ public class Lab03Test { List<Object> categories = new ArrayList<>(); categories.add(lab01Data.getCategories().get(0)); categories.add(lab01Data.getCategories().get(1)); + List<?> questions = gameController.getQuestions(categories, 2); + assertNotNull(questions); + assertFalse("Questions for categories should not be empty", questions.isEmpty()); + assertTrue("Should at most return 2 questions", questions.size() <= 2); + Object player = gameController.getOrCreatePlayer("PlayerName"); + assertNotNull(player); Object game = gameController.createGame(player, questions); + assertNotNull(game); boolean gameFound = false; - for (EntityType<?> clazzes : metaData.getEntities()) { - if (clazzes.getJavaType() == game.getClass()) { + for (EntityType<?> classes : metaData.getEntities()) { + if (classes.getJavaType() == game.getClass()) { gameFound = true; - setGameEntity(clazzes); + setGameEntity(classes); } } if (!gameFound) { - Assert.fail("Could not find game class as entity"); + fail("Could not find Game class as entity"); } } @Test public void test3FindPlayerEntity() { if (metaData == null) { - Assert.fail("No MetaModel"); + fail("No MetaModel"); } Lab03Game gameController = controller.getLab03Game(); Object player = gameController.getOrCreatePlayer("PlayerName"); + assertNotNull(player); boolean playerFound = false; - for (EntityType<?> clazzes : metaData.getEntities()) { - if (clazzes.getJavaType() == player.getClass()) { + for (EntityType<?> classes : metaData.getEntities()) { + if (classes.getJavaType() == player.getClass()) { playerFound = true; } } if (!playerFound) { - Assert.fail("Could not find player class as entity"); + fail("Could not find Player class as entity"); } } } diff --git a/src/test/java/de/hda/fbi/db2/test/package-info.java b/src/test/java/de/hda/fbi/db2/test/package-info.java deleted file mode 100644 index 64e0bad0e6281968eef415c15701bba933bf151d..0000000000000000000000000000000000000000 --- a/src/test/java/de/hda/fbi/db2/test/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package de.hda.fbi.db2.test;