import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import junit.framework.TestCase;

/**
 * Reference tests for SecondsToHours (Lab03B).
 *
 * @author R.Grove
 * @author C.Mayfield
 * @version 1.3, 09/13/2012 at 9pm
 */
public class Lab04A_ReferenceTest extends TestCase {
    
    /**
     * Test the input prompt.
     */
    public void testPrompt() {
        String input = "1000\n";
        String expected = "Enter the number of seconds: ";
        
        // redirect standard in/out
        System.setIn(new ByteArrayInputStream(input.getBytes()));
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        System.setOut(new PrintStream(output));
        
        // call main and verify the output
        SecondsToHours.main(new String[] {});
        String actual = output.toString();
        if (!actual.startsWith(expected)) {
            fail("Check the format/spelling of your prompt");
        }
    }
    
    /**
     * Test the output format.
     */
    public void testOutput() {
        String input = "1000\n";
        
        // regex for each line of output
        String[] expected = 
        {
            ".*", // ignore prompt
            "\\b\\d+\\b seconds = \\b\\d+\\b hours, \\b\\d+\\b minutes, and \\b\\d+\\b seconds"
        };
        
        // redirect standard in/out
        System.setIn(new ByteArrayInputStream(input.getBytes()));
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        System.setOut(new PrintStream(output));
        
        // call main and verify the output
        SecondsToHours.main(new String[] {});
        String[] actual = getLines(output.toString());
        if (actual.length != 2) 
        {
            fail("Incorrect output: have " + actual.length
                     + " lines, but need " + 2);
        }
        // check the format of each line
        for (int i = 0; i < expected.length; i++) 
        {
//        	System.err.println(expected[i]);
            assertTrue("Invalid output format on line " + (i+1),
                       actual[1].matches(expected[i]));
        }
    }
    
    /**
     * Invoke value test cases in this order.
     */
    public void testValues() {
        doFullTest(0);
        doFullTest(10);
        doFullTest(100);
        doFullTest(1000);
        doFullTest(10000);
    }
    
    /**
     * Split the string by newlines, but preserve any blank lines.
     */
    private String[] getLines(String s) {
        // count the number of newlines
        int n = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '\n') {
                n++;
            }
        }
        // copy the resulting lines
        int beg = 0;
        int cur = 0;
        String[] lines = new String[n];
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '\n') {
                lines[cur] = s.substring(beg, i);
                beg = i + 1; // strip newline
                cur++;
            }
        }
        return lines;
    }
    
    /**
     * Test the main method for correct prompts and output.
     */
    private void doFullTest(int sec) {
        
        // compute the expected answers
        int hh = sec / 3600;
        int mm = sec % 3600 / 60;
        int ss = sec % 60;
        
        // build the input/output strings
        String input = sec + "\n";
        String expected =
            // prompts
            "Enter the number of seconds: "
            // report
            + "\n"
            + sec + " seconds = "
            + hh + " hours, "
            + mm + " minutes, and "
            + ss + " seconds\n";
        
        // set up the input and output streams
        System.setIn(new ByteArrayInputStream(input.getBytes()));
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        System.setOut(new PrintStream(output));
        
        // call main and compare the output
        SecondsToHours.main(new String[] {});
        assertEquals("Try running your program with input " + sec,
                     expected, output.toString());
    }
    
}
