001/**
002 * This is part of HW0: Environment Setup and Java Introduction for CSE 331.
003 */
004package hw3.test;
005import hw3.*;
006
007import java.util.Iterator;
008import java.util.LinkedList;
009import java.util.Random;
010import java.util.Set;
011import java.util.HashSet;
012
013import org.junit.Test;
014import org.junit.BeforeClass;
015import static org.junit.Assert.*;
016import static org.hamcrest.Matchers.greaterThan;
017
018/**
019 * BoxTest is a glassbox test of the Box class.
020 *
021 * Recall that like a BallContainer, the Box is a container for Balls and you
022 * can only put a Ball into a Box once. After you put the Ball into
023 * the Box, further attempts to do so will fail, since the Ball is
024 * already in the Box! Similarly, you cannot expect to remove a Ball
025 * from a Box if it is not inside the Box.
026 *
027 * In addition, a Box differs from a ballcontainer because it only has a finite
028 * volume. As Balls get put into a Box, it gets filled up. Once a Box
029 * is full, further attempts to put Balls into the Box will also fail.
030 *
031 * @see hw3.Ball
032 * @see hw3.BallContainer
033 * @see hw3.Box
034 */
035@SuppressWarnings("nullness")
036public class BoxTest {
037
038    private static Box box = null;
039    private static Ball[] b = null;
040
041    private static final int NUM_BALLS_TO_TEST = 5;
042    private static final int BOX_CAPACITY = NUM_BALLS_TO_TEST - 1;
043    private static final double BALL_UNIT_VOLUME = 10.0;
044    private static final double JUNIT_DOUBLE_DELTA = 0.0001;
045    private static final int TRIES_FOR_BALLS_TEST = 3;
046
047    @BeforeClass
048    public static void setupBeforeTests() throws Exception {
049        assertThat("Test case error, you must test at least 1 Ball!!",
050                   NUM_BALLS_TO_TEST, greaterThan(0));
051        assertThat("This test case is set up assuming that the box cannot contain all the balls, please check and change parameters!",
052                   NUM_BALLS_TO_TEST, greaterThan(BOX_CAPACITY));
053
054        double box_volume = 0;
055
056        // Let's create the balls we need.
057        b = new Ball[NUM_BALLS_TO_TEST];
058        for (int i=0; i<NUM_BALLS_TO_TEST; i++) {
059            if (i<BOX_CAPACITY) {
060                box_volume += (i+1)*BALL_UNIT_VOLUME;
061            }
062            b[i] = new Ball((i+1)*BALL_UNIT_VOLUME);
063        }
064
065        // Now, we create the box once we figure out how big a box we
066        // need.
067        box = new Box(box_volume);
068
069    }
070
071    /** Test to check that Box.add(Ball) is implemented correctly */
072    @Test
073    public void testAdd() {
074        box.clear();
075        for (int i=0; i<BOX_CAPACITY; i++) {
076            assertTrue("Box.add(Ball) failed to add a new Ball!",
077                       box.add(b[i]));
078            assertFalse("Box.add(Ball) seems to allow the same Ball to be added twice!",
079                        box.add(b[i]));
080            assertTrue("Box does not contain a ball after it is supposed to have been added!",
081                       box.contains(b[i]));
082        }
083        for (int i=BOX_CAPACITY; i<NUM_BALLS_TO_TEST; i++) {
084            assertFalse("Box.add(Ball) allows a Ball to be added even though it is already full!",
085                        box.add(b[i]));
086        }
087    }
088
089    /** Test to check that Box.getBallsFromSmallest() is implemented correctly */
090    @Test
091    public void testGetBalls() {
092        Random rnd = new Random();
093
094        for (int k=0; k<TRIES_FOR_BALLS_TEST; k++) {
095
096            box.clear();
097
098            // Let's put all the balls into a list.
099            LinkedList<Ball> list = new LinkedList<Ball>();
100            for (int i=0; i<BOX_CAPACITY; i++) {
101                list.add(b[i]);
102            }
103
104            // First we add the balls to the box in some random order.
105            for (int i=0; i<BOX_CAPACITY; i++) {
106                box.add(list.remove(rnd.nextInt(list.size())));
107            }
108
109            int contentsSize = box.size();
110            // Next we call the iterator and check that the balls come out
111            // in the correct order.
112            Iterator<Ball> it = box.getBallsFromSmallest();
113            int count = 0;
114            while (it.hasNext() && count < BOX_CAPACITY) {
115                Ball ball = it.next();
116                assertEquals("Balls are not returned by Box.getBallsFromSmallest() iterator in the correct order",
117                             b[count], ball);
118                if (b[count] != ball) {
119                    break;
120                }
121                count++;
122            }
123            assertEquals("Box.getBallsFromSmallest() did not return all the balls",
124                         BOX_CAPACITY, count);
125            assertEquals("Number of balls in box was modified",
126                         contentsSize, box.size());
127        }
128    }
129
130    /**
131     * Test to check that Box.remove(Ball) is implemented
132     * correctly. Depending on how <code>getBallsFromSmallest()</code>
133     * is implemented, remove() might have to be overridden and this
134     * test helps ensure that remove() is not broken in the process.
135     */
136    @Test
137    public void testRemove() {
138        box.clear();
139        assertFalse("Box.remove(Ball) should fail because box is empty, but it didn't!",
140                    box.remove(b[0]));
141        for (int i=0; i<BOX_CAPACITY; i++) {
142            box.clear();
143            for (int j=0; j<i; j++) {
144                box.add(b[j]);
145            }
146            for (int j=0; j<i; j++) {
147                assertTrue("Box.remove(Ball) failed to remove a Ball that is supposed to be inside",
148                           box.remove(b[j]));
149                assertFalse("Box still contains a ball after it is supposed to have been removed!",
150                            box.contains(b[j]));
151            }
152            for (int j=i; j<NUM_BALLS_TO_TEST; j++) {
153                assertFalse("Box.remove(Ball) did not fail for a Ball that is not inside",
154                            box.remove(b[j]));
155            }
156        }
157    }
158
159
160    /** Test to check that Box.clear() is implemented correctly */
161    @Test
162    public void testClear() {
163        box.clear();
164        assertEquals("Box is not empty after being cleared!", 0, box.size());
165        box.add(b[0]);
166        box.clear();
167        assertEquals("Box is not empty after being cleared!", 0, box.size());
168    }
169
170    /** Test to check that check that we can put a Ball into a Box */
171    @Test
172    public void testVolume() {
173        box.clear();
174        assertEquals("Volume of empty Box is not zero!",
175                     0, box.getVolume(), JUNIT_DOUBLE_DELTA);
176        for (int i=0; i<BOX_CAPACITY; i++) {
177            box.add(b[i]);
178            assertEquals("Volume of Box with one ball",
179                         (i+1)*(i+2)*BALL_UNIT_VOLUME/2, box.getVolume(),
180                         JUNIT_DOUBLE_DELTA);
181        }
182    }
183
184    /** Test to check that size() returns the correct number. */
185    @Test
186    public void testSize() {
187        box.clear();
188        assertEquals("size() of empty Box is not zero!", 0, box.size());
189        for (int i=0; i<BOX_CAPACITY; i++) {
190            box.add(b[i]);
191            assertEquals("size() of Box with "+(i+1)+" ball(s)",
192                         i+1, box.size());
193        }
194    }
195
196    /** Test to check that size() returns the correct number. */
197    @Test
198    public void testContains() {
199        box.clear();
200        for (int i=0; i<BOX_CAPACITY; i++) {
201            assertFalse("Empty Box seems to contain a ball!",
202                        box.contains(b[i]));
203        }
204        for (int i=0; i<BOX_CAPACITY; i++) {
205            box.add(b[i]);
206            assertTrue("Box does not contain a Ball that is supposed to be inside!",
207                       box.contains(b[i]));
208            for (int j=i+1; j<BOX_CAPACITY; j++) {
209                assertFalse("Box seems to contain a Ball that is not inside!",
210                            box.contains(b[j]));
211            }
212        }
213    }
214
215    /**
216     * Test to check that iterator() is implemented correctly.
217     */
218    @Test
219    public void testIterator() {
220        Set<Ball> allBalls = new HashSet<Ball>();
221        Set<Ball> seenBalls = new HashSet<Ball>();
222        box.clear();
223        for (Ball aBall : b) {
224            box.add(aBall);
225            allBalls.add(aBall);
226        }
227        int i = 0;
228        for (Ball aBall : box) {
229            assertTrue("Iterator returned a ball that isn't in the container: " + aBall,
230                       allBalls.contains(aBall));
231            assertFalse("Iterator returned the same ball twice: " + aBall,
232                        seenBalls.contains(aBall));
233            seenBalls.add(aBall);
234            i++;
235        }
236        assertEquals("BallContainer iterator did not return enough items!",
237                     b.length-1, i);
238    }
239}