From 1cc696a7921bf8279ba55ea735bc84dda20ef2de Mon Sep 17 00:00:00 2001 From: Torstein Stromme <torstein.stromme@uib.no> Date: Sun, 12 Feb 2023 01:49:08 +0100 Subject: [PATCH] test-design med refleksjon kun i wrapper-klasser --- README.md | 11 +- .../uib/inf101/colorgrid/TestColorGrid.java | 100 +++++++------ .../no/uib/inf101/colorgrid/WColorGrid.java | 101 +++++++++++++ .../uib/inf101/gridview/RecordGraphics2D.java | 27 ++-- .../TestCellPositionToPixelConverter.java | 128 ++-------------- .../no/uib/inf101/gridview/TestGridView.java | 140 +++++------------- .../WCellPositionToPixelConverter.java | 86 +++++++++++ .../no/uib/inf101/gridview/WGridView.java | 117 +++++++++++++++ 8 files changed, 427 insertions(+), 283 deletions(-) create mode 100644 src/test/java/no/uib/inf101/colorgrid/WColorGrid.java create mode 100644 src/test/java/no/uib/inf101/gridview/WCellPositionToPixelConverter.java create mode 100644 src/test/java/no/uib/inf101/gridview/WGridView.java diff --git a/README.md b/README.md index 4feeca9..d6607c4 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ I denne lab'en skal vi tegne et rutenett som vist over. Oppgaven består i hoved * Laben antar at du har kjennskap til grunnleggende java, inkludert [grensesnitt](https://inf101.ii.uib.no/notat/grensesnitt/), [klasser og objekter](https://inf101.ii.uib.no/notat/objekter/). * Du bør ha skummet igjennom kursnotatene om [arv](https://inf101.ii.uib.no/notat/arv), men vi kommer ikke til å gå i dypden på dette i denne lab'en. -* Kursnotatene om [grafikk](https://inf101.ii.uib.no/notat/grafikk) vil være spesielt relevante. Kan brukes som et oppslagsverk for å finne ut hvordan noe kan tegnes. +* I den utleverte koden dukker det opp to *record*-klasser. Du trenger ikke sette deg inn i mutabilitet for denne lab'en, men kursnotatene om dette inneholder også et [avsnitt om record-klasser](https://inf101.ii.uib.no/notat/mutabilitet/#record) hvor det er verdt å skumme gjennom eksempelet som demonstrerer bruken. +* Kursnotatene om [grafikk](https://inf101.ii.uib.no/notat/grafikk) vil naturligvis være spesielt relevante, primært de tre første avsnittene om rammeverket, koordinatsystemet og grunnleggende figurer; men også hjelpemetoder, farger og adaptiv tegning blir adressert i lab'en. ## Bli kjent med utlevert kode @@ -114,16 +115,16 @@ Disse siste delene med informasjon vil ikke endre seg særlig fra kall til kall, I klassen `CellPositionToPixelConverter`: -* [ ] Opprett feltvariabler: +* [ ] Opprett instansvariabler: * Et `Rectangle2D` -objekt `box` som beskriver innenfor hvilket område rutenettet skal tegnes * Et `GridDimension` -objekt `gd` som beskriver størrelsen til rutenettet rutene vil være en del av * En `double` kalt `margin` som beskriver hvor stor avstanden skal være mellom rutene -* [ ] Opprett en konstruktør i klassen med tre parametre: et `Rectangle2D` -objekt, et `GridDimension` -objekt og en `double`. Initaliser feltvariablene med verdiene mottat i konstruktøren. +* [ ] Opprett en konstruktør i klassen med tre parametre: et `Rectangle2D` -objekt, et `GridDimension` -objekt og en `double`. Initaliser feltvariablene med argumentene som mottas i konstruktøren. * [ ] Opprett metoden `getBoundsForCell` med en parameter av typen `CellPosition` (i figur under navgitt `cp`) og returtype `Rectangle2D`. Returverdien er et `Rectangle2D` -objekt. For å opprette dette objektet, må du regne ut fire verdier: x, y, bredde og høyde for den gitte ruten. Så kan du returnere et nytt `Rectangle2D.Double` -objekt med disse verdiene. -Illustrasjonen under visualiserer parameterne og resultatvariablene. Svarte variabler er gitt som input eller er tilgjengelig som feltvariabler, mens de røde variablene er de du skal regne ut og returnere. +Illustrasjonen under visualiserer parameterne og resultatvariablene. Variabler i svart tekst er gitt som input eller er tilgjengelig som feltvariabler, mens variablene i rød kursiv tekst er de du skal regne ut og returnere.  @@ -141,7 +142,7 @@ Hint: > > Vi begynner med å regne ut `cellWidth`. Siden vi har 4 kolonner totalt, vil det gå med 5*30=150 piksler til marginer, og vi får da 190 piksler igjen å fordele på de fire kolonnene. Vi får da at cellen skal ha bredde 47.5. > -> For å finne verdien til `cellX` begynner vi i dette tilfellet på `box.getX()` og legger til margin + rutebredde + margin + rutebredde + margin. Verdien blir da 30+30+47.5+30+47.5+30 = 215. +> For å finne verdien til `cellX` begynner vi på posisjonen `box.getX()` og går derfra videre mot høyre ved å plusse på margin + rutebredde + margin + rutebredde + margin. Verdien blir da 30+30+47.5+30+47.5+30 = 215. > > Tilsvarende finner vi at `cellHeight` blir 40 og `cellY` blir 130. diff --git a/src/test/java/no/uib/inf101/colorgrid/TestColorGrid.java b/src/test/java/no/uib/inf101/colorgrid/TestColorGrid.java index bd8f848..bb3a1c0 100644 --- a/src/test/java/no/uib/inf101/colorgrid/TestColorGrid.java +++ b/src/test/java/no/uib/inf101/colorgrid/TestColorGrid.java @@ -3,35 +3,51 @@ package no.uib.inf101.colorgrid; import org.junit.jupiter.api.Test; import java.awt.Color; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.List; import static org.junit.jupiter.api.Assertions.*; public class TestColorGrid { + @Test + public void sanityTest() { + IColorGrid grid = new WColorGrid(3, 4); + + // Check that number of rows and cols match up + assertEquals(3, grid.rows()); + assertEquals(4, grid.cols()); + + // Check that default value is null + assertEquals(null, grid.get(new CellPosition(1, 2))); + + // Check that we can set a value and retrieve it again + grid.set(new CellPosition(1, 2), Color.RED); + assertEquals(Color.RED, grid.get(new CellPosition(1, 2))); + assertEquals(null, grid.get(new CellPosition(2, 1))); + } @Test - public void testSize() { - IColorGrid grid = createGrid(10, 20); - assertEquals(10, grid.rows()); - assertEquals(20, grid.cols()); - - grid = createGrid(1, 1); - assertEquals(1, grid.rows()); - assertEquals(1, grid.cols()); + public void testDimension() { + GridDimension gd = new WColorGrid(10, 20); + assertEquals(10, gd.rows()); + assertEquals(20, gd.cols()); + + gd = new WColorGrid(1, 1); + assertEquals(1, gd.rows()); + assertEquals(1, gd.cols()); } @Test public void testGetDefaultValue() { - IColorGrid grid = createGrid(10, 20); + IColorGrid grid = new WColorGrid(10, 20); assertNull(grid.get(new CellPosition(0, 0))); assertNull(grid.get(new CellPosition(2, 3))); } @Test public void testSetGetInCorners() { - IColorGrid grid = createGrid(10, 20); + IColorGrid grid = new WColorGrid(10, 20); // Set color in corners grid.set(new CellPosition(0, 0), Color.RED); grid.set(new CellPosition(0, 19), Color.GREEN); @@ -45,9 +61,35 @@ public class TestColorGrid { assertEquals(Color.YELLOW, grid.get(new CellPosition(9, 19))); } + @Test + public void testGetCells() { + IColorGrid grid = new WColorGrid(2, 2); + + grid.set(new CellPosition(0, 0), Color.RED); + grid.set(new CellPosition(0, 1), Color.GREEN); + grid.set(new CellPosition(1, 0), Color.BLUE); + grid.set(new CellPosition(1, 1), Color.BLACK); + + List<CellColor> expected = Arrays.asList( + new CellColor(new CellPosition(0, 0), Color.RED), + new CellColor(new CellPosition(0, 1), Color.GREEN), + new CellColor(new CellPosition(1, 0), Color.BLUE), + new CellColor(new CellPosition(1, 1), Color.BLACK) + ); + List<CellColor> actual = grid.getCells(); + + assertEquals(expected.size(), actual.size()); + for (CellColor cp : actual) { + assertTrue(expected.contains(cp)); + } + for (CellColor cp : expected) { + assertTrue(actual.contains(cp)); + } + } + @Test public void testIndexOutOfBoundsException() { - IColorGrid grid = createGrid(10, 20); + IColorGrid grid = new WColorGrid(10, 20); // Test out of bounds for get assertThrows(IndexOutOfBoundsException.class, () -> grid.get(new CellPosition(-1, 0))); @@ -61,36 +103,4 @@ public class TestColorGrid { assertThrows(IndexOutOfBoundsException.class, () -> grid.set(new CellPosition(10, 0), Color.RED)); assertThrows(IndexOutOfBoundsException.class, () -> grid.set(new CellPosition(0, 20), Color.RED)); } - - - /** - * Create a new ColorGrid with the given rows and cols. This method - * will only work if you have implemented the ColorGrid class with - * the correct parameters (two int's), otherwise the test will fail - * when calling this method. - * - * @param rows number of rows in the colorgrid to create - * @param cols number of columns in the colorgrid to create - * @return a new ColorGrid - */ - public IColorGrid createGrid(int rows, int cols) { - try { - Constructor<?> c = ColorGrid.class.getConstructor(int.class, int.class); - Object o = c.newInstance(rows, cols); - if (o instanceof IColorGrid grid) { - return grid; - } - fail("Constructor did not return an IColorGrid. This could be " - + "because you forgot to implement the IColorGrid interface."); - } catch (NoSuchMethodException e) { - fail("Could not find constructor ColorGrid(int, int)"); - } catch (InvocationTargetException e) { - fail("Constructor crashed: " + e); - } catch (InstantiationException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - fail("Constructor is not public: " + e); - } - return null; - } } diff --git a/src/test/java/no/uib/inf101/colorgrid/WColorGrid.java b/src/test/java/no/uib/inf101/colorgrid/WColorGrid.java new file mode 100644 index 0000000..b375c7a --- /dev/null +++ b/src/test/java/no/uib/inf101/colorgrid/WColorGrid.java @@ -0,0 +1,101 @@ +package no.uib.inf101.colorgrid; + +// This class is a "wrapper" around ColorGrid (the class you will +// write). It uses some concepts that are outside the syllabus of +// INF101, and you are not expected to understand the code in the +// createColorGrid method. +// +// We use this class to be able to write the tests in TestColorGrid +// without getting compilation errors, even if ColorGrid is not +// implemented yet. This is done in the context of a scaffolded +// homework assignment, and is not something which is normally seen in +// the wild. + +import java.awt.Color; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.fail; + +public class WColorGrid implements IColorGrid { + + private final IColorGrid cg; + + public WColorGrid(int rows, int cols) { + this.cg = newColorGrid(rows, cols); + } + + @Override + public List<CellColor> getCells() { + return this.cg.getCells(); + } + + @Override + public int rows() { + return this.cg.rows(); + } + + @Override + public int cols() { + return this.cg.cols(); + } + + @Override + public Color get(CellPosition pos) { + return this.cg.get(pos); + } + + @Override + public void set(CellPosition pos, Color color) { + this.cg.set(pos, color); + } + + /** + * Create a new ColorGrid with the given rows and cols. This method + * will only work if you have implemented the ColorGrid class with + * the correct parameters (two int's), otherwise the test will fail + * when calling this method. + * + * @param rows number of rows in the colorgrid to create + * @param cols number of columns in the colorgrid to create + * @return a new ColorGrid + */ + private static IColorGrid newColorGrid(int rows, int cols) { + try { + Constructor<?> c = ColorGrid.class.getConstructor(int.class, int.class); + Object o = c.newInstance(rows, cols); + if (o instanceof IColorGrid grid) { + return grid; + } + fail("ColorGrid constructor did not return an IColorGrid. This could " + + "be because you forgot to implement the IColorGrid interface."); + } catch (NoSuchMethodException e) { + fail("Could not find constructor ColorGrid(int, int)"); + } catch (InvocationTargetException e) { + fail("Constructor crashed: " + e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + fail("Constructor is not public: " + e); + } + return null; + } + + public static IColorGrid newGridFromStrings(String[] sgrid) { + IColorGrid grid = new WColorGrid(sgrid.length, sgrid[0].length()); + for (int r = 0; r < grid.rows(); r++) { + for (int c = 0; c < grid.cols(); c++) { + grid.set(new CellPosition(r, c), switch (sgrid[r].charAt(c)) { + case 'R' -> Color.RED; + case 'G' -> Color.GREEN; + case 'B' -> Color.BLUE; + case 'Y' -> Color.YELLOW; + default -> null; + }); + } + } + return grid; + } + +} diff --git a/src/test/java/no/uib/inf101/gridview/RecordGraphics2D.java b/src/test/java/no/uib/inf101/gridview/RecordGraphics2D.java index 307851d..7697610 100644 --- a/src/test/java/no/uib/inf101/gridview/RecordGraphics2D.java +++ b/src/test/java/no/uib/inf101/gridview/RecordGraphics2D.java @@ -21,20 +21,21 @@ import java.util.Map; public class RecordGraphics2D extends java.awt.Graphics2D { private Color color = null; - private final List<Shape> fillRecordShapes = new ArrayList<>(); - private final List<Color> fillRecordColors = new ArrayList<>(); + private final List<Shape> recordedFillShapes = new ArrayList<>(); + private final List<Color> recordedFillColors = new ArrayList<>(); - public List<Shape> getFillRecordShapes() { - return this.fillRecordShapes; + public List<Shape> getRecordedFillShapes() { + return this.recordedFillShapes; } - public List<Color> getFillRecordColors() { - return this.fillRecordColors; + public List<Color> getRecordedFillColors() { + return this.recordedFillColors; } - public void resetRecord() { - this.fillRecordShapes.clear(); - this.fillRecordColors.clear(); + @Override + public void fill(Shape s) { + this.recordedFillShapes.add(s); + this.recordedFillColors.add(this.color); } @Override @@ -42,12 +43,6 @@ public class RecordGraphics2D extends java.awt.Graphics2D { // dummy ignores } - @Override - public void fill(Shape s) { - this.fillRecordShapes.add(s); - this.fillRecordColors.add(this.color); - } - @Override public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { return false; @@ -190,7 +185,7 @@ public class RecordGraphics2D extends java.awt.Graphics2D { @Override public Color getColor() { - throw new UnsupportedOperationException("Not supported yet."); + return this.color; } @Override diff --git a/src/test/java/no/uib/inf101/gridview/TestCellPositionToPixelConverter.java b/src/test/java/no/uib/inf101/gridview/TestCellPositionToPixelConverter.java index 2b2fa14..6f7af4b 100644 --- a/src/test/java/no/uib/inf101/gridview/TestCellPositionToPixelConverter.java +++ b/src/test/java/no/uib/inf101/gridview/TestCellPositionToPixelConverter.java @@ -1,132 +1,32 @@ package no.uib.inf101.gridview; -import no.uib.inf101.colorgrid.CellColor; -import no.uib.inf101.colorgrid.CellPosition; -import no.uib.inf101.colorgrid.GridDimension; -import no.uib.inf101.colorgrid.IColorGrid; +import no.uib.inf101.colorgrid.*; import org.junit.jupiter.api.Test; import java.awt.*; import java.awt.geom.Rectangle2D; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; import static org.junit.jupiter.api.Assertions.*; public class TestCellPositionToPixelConverter { @Test - public void illustratedSample() { - IColorGrid grid = newGridFromString(String.join("\n", - "R--B", - "----", - "Y--G") - ); - CellPositionToPixelConverter converter = getConverter( + public void sanityTest() { + // Same case as illustrated in README + // Setup + IColorGrid grid = new WColorGrid(3, 4); + grid.set(new CellPosition(0, 0), Color.RED); + grid.set(new CellPosition(0, 3), Color.BLUE); + grid.set(new CellPosition(2, 0), Color.YELLOW); + grid.set(new CellPosition(2, 3), Color.GREEN); + + // Construct CellPositionToPixelConverter (using a wrapper class) + WCellPositionToPixelConverter converter = new WCellPositionToPixelConverter( new Rectangle2D.Double(30, 30, 340, 240), grid,30 ); + // Check result Rectangle2D expected = new Rectangle2D.Double(215, 130, 47.5, 40); - assertEquals(expected, getBoundsForCell(converter, new CellPosition(1, 2))); - } - - ///////////////////////////// - // Helper methods - ///////////////////////////// - - static Rectangle2D getBoundsForCell(CellPositionToPixelConverter converter, CellPosition cp) { - try { - Method method = CellPositionToPixelConverter.class.getMethod("getBoundsForCell", CellPosition.class); - // Check that the method is public - assertFalse(Modifier.isPrivate(method.getModifiers()), - "The method getBoundsForCell(CellPosition) in the CellPositionToPixelConverter class should not be private"); - - Object result = method.invoke(converter, cp); - assertInstanceOf(Rectangle2D.class, result, - "The method getBoundsForCell(CellPosition) in the CellPositionToPixelConverter class should return a Rectangle2D"); - return (Rectangle2D) result; - } catch (NoSuchMethodException e) { - fail("Could not find the method getBoundsForCell(CellPosition) in the CellPositionToPixelConverter class"); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - throw new IllegalStateException("Should not be possible to reach this point"); - } - - - static CellPositionToPixelConverter getConverter(Rectangle2D box, GridDimension gd, double margin) { - try { - Constructor<?> constructor = CellPositionToPixelConverter.class.getConstructor( - Rectangle2D.class, GridDimension.class, double.class - ); - - // Check that the constructor is public - assertTrue(Modifier.isPublic(constructor.getModifiers()), - "The constructor CellPositionToPixelConverter(Rectangle2D, GridDimension, double)" - + " should be public"); - - // Create a new object using the constructor and return it - return (CellPositionToPixelConverter) constructor.newInstance(box, gd, margin); - } catch (NoSuchMethodException e) { - fail("Could not find the constructor CellPositionToPixelConverter(Rectangle2D, GridDimension, " + - "double) in the CellPositionToPixelConverter class"); - } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { - throw new RuntimeException(e); - } - throw new IllegalStateException("Should not be possible to reach this point"); - } - - static IColorGrid newGridFromString(String stringGrid) { - return new IColorGrid() { - private String[] lines = stringGrid.split("\n"); - - @Override - public int rows() { - return this.lines.length; - } - - @Override - public int cols() { - return this.lines[0].length(); - } - - @Override - public Color get(CellPosition pos) { - if (pos.row() < 0 || pos.row() >= this.rows()) { - throw new IllegalArgumentException("Row out of bounds"); - } - if (pos.col() < 0 || pos.col() >= this.cols()) { - throw new IllegalArgumentException("Column out of bounds"); - } - return switch (this.lines[pos.row()].charAt(pos.col())) { - case 'R' -> Color.RED; - case 'G' -> Color.GREEN; - case 'B' -> Color.BLUE; - case 'Y' -> Color.YELLOW; - default -> null; - }; - } - - @Override - public void set(CellPosition pos, Color color) { - // ignore - } - - @Override - public java.util.List<CellColor> getCells() { - List<CellColor> a = new ArrayList<>(); - for (int r = 0; r < this.rows(); r++) { - for (int c = 0; c < this.cols(); c++) { - Color color = this.get(new CellPosition(r, c)); - a.add(new CellColor(new CellPosition(r, c), color)); - } - } - return a; - } - }; + assertEquals(expected, converter.getBoundsForCell(new CellPosition(1, 2))); } } diff --git a/src/test/java/no/uib/inf101/gridview/TestGridView.java b/src/test/java/no/uib/inf101/gridview/TestGridView.java index a3b3bf5..58a2eb2 100644 --- a/src/test/java/no/uib/inf101/gridview/TestGridView.java +++ b/src/test/java/no/uib/inf101/gridview/TestGridView.java @@ -14,68 +14,57 @@ package no.uib.inf101.gridview; import no.uib.inf101.colorgrid.*; import org.junit.jupiter.api.Test; -import javax.swing.JPanel; import java.awt.*; import java.awt.geom.Rectangle2D; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import static org.junit.jupiter.api.Assertions.*; public class TestGridView { @Test - public void preferredSize() { - GridView view = newGridView(null); - if ((Object) view instanceof JPanel panel) { - assertNotNull(panel, "Unable to create a new GridView object"); - assertNotNull(panel.getPreferredSize(), "PreferredSize should not be null"); - assertEquals(400, panel.getPreferredSize().width); - assertEquals(300, panel.getPreferredSize().height); - } else { - fail("The GridView class should extend JPanel"); - } + public void preferredSizeTest() { + WGridView view = new WGridView(null); + assertNotNull(view.getPreferredSize(), "PreferredSize shouldn't be null"); + assertEquals(400, view.getPreferredSize().width); + assertEquals(300, view.getPreferredSize().height); } @Test - public void drawCellsIllustratedSample() { - // Sample case from assignment text - IColorGrid sampleGrid = TestCellPositionToPixelConverter.newGridFromString(String.join("\n", + public void drawCellsSanityTest() { + // Sample case illustrated in README + IColorGrid grid = WColorGrid.newGridFromStrings(new String[]{ "R--B", "----", "Y--G" - )); - Rectangle2D rect = new Rectangle2D.Double(30, 30, 340, 240); + }); + Rectangle2D box = new Rectangle2D.Double(30, 30, 340, 240); double margin = 30; - RecordGraphics2D record = singleDrawCellsRun(sampleGrid, rect, margin); + RecordGraphics2D recording = singleDrawCellsRun(grid, box, margin); - // Check that the correct number of calls were made - assertEquals(12, record.getFillRecordShapes().size(), + // Check that the correct number of calls were filled + assertEquals(12, recording.getRecordedFillShapes().size(), "The drawCells method call draw 12 rectangles in the illustrated sample case"); - assertColorIsDrawnOnceAt(record, Color.RED, new Rectangle2D.Double(60, 60, 47.5, 40)); - assertColorIsDrawnOnceAt(record, Color.BLUE, new Rectangle2D.Double(292.5, 60, 47.5, 40)); - assertColorIsDrawnOnceAt(record, Color.YELLOW, new Rectangle2D.Double(60, 200, 47.5, 40)); - assertColorIsDrawnOnceAt(record, Color.GREEN, new Rectangle2D.Double(292.5, 200, 47.5, 40)); + assertColorIsDrawnOnceAt(recording, Color.RED, new Rectangle2D.Double(60, 60, 47.5, 40)); + assertColorIsDrawnOnceAt(recording, Color.BLUE, new Rectangle2D.Double(292.5, 60, 47.5, 40)); + assertColorIsDrawnOnceAt(recording, Color.YELLOW, new Rectangle2D.Double(60, 200, 47.5, 40)); + assertColorIsDrawnOnceAt(recording, Color.GREEN, new Rectangle2D.Double(292.5, 200, 47.5, 40)); } @Test - public void drawCellsIllustratedSampleDifferentColors() { - // Sample case from assignment text - IColorGrid sampleGrid = TestCellPositionToPixelConverter.newGridFromString(String.join("\n", + public void drawCellsSanityOtherColors() { + // Sample case illustrated in README, but colors mixed up + IColorGrid sampleGrid = WColorGrid.newGridFromStrings(new String[]{ "Y--R", "----", "G--B" - )); + }); Rectangle2D rect = new Rectangle2D.Double(30, 30, 340, 240); double margin = 30; RecordGraphics2D record = singleDrawCellsRun(sampleGrid, rect, margin); // Check that the correct number of calls were made - assertEquals(12, record.getFillRecordShapes().size(), - "The drawCells method call draw 12 rectangles in the illustrated sample case"); + assertEquals(12, record.getRecordedFillShapes().size()); assertColorIsDrawnOnceAt(record, Color.YELLOW, new Rectangle2D.Double(60, 60, 47.5, 40)); assertColorIsDrawnOnceAt(record, Color.RED, new Rectangle2D.Double(292.5, 60, 47.5, 40)); @@ -84,19 +73,19 @@ public class TestGridView { } @Test - public void drawCellsIllustratedSampleTweak() { + public void drawCellsSanityTestPixelsMoved() { // Sample case from assignment text - IColorGrid sampleGrid = TestCellPositionToPixelConverter.newGridFromString(String.join("\n", + IColorGrid sampleGrid = WColorGrid.newGridFromStrings(new String[]{ "R--B", "----", "Y--G" - )); + }); Rectangle2D rect = new Rectangle2D.Double(40, 20, 342, 243); double margin = 30; RecordGraphics2D record = singleDrawCellsRun(sampleGrid, rect, margin); // Check that the correct number of calls were made - assertEquals(12, record.getFillRecordShapes().size()); + assertEquals(12, record.getRecordedFillShapes().size()); assertColorIsDrawnOnceAt(record, Color.RED, new Rectangle2D.Double(70, 50, 48, 41)); assertColorIsDrawnOnceAt(record, Color.BLUE, new Rectangle2D.Double(304, 50, 48, 41)); @@ -107,13 +96,13 @@ public class TestGridView { @Test public void drawCellsSingleCell() { // Sample case from assignment text - IColorGrid sampleGrid = TestCellPositionToPixelConverter.newGridFromString("R"); + IColorGrid sampleGrid = WColorGrid.newGridFromStrings(new String[] {"R"}); Rectangle2D rect = new Rectangle2D.Double(20, 30, 40, 50); double margin = 10; RecordGraphics2D record = singleDrawCellsRun(sampleGrid, rect, margin); // Check that the correct number of calls were made - assertEquals(1, record.getFillRecordShapes().size()); + assertEquals(1, record.getRecordedFillShapes().size()); assertColorIsDrawnOnceAt(record, Color.RED, new Rectangle2D.Double(30, 40, 20, 30)); } @@ -123,80 +112,25 @@ public class TestGridView { private void assertColorIsDrawnOnceAt(RecordGraphics2D record, Color color, Rectangle2D expectedRectangle) { int count = 0; - for (int i=0; i<record.getFillRecordColors().size(); i++) { - if (record.getFillRecordColors().get(i).equals(color)) { - assertEquals(expectedRectangle, record.getFillRecordShapes().get(i), + for (int i = 0; i<record.getRecordedFillColors().size(); i++) { + if (record.getRecordedFillColors().get(i).equals(color)) { + assertEquals(expectedRectangle, record.getRecordedFillShapes().get(i), "Incorrect bounds for color "+ color ); count++; } } - assertEquals(1, count, "There should be exactly one rectangle with color "+ color); - } - - static GridView newGridView(IColorGrid grid) { - try { - Constructor<?> constructor = GridView.class.getConstructor(IColorGrid.class); - - // Check that the constructor is not private - assertFalse(Modifier.isPrivate(constructor.getModifiers()), - "The constructor GridView(IColorGrid) should not be private"); - - // Create a new object using the constructor and return it - return (GridView) constructor.newInstance(grid); - } catch (NoSuchMethodException e) { - if (grid != null) { - fail("Could not find the constructor GridView(IColorGrid) in the GridView class"); - } - try { - Constructor<?> constructor = GridView.class.getConstructor(); - return (GridView) constructor.newInstance(); - } catch (NoSuchMethodException ex) { - fail("Could not find the constructor GridView() or GridView(IColorGrid) in the GridView class"); - } catch (InvocationTargetException | InstantiationException | IllegalAccessException ex) { - throw new RuntimeException(ex); - } - } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { - throw new RuntimeException(e); - } - throw new IllegalStateException("Should not be possible to reach this point"); + assertEquals(1, count, "There should be exactly one rectangle with color " + color); } public static RecordGraphics2D singleDrawCellsRun( IColorGrid grid, Rectangle2D rect, double margin) { // Get the "drawCells" method from the GridView class and invoke // the method with the fake Graphics2D object - try { - Method drawCell = GridView.class.getDeclaredMethod("drawCells", - Graphics2D.class, - CellColorCollection.class, - CellPositionToPixelConverter.class - ); - - // Test that the method is static - assertTrue(Modifier.isStatic(drawCell.getModifiers()), - "The drawCells method should be static, and it should not use any instance variables"); - - // Test that the method is private - assertTrue(Modifier.isPrivate(drawCell.getModifiers()), - "The drawCells method should be private"); - - // Make the method accessible in case it is private - drawCell.setAccessible(true); - - // Preparing a "fake" Graphics2D object that records stuff that - // happens to it instead of actually drawing anything - RecordGraphics2D g2 = new RecordGraphics2D(); - - // Invoke the method - drawCell.invoke(null, g2, grid, TestCellPositionToPixelConverter.getConverter(rect, grid, margin)); - return g2; - } catch (NoSuchMethodException e) { - fail("Could not find the method drawCells(Graphics2D, CellColorCollection," - + " CellPositionToPixelConverter) in the GridView class"); - } catch (InvocationTargetException | IllegalAccessException e) { - throw new RuntimeException(e); - } - throw new IllegalStateException("Should not reach this point"); + RecordGraphics2D g2 = new RecordGraphics2D(); + CellPositionToPixelConverter converter = WCellPositionToPixelConverter.newCellPositionToPixelConverter( + rect, grid, margin); + WGridView.drawCells(g2, grid, converter); + return g2; } } diff --git a/src/test/java/no/uib/inf101/gridview/WCellPositionToPixelConverter.java b/src/test/java/no/uib/inf101/gridview/WCellPositionToPixelConverter.java new file mode 100644 index 0000000..937c79d --- /dev/null +++ b/src/test/java/no/uib/inf101/gridview/WCellPositionToPixelConverter.java @@ -0,0 +1,86 @@ +package no.uib.inf101.gridview; + +// This class is a "wrapper" around CellPositionToPixelConverter (the +// class you will write). It uses some concepts that are outside the +// syllabus of INF101, and you are not expected to understand the code. +// +// We use this class to be able to write the tests in +// TestCellPositionToPixelConverter without getting compilation errors, +// even if CellPositionToPixelConverter is not implemented yet. This is +// done in the context of a scaffolded homework assignment, and is not +// something which is normally seen in the wild. + +import no.uib.inf101.colorgrid.CellPosition; +import no.uib.inf101.colorgrid.GridDimension; + +import java.awt.geom.Rectangle2D; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +public class WCellPositionToPixelConverter { + + CellPositionToPixelConverter converter; + + public WCellPositionToPixelConverter(Rectangle2D box, GridDimension gd, + double margin) { + this.converter = newCellPositionToPixelConverter(box, gd, margin); + } + + public Rectangle2D getBoundsForCell(CellPosition cp) { + try { + Method method = CellPositionToPixelConverter.class.getDeclaredMethod( + "getBoundsForCell", CellPosition.class); + // Check that the method is public + assertFalse(Modifier.isPrivate(method.getModifiers()), + "The method getBoundsForCell(CellPosition) in the " + + "CellPositionToPixelConverter class should not be private"); + + Object result = method.invoke(this.converter, cp); + assertInstanceOf(Rectangle2D.class, result, + "The method getBoundsForCell(CellPosition) in the " + + "CellPositionToPixelConverter class should return a " + + "Rectangle2D"); + return (Rectangle2D) result; + } catch (NoSuchMethodException e) { + fail("Could not find the method getBoundsForCell(CellPosition) in " + + "the CellPositionToPixelConverter class " + e); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + throw new IllegalStateException("Should not be possible to reach this " + + "point"); + } + + + static CellPositionToPixelConverter newCellPositionToPixelConverter( + Rectangle2D box, GridDimension gd, double margin) { + try { + Constructor<?> constructor = CellPositionToPixelConverter.class + .getConstructor( + Rectangle2D.class, GridDimension.class, double.class); + + // Check that the constructor is public + assertTrue(Modifier.isPublic(constructor.getModifiers()), + "The constructor CellPositionToPixelConverter" + + "(Rectangle2D, GridDimension, double) should be public"); + + // Create a new object using the constructor and return it + return (CellPositionToPixelConverter) constructor.newInstance(box, gd, + margin); + } catch (NoSuchMethodException e) { + fail("Could not find the constructor CellPositionToPixelConverter" + + "(Rectangle2D, GridDimension, double) in the " + + "CellPositionToPixelConverter class"); + } catch (InvocationTargetException + | InstantiationException + | IllegalAccessException e) { + throw new RuntimeException(e); + } + throw new IllegalStateException("Shouldn't reach this point"); + } +} diff --git a/src/test/java/no/uib/inf101/gridview/WGridView.java b/src/test/java/no/uib/inf101/gridview/WGridView.java new file mode 100644 index 0000000..d520316 --- /dev/null +++ b/src/test/java/no/uib/inf101/gridview/WGridView.java @@ -0,0 +1,117 @@ +package no.uib.inf101.gridview; + +// This class is a "wrapper" around GridView (the class you will +// write). It uses some concepts that are outside the syllabus of +// INF101, and you are not expected to understand the code. +// +// We use this class to be able to write the tests in TestGridView +// without getting compilation errors, even if GridView is not +// implemented yet. This is done in the context of a scaffolded +// homework assignment, and is not something which is normally seen in +// the wild. + +import no.uib.inf101.colorgrid.CellColorCollection; +import no.uib.inf101.colorgrid.IColorGrid; + +import javax.swing.JPanel; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import static org.junit.jupiter.api.Assertions.*; + +public class WGridView extends JPanel { + + private final GridView gv; + private final JPanel jp; + + public WGridView(IColorGrid grid) { + GridView gv = WGridView.newGridView(grid); + assertInstanceOf(JPanel.class, gv, "GridView objects does not " + + "carry the type JPanel. Does your GridView class extend JPanel?"); + this.gv = gv; + if ((Object) gv instanceof JPanel jp) { + this.jp = jp; + } else { + throw new IllegalStateException("Should not be here"); + } + } + + @Override + public Dimension getPreferredSize() { + return this.jp.getPreferredSize(); + } + + static void drawCells(Graphics2D g2, CellColorCollection cells, + CellPositionToPixelConverter converter) { + try { + Method drawCell = GridView.class.getDeclaredMethod("drawCells", + Graphics2D.class, + CellColorCollection.class, + CellPositionToPixelConverter.class + ); + + // Test that the method is static + assertTrue(Modifier.isStatic(drawCell.getModifiers()), + "The drawCells method should be static, " + + "and it should not use any instance variables"); + + // Test that the method is private + assertTrue(Modifier.isPrivate(drawCell.getModifiers()), + "The drawCells method should be private"); + + // Make the method accessible in case it is private + drawCell.setAccessible(true); + + // Invoke the method + drawCell.invoke(null, g2, cells, converter); + } catch (NoSuchMethodException e) { + fail("Could not find the method drawCells(Graphics2D, " + + "CellColorCollection, CellPositionToPixelConverter) in the " + + "GridView class"); + } catch (InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + + + private static GridView newGridView(IColorGrid grid) { + try { + Constructor<?> constructor = GridView.class.getConstructor( + IColorGrid.class); + + // Check that the constructor is not private + assertFalse(Modifier.isPrivate(constructor.getModifiers()), + "The constructor GridView(IColorGrid) should not " + + "be private"); + + // Create a new object using the constructor and return it + return (GridView) constructor.newInstance(grid); + } catch (NoSuchMethodException e) { + if (grid != null) { + fail("Could not find the constructor GridView(IColorGrid) in " + + "the GridView class"); + } + try { + Constructor<?> constructor = GridView.class.getConstructor(); + return (GridView) constructor.newInstance(); + } catch (NoSuchMethodException ex) { + fail("Could not find the constructor GridView() or " + + "GridView(IColorGrid) in the GridView class"); + } catch (InvocationTargetException + | InstantiationException + | IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } catch (InvocationTargetException + | InstantiationException + | IllegalAccessException e) { + throw new RuntimeException(e); + } + throw new IllegalStateException("Shouldn't reach this point"); + } +} -- GitLab