-2

So I have code that runs, but I need to write the test for one of the classes (Cells) and I'm not sure how to go about this.

Typically, I'd have a helper class at the start of CellsTest, which would encapsulate the constructor call, which relies on the specific class name of the class that implements the overall program. This helper class would return an instance of that program.

So, what I'd think is that I'd need to have something like:

    private Maze createMaze() {
            return new Maze();
        }

But then, my test methods can't access the Cell methods, just the Maze methods...So I'm guessing that that is wrong, and I'm not sure how I should actually be doing this.

    //Maze.java 
    package falstad;
    import java.awt.*;
    import java.util.ArrayList;
    import java.util.Iterator;

    /**
     * Class handles the user interaction for the maze.
     */
    //public class Maze extends Panel {
    public class Maze  {
            final private ArrayList<Viewer> views = new ArrayList<Viewer>() ;
            MazePanel panel ; // graphics to draw on, shared by all views
            private int state;                     
            private int percentdone = 0; 
            private boolean showMaze;
            private boolean showSolution;
            private boolean solving;
            private boolean mapMode;
            //static final int viewz = 50;    
            int viewx, viewy, angle;
            int dx, dy;
            int px, py ;
            int walkStep;
            int viewdx, viewdy;
            boolean deepdebug = false;
            boolean allVisible = false;
            boolean newGame = false;

            int mazew; // width
            int mazeh; // height
            Cells mazecells ; 
            Distance mazedists ;
            Cells seencells ; 

            BSPNode rootnode ;

            // Mazebuilder is used to calculate a new maze together with a solution
            // The maze is computed in a separate thread. It is started in the local Build method.
            MazeBuilder mazebuilder;

            final int ESCAPE = 27;
            int method = 0 ; // 0 : default method, Falstad's original code

            int zscale = Constants.VIEW_HEIGHT/2;

            private RangeSet rset;

            //Constructor
            public Maze() {
                    super() ;
                    panel = new MazePanel() ;
            }
            //selects a generation method
            public Maze(int method)
            {
                    super() ;
                    if (1 == method)
                            this.method = 1 ;
                    panel = new MazePanel() ;
            }

            public void init() {
                    state = Constants.STATE_TITLE;
                    rset = new RangeSet();
                    panel.initBufferImage() ;
                    addView(new MazeView(this)) ;
                    notifyViewerRedraw() ;
            }

            // Method obtains a new Mazebuilder and computes new maze
            private void build(int skill) {
                    state = Constants.STATE_GENERATING;
                    percentdone = 0;
                    notifyViewerRedraw() ;
                    // select generation method
                    switch(method){
                    case 1 : mazebuilder = new MazeBuilderPrim(); 
                    break ;
                    case 0: 
                    default : mazebuilder = new MazeBuilder();
                    break ;
                    }
                    mazew = Constants.SKILL_X[skill];
                    mazeh = Constants.SKILL_Y[skill];
                    mazebuilder.build(this, mazew, mazeh, Constants.SKILL_ROOMS[skill], Constants.SKILL_PARTCT[skill]);
            }

            //Call back method for MazeBuilder to communicate newly generated maze 
            public void newMaze(BSPNode root, Cells c, Distance dists, int startx, int starty) {
                    if (Cells.deepdebugWall)
                    {   
                            c.saveLogFile(Cells.deepedebugWallFileName);
                    }
                    showMaze = showSolution = solving = false;
                    mazecells = c ;
                    mazedists = dists;
                    seencells = new Cells(mazew+1,mazeh+1) ;
                    rootnode = root ;
                    setCurrentDirection(1, 0) ;
                    setCurrentPosition(startx,starty) ;
                    walkStep = 0;
                    viewdx = dx<<16;
                    viewdy = dy<<16;
                    angle = 0;
                    mapMode = false;
                    state = Constants.STATE_PLAY;
                    cleanViews() ;
                    addView(new FirstPersonDrawer(Constants.VIEW_WIDTH,Constants.VIEW_HEIGHT,
                                    Constants.MAP_UNIT,Constants.STEP_SIZE, mazecells, seencells, 10, mazedists.getDists(), mazew, mazeh, root, this)) ;

                    addView(new MapDrawer(Constants.VIEW_WIDTH,Constants.VIEW_HEIGHT,Constants.MAP_UNIT,Constants.STEP_SIZE, mazecells, seencells, 10, mazedists.getDists(), mazew, mazeh, this)) ;

                    notifyViewerRedraw() ;
            }



            public void addView(Viewer view) {
                    views.add(view) ;
            }
            public void removeView(Viewer view) {
                    views.remove(view) ;
            }
            private void cleanViews() {

                    Iterator<Viewer> it = views.iterator() ;
                    while (it.hasNext())
                    {
                            Viewer v = it.next() ;
                            if ((v instanceof FirstPersonDrawer)||(v instanceof MapDrawer))
                            {
                                    //System.out.println("Removing " + v);
                                    it.remove() ;
                            }
                    }

            }
            private void notifyViewerRedraw() {

                    Iterator<Viewer> it = views.iterator() ;
                    while (it.hasNext())
                    {
                            Viewer v = it.next() ;

                            v.redraw(panel.getBufferGraphics(), state, px, py, viewdx, viewdy, walkStep, Constants.VIEW_OFFSET, rset, angle) ;
                    }

                    panel.update() ;
            }
            private void notifyViewerIncrementMapScale() {

                    Iterator<Viewer> it = views.iterator() ;
                    while (it.hasNext())
                    {
                            Viewer v = it.next() ;
                            v.incrementMapScale() ;
                    }

                    panel.update() ;
            }
            private void notifyViewerDecrementMapScale() {

                    Iterator<Viewer> it = views.iterator() ;
                    while (it.hasNext())
                    {
                            Viewer v = it.next() ;
                            v.decrementMapScale() ;
                    }

                    panel.update() ;
            }
            boolean isInMapMode() {
                    return mapMode ;
            }
            boolean isInShowMazeMode() {
                    return showMaze ;
            }
            boolean isInShowSolutionMode() {
                    return showSolution ;
            }
            public String getPercentDone(){
                    return String.valueOf(percentdone) ;
            }
            public Panel getPanel() {
                    return panel ;
            }
            private void setCurrentPosition(int x, int y)
            {
                    px = x ;
                    py = y ;
            }
            private void setCurrentDirection(int x, int y)
            {
                    dx = x ;
                    dy = y ;
            }
            void buildInterrupted() {
                    state = Constants.STATE_TITLE;
                    notifyViewerRedraw() ;
                    mazebuilder = null;
            }
            final double radify(int x) {
                    return x*Math.PI/180;
            }
            public boolean increasePercentage(int pc) {
                    if (percentdone < pc && pc < 100) {
                            percentdone = pc;
                            if (state == Constants.STATE_GENERATING)
                            {
                                    notifyViewerRedraw() ;
                            }
                            else
                                    dbg("Warning: Receiving update request for increasePercentage while not in generating state, skip redraw.") ;
                            return true ;
                    }
                    return false ;
            }

            private void dbg(String str) {
                    //System.out.println(str);
            }

            private void logPosition() {
                    if (!deepdebug)
                            return;
                    dbg("x="+viewx/Constants.MAP_UNIT+" ("+
                                    viewx+") y="+viewy/Constants.MAP_UNIT+" ("+viewy+") ang="+
                                    angle+" dx="+dx+" dy="+dy+" "+viewdx+" "+viewdy);
            }
            private boolean checkMove(int dir) {
                    int a = angle/90;
                    if (dir == -1)
                            a = (a+2) & 3;
                    return mazecells.hasMaskedBitsFalse(px, py, Constants.MASKS[a]) ;
            }         
            private void rotateStep() {
                    angle = (angle+1800) % 360;
                    viewdx = (int) (Math.cos(radify(angle))*(1<<16));
                    viewdy = (int) (Math.sin(radify(angle))*(1<<16));
                    moveStep();
            }
            private void moveStep() {
                    notifyViewerRedraw() ;
                    try {
                            Thread.currentThread().sleep(25);
                    } catch (Exception e) { }
            }

            private void rotateFinish() {
                    setCurrentDirection((int) Math.cos(radify(angle)), (int) Math.sin(radify(angle))) ;
                    logPosition();
            }

            private void walkFinish(int dir) {
                    setCurrentPosition(px + dir*dx, py + dir*dy) ;

                    if (isEndPosition(px,py)) {
                            state = Constants.STATE_FINISH;
                            notifyViewerRedraw() ;
                    }
                    walkStep = 0;
                    logPosition();
            }

            private boolean isEndPosition(int x, int y) {
                    return x < 0 || y < 0 || x >= mazew || y >= mazeh;
            }

            synchronized private void walk(int dir) {
                    if (!checkMove(dir))
                            return;
                    for (int step = 0; step != 4; step++) {
                            walkStep += dir;
                            moveStep();
                    }
                    walkFinish(dir);
            }

            synchronized private void rotate(int dir) {
                    final int originalAngle = angle;
                    final int steps = 4;

                    for (int i = 0; i != steps; i++) {
                            angle = originalAngle + dir*(90*(i+1))/steps;
                            rotateStep();
                    }
                    rotateFinish();
            }
           //CLASS CONTROLLING PLAYER MOVEMENTS, REMOVED FOR CHARACTER COUNT
            }
    }


