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.
 
 ![Illustrasjon av variabler som opptrer i getBoundsForCell](./img/getBoundsForCell.png)
 
@@ -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