// for a JUnit 4.4 test case, 
//    you generally import the following:

import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

/**
 * a JUnit 4.4 test class for class GameDie,
 *     developed during the CS 458 Week 5 Labs
 *     trying to use a TDD (test-driven
 *     development) approach
 *     *   first, write a test
 *     *   then, write just enough code
 *         to pass that test
 * (yes, this is a bit more extreme than
 *  test-first, writing your tests for
 *  a module and then writing the module)
 * 
 * @author Sharon Tuttle
 * @version 2017-09-20
 */

public class GameDieTest
{
    // data fields
    
    // create one or more instances of the
    //    class under test as private data fields
    //    of the testing class
    
    private GameDie d6;
    private GameDie d12;
    
    // the @Before annotation is used for
    //   a method usually named setUp;
    // *   there is only ONE of these methods
    //     per JUnit test class
    // *   this method is run once before
    //     EACH unit test method
    
    /**
     * create two example GameDie instances, one using
     *     each constructor, prior to each test method
     */

    @Before
    public void setUp() throws Exception
    {
        d6 = new GameDie();
        d12 = new GameDie(12);
    }
    
    // use the @Test annotation to signal
    //    that the next method is a test case
    //    method
    // (usually, their names start with test, also)
    
    /**
     * test method for method getNumSides
     */
    
    @Test
    public void testGetNumSides()
    {
        assertEquals("d6 should have 6 sides",
                     6, d6.getNumSides());
        assertEquals("d12 should have 12 sides",
                     12, d12.getNumSides());
    }
    
    /**
     * test method for method getTop
     */
    
    @Test
    public void testGetTop()
    {
        // does a newly-created die have
        //     a top of 1?
        // (remember, @Before method setUp sets up a fresh
        //     new GameDie in d6 and in d12 for each test 
        //     method)
        
        assertEquals("d6 top should be 1",
                     1, d6.getTop());
        assertEquals("d12 top should be 1",
                     1, d12.getTop());
        
        // is a rolled die's top equal
        //    to its previous roll?
        
        int d6Roll = d6.roll();
        int d12Roll = d12.roll();
        
        assertEquals("d6 top should equal its last roll",
                     d6Roll, d6.getTop());
        assertEquals("d12 top should equal its last roll",
                     d12Roll, d12.getTop());
    }
    
    /**
     * try to test whether method roll works reasonably
     */
    
    @Test
    public void testRoll()
    {
        // putting in loops from both labs... 8-)
        
        int testRollResult;
        
        // roll d6 many times, make sure its result
        //     is always in the required range and its
        //     top is always appropriately reset
        
        for (int i=0; i<10000000; i++)
        {
            testRollResult = d6.roll();
            assertTrue("d6 roll should be " +
                       "between 1 and 6",
                       ((testRollResult == 1) ||
                        (testRollResult == 2) ||
                        (testRollResult == 3) ||
                        (testRollResult == 4) ||
                        (testRollResult == 5) ||
                        (testRollResult == 6)));
             assertEquals("d6 top should equal latest d6 roll",
                          testRollResult, d6.getTop());
        }
        
        // roll d12 many times, make sure its result
        //     is always in the required range and its
        //     top is always appropriately reset
        
        for (int i=0; i<64700900; i++)
        {
            testRollResult = d12.roll();
            assertTrue("roll of d12 must be in [1, 12]",
                       ((testRollResult == 1) ||
                        (testRollResult == 2) ||
                        (testRollResult == 3) ||
                        (testRollResult == 4) ||
                        (testRollResult == 5) ||
                        (testRollResult == 6) ||
                        (testRollResult == 7) ||
                        (testRollResult == 8) ||
                        (testRollResult == 9) ||
                        (testRollResult == 10) ||
                        (testRollResult == 11) ||
                        (testRollResult == 12)));
            
            assertEquals("d12 top should equal latest d12 roll",
                         testRollResult, d12.getTop());
        } 
        
        // post-lab, just-for-fun:
        // let's attempt to verify that roll does not
        //     always return the same roll value
        
        int d6RollResults[] = new int[d6.getNumSides()];
        int d12RollResults[] = new int[d12.getNumSides()];
        
        // initially, all values have been rolled 0 times
        
        for (int i=0; i<d6.getNumSides(); i++)
        {
            d6RollResults[i] = 0;
        }
        
        for (int i=0; i<d12.getNumSides(); i++)
        {
            d12RollResults[i] = 0;
        }
        
        // now roll each 10000 times, and log number of
        //     times each value is rolled
        // (will log a roll of i in index (i-1)...)
        
        for (int i=0; i<10000; i++)
        {
            testRollResult = d6.roll();
            d6RollResults[(testRollResult-1)]++;
            
            testRollResult = d12.roll();
            d12RollResults[(testRollResult-1)]++;
        }
        
        // what I am worried about: probabilistically,
        //     is this a reasonable test?
        // BUT: in 10000 rolls, SHOULD one expect every
        //     value to be rolled at least once?
        
        for (int i=0; i<d6.getNumSides(); i++)
        {
            assertTrue("did not roll " + (i+1) + " at least "
                       + "once in 10000 d6 rolls", 
                       d6RollResults[i] > 0);
        }
        
        for (int i=0; i<d12.getNumSides(); i++)
        {
            assertTrue("did not roll " + (i+1) + " at least "
                       + "once in 10000 d12 rolls", 
                       d12RollResults[i] > 0);
        }
    }
}