//Cells.java


package falstad;

import java.io.BufferedWriter;
import java.io.FileWriter;

//This class encapsulates all access to a grid of cells
public class Cells {

    private int width;
    private int height ;
    private int[][] cells; 


    public Cells(int w, int h) {
            width = w ;
            height = h ;
            cells = new int[w][h];
    }


    public Cells(int[][] target){
            this(target.length, target[0].length);
            for (int i=0; i<width; i++)
                    for (int j=0; j<height; j++)
                            this.cells[i][j]=target[i][j];
    }

    public int getCells( int x, int y )
    {
            return cells[x][y] ;
    }
    static public int[] getMasks() {
            return Constants.MASKS ;
    }

    public boolean canGo(int x, int y, int dx, int dy) {
            if (hasMaskedBitsTrue(x, y, (getBit(dx, dy) << Constants.CW_BOUND_SHIFT)))
                    return false;
            return isFirstVisit(x+dx, y+dy);
    }
    private int getBit(int dx, int dy) {
            int bit = 0;
            switch (dx + dy * 2) {
            case 1:  bit = Constants.CW_RIGHT; break;
            case -1: bit = Constants.CW_LEFT;  break;
            case 2:  bit = Constants.CW_BOT;   break;
            case -2: bit = Constants.CW_TOP;   break;
            default: dbg("getBit problem "+dx+" "+dy); break;
            }
            return bit;
    }
//CLASSES FOR BITWISE ADJUSTMENTS, REMOVED FOR CHARACTER COUNT



    public void initialize() {
            int x, y;

            for (x = 0; x != width; x++) {
                    for (y = 0; y != height; y++) {
                            setBitToOne(x, y, (Constants.CW_VISITED | Constants.CW_ALL));
                    }
                    setBitToOne(x, 0, Constants.CW_TOP_BOUND);
                    setBitToOne(x, height-1, Constants.CW_BOT_BOUND);
            }
            for (y = 0; y != height; y++) {
                    setBitToOne(0, y, Constants.CW_LEFT_BOUND);
                    setBitToOne(width-1, y, Constants.CW_RIGHT_BOUND);
            }
    }

    public boolean areaOverlapsWithRoom(int rx, int ry, int rxl, int ryl) {
            int x, y;
            for (x = rx-1; x <= rxl+1; x++)
            {
                    for (y = ry-1; y <= ryl+1; y++)
                    {
                            if (isInRoom(x, y))
                                    return true ;
                    }
            }
            return false ;
    }

    private void deleteBound(int x, int y, int dx, int dy) {
            setBoundToZero(x, y, dx, dy);
            setBoundToZero(x+dx, y+dy, -dx, -dy) ;
    }


    public void addBoundWall(int x, int y, int dx, int dy) {

            setBoundAndWallToOne(x, y, dx, dy);
            setBoundAndWallToOne(x+dx, y+dy, -dx, -dy);
    }

    public void deleteWall(int x, int y, int dx, int dy) {
            setWallToZero(x, y, dx, dy);
            setWallToZero(x+dx, y+dy, -dx, -dy);

            if (deepdebugWall) 
                    logWall( x,  y,  dx,  dy);

    public void markAreaAsRoom(int rw, int rh, int rx, int ry, int rxl, int ryl) {
            int x;
            int y;
            for (x = rx; x <= rxl; x++)
                    for (y = ry; y <= ryl; y++) {
                            setAllToZero(x, y);
                            setInRoomToOne(x, y);
                    }
            for (x = rx; x <= rxl; x++) {
                    addBoundWall(x, ry, 0, -1);
                    addBoundWall(x, ryl, 0, 1);
            }
            for (y = ry; y <= ryl; y++) {
                    addBoundWall(rx, y, -1, 0);
                    addBoundWall(rxl, y, 1, 0);
            }
            int wallct = (rw+rh)*2;
            SingleRandom random = SingleRandom.getRandom() ;
            for (int ct = 0; ct != 5; ct++) {
                    int door = random.nextIntWithinInterval(0, wallct-1);
                    int dx, dy;
                    if (door < rw*2) {
                            y = (door < rw) ? 0 : rh-1;
                            dy = (door < rw) ? -1 : 1;
                            x = door % rw;
                            dx = 0;
                    } else {
                            door -= rw*2;
                            x = (door < rh) ? 0 : rw-1;
                            dx = (door < rh) ? -1 : 1;
                            y = door % rh;
                            dy = 0;
                    }
                    deleteBound(x+rx, y+ry, dx, dy);
            }
    }
    public boolean hasMaskedBitsTrue(int x, int y, int bitmask) {
            return (cells[x][y] & bitmask) != 0;
    }

    public boolean isInRoom(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_IN_ROOM);
    }
    private boolean isFirstVisit(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_VISITED);
    }
    public boolean hasWallOnRight(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_RIGHT);
    }
    public boolean hasWallOnLeft(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_LEFT);
    }
    public boolean hasWallOnTop(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_TOP);
    }
    public boolean hasWallOnBottom(int x, int y) {
            return hasMaskedBitsTrue(x, y, Constants.CW_BOT);
    }

    public boolean hasNoWallOnBottom(int x, int y) {
            return !hasMaskedBitsTrue(x, y, Constants.CW_BOT);
    }
    public boolean hasNoWallOnTop(int x, int y) {
            return !hasMaskedBitsTrue(x, y, Constants.CW_TOP);
    }
    public boolean hasNoWallOnLeft(int x, int y) {
            return !hasMaskedBitsTrue(x, y, Constants.CW_LEFT);
    }
    public boolean hasNoWallOnRight(int x, int y) {
            return !hasMaskedBitsTrue(x, y, Constants.CW_RIGHT);
    }


    public boolean hasMaskedBitsFalse(int x, int y, int bitmask) {
            return (cells[x][y] & bitmask) == 0;
    }

    public boolean hasMaskedBitsGTZero(int x, int y, int bitmask) {
            return (cells[x][y] & bitmask) > 0;
    }


    private void dbg(String str) {
            System.out.println("Cells: "+str);
    }

    public String toString() {
            String s = "" ;
            for (int i = 0 ; i < width ; i++)
            {
                    for (int j = 0 ; j < height ; j++)
                            s += " i:" + i + " j:" + j + "=" + cells[i][j] ;
                    s += "\n" ;
            }
            return s ;
    }

    static boolean deepdebugWall = false;
    static final String deepedebugWallFileName = "logDeletedWalls.txt" ;
    StringBuffer traceWall = (deepdebugWall) ? new StringBuffer("x  y  dx  dy\n") : null ;


    private void logWall(int x, int y, int dx, int dy) {
            if (null != traceWall)
            {
                    traceWall.append(x + " " + y + " " + dx + " " + dy + "\n");
            }
    }

    public void saveLogFile( String filename )
    {
            try {  
                    BufferedWriter out = new BufferedWriter(new FileWriter(filename));  
            out.write(traceWall.toString());  
            out.close();
    } catch (Exception e) {  
        e.printStackTrace();  
    }  
    }

}

//MazeBuilder.java 


package falstad;

public class MazeBuilder implements Runnable {
        protected int width, height ;
        Maze maze; 
        private int rooms; 
        int expectedPartiters;  
        protected int startx, starty ; 
        protected Cells cells; 
        protected Distance dists ;
        protected SingleRandom random ;
        Thread buildThread; 
        public MazeBuilder(){
            random = SingleRandom.getRandom();
    }

    public MazeBuilder(boolean deterministic){
            if (true == deterministic)
            {
                    System.out.println("Project 2: functionality to make maze generation deterministic not implemented yet! Fix this!");


            }
            random = SingleRandom.getRandom();
    }


    static int getSign(int num) {
            return (num < 0) ? -1 : (num > 0) ? 1 : 0;
    }


    protected void generate() {
            generatePathways();
            final int[] remote = dists.computeDistances(cells) ;
            final int[] pos = dists.getStartPosition();
            startx = pos[0] ;
            starty = pos[1] ;
            setExitPosition(remote[0], remote[1]);
    }


    protected void generatePathways() {
            int[][] origdirs = new int[width][height] ;
            int x = random.nextIntWithinInterval(0, width-1) ;
            int y = 0;
            final int firstx = x ;
            final int firsty = y ;
            int dir = 0;            
            int origdir = dir;      
            cells.setVisitedFlagToZero(x, y);
            while (true) {         
                    int dx = Constants.DIRS_X[dir];
                    int dy = Constants.DIRS_Y[dir];

                    if (!cells.canGo(x, y, dx, dy)) {

                            dir = (dir+1) & 3;
                            if (origdir == dir) {

                                    if (x == firstx && y == firsty)
                                            break;


                                    int odr = origdirs[x][y];
                                    dx = Constants.DIRS_X[odr];
                                    dy = Constants.DIRS_Y[odr];

                                    x -= dx;
                                    y -= dy;
                                    origdir = dir = random.nextIntWithinInterval(0, 3);
                            }
                    } else {
                            cells.deleteWall(x, y, dx, dy);
                            x += dx;
                            y += dy;
                            cells.setVisitedFlagToZero(x, y);
                            origdirs[x][y] = dir;
                            origdir = dir = random.nextIntWithinInterval(0, 3);
                    }
            }
    }

    protected void setExitPosition(int remotex, int remotey) {
            int bit = 0;
            if (remotex == 0)
                    bit = Constants.CW_LEFT;
            else if (remotex == width-1)
                    bit = Constants.CW_RIGHT;
            else if (remotey == 0)
                    bit = Constants.CW_TOP;
            else if (remotey == height-1)
                    bit = Constants.CW_BOT;
            else
                    dbg("Generate 1");
            cells.setBitToZero(remotex, remotey, bit);
            //System.out.println("exit position set to zero: " + remotex + " " + remotey + " " + bit + ":" + cells.hasMaskedBitsFalse(remotex, remotey, bit)
            //              + ", Corner case: " + ((0 == remotex && 0 == remotey) || (0 == remotex &&  height-1 == remotey) || (width-1 == remotex && 0 == remotey) || (width-1 == remotex && height-1 == remotey)));
    }


    static final int MIN_ROOM_DIMENSION = 3 ;
    static final int MAX_ROOM_DIMENSION = 8 ;

    private boolean placeRoom() {
            final int rw = random.nextIntWithinInterval(MIN_ROOM_DIMENSION, MAX_ROOM_DIMENSION);
            if (rw >= width-4)
                    return false;

            final int rh = random.nextIntWithinInterval(MIN_ROOM_DIMENSION, MAX_ROOM_DIMENSION);
            if (rh >= height-4)
                    return false;

            final int rx = random.nextIntWithinInterval(1, width-rw-1);
            final int ry = random.nextIntWithinInterval(1, height-rh-1);
            final int rxl = rx+rw-1;
            final int ryl = ry+rh-1;
            if (cells.areaOverlapsWithRoom(rx, ry, rxl, ryl))
                    return false ;
            cells.markAreaAsRoom(rw, rh, rx, ry, rxl, ryl);
            return true;
    }

    static void dbg(String str) {
            System.out.println("MazeBuilder: "+str);
    }



    public void build(Maze mz, int w, int h, int roomct, int pc) {
            init(mz, w, h, roomct, pc);
            buildThread = new Thread(this);
            buildThread.start();
    }
    private void init(Maze mz, int w, int h, int roomct, int pc) {
            maze = mz;
            width = w;
            height = h;
            rooms = roomct;
            expectedPartiters = pc;
            cells = new Cells(w,h) ;
            dists = new Distance(w,h) ;
            //colchange = random.nextIntWithinInterval(0, 255); 
    }

    static final long SLEEP_INTERVAL = 100 ;
            try {
                    cells.initialize();
                    // rooms into maze
                    generateRooms();

                    Thread.sleep(SLEEP_INTERVAL) ; 

                    // pathways into the maze, 
                    generate();

                    Thread.sleep(SLEEP_INTERVAL) ; 

                    final int colchange = random.nextIntWithinInterval(0, 255);
                    final BSPBuilder b = new BSPBuilder(maze, dists, cells, width, height, colchange, expectedPartiters) ;
                    BSPNode root = b.generateBSPNodes();

                    Thread.sleep(SLEEP_INTERVAL) ; 

                    // dbg("partiters = "+partiters);
                    maze.newMaze(root, cells, dists, startx, starty);
            }
            catch (InterruptedException ex) {
                    // dbg("Catching signal to stop") ;
            }
    }

    static final int MAX_TRIES = 250 ;


    private int generateRooms() {
            int tries = 0 ;
            int result = 0 ;
            while (tries < MAX_TRIES && result <= rooms) {
                    if (placeRoom())
                            result++ ;
                    else
                            tries++ ;
            }
            return result ;
    }

    public void interrupt() {
            buildThread.interrupt() ;
    }
}

Obviously, I'm not asking for someone to write a test for me - not that any of you would, as this is fairly obviously a homework assignment - but I'd like some help getting started on this. So if anyone could clue me into the surely obvious trick that I'm missing, that would be wonderful!

user2309856
  • 101
  • 1
  • 3
  • 10

1 Answers1

1

A unit test for Cells class can look like the below: While you unit test the Cells class you might not need the Maze or MazeBuilder, you only need an instance of Cells class and call its methods in order to check that they work correctly isolated from the rest of the application.

Also take a look at this: Unit testing in Java - what is it? as it provides a good reference.

import org.junit.Assert;
import org.junit.Test;

public class CellsTest {

    @Test
    void testHasWallOnRight() {
        //setup
        int[][] target = new int[][] { { 0, 0, 0, 0 }, 
                { 0, 0, 0, 0 }, 
                { 0, 0, 0, 0 }, 
                { 0, 0, 0, 0 } };

        Cells a = new Cells(target);

        //act
        boolean result = a.hasWallOnRight(1,1);

        //assert
        Assert.assertFalse(result);
    }
}
Community
  • 1
  • 1
Claudiu
  • 1,469
  • 13
  • 21