public static void main(String[] args) {
        testStdIn = canResync();
        
        if (testStdIn) 
            StdOut.println("Note: any errors appear duplicated since tests run 2x.");
        else 
            StdOut.println("Note: StdIn.resync is private, only In will be tested.");

        test("this is a test", 
             new Object[][]{
            {"isEmpty", false}, {"hasNextChar", true}, {"hasNextLine", true},
            {"readAllStrings", new String[]{"this", "is", "a", "test"}},
            {"isEmpty", true}, {"hasNextChar", false}, {"hasNextLine", false}
        });
        test("\n\n\n", 
             new Object[][]{
            {"isEmpty", true}, {"hasNextChar", true}, {"hasNextLine", true},
            {"readAll", "\n\n\n"}
        });
        test("", 
             new Object[][]{
            {"isEmpty", true}, {"hasNextChar", false}, {"hasNextLine", false}
        });
        test("\t\t  \t\t", 
             new Object[][]{
            {"isEmpty", true}, {"hasNextChar", true}, {"hasNextLine", true},
            {"readAll", "\t\t  \t\t"}
        });
        test("readLine consumes newline\nyeah!", 
             new Object[][]{
            {"readLine", "readLine consumes newline"}, {"readChar", 'y'}
        });
        test("readString doesn't consume spaces", 
             new Object[][]{
            {"readString", "readString"}, {"readChar", ' '}
        });
        test("\n\nblank lines test",
             new Object[][]{
            {"readLine", ""}, {"readLine", ""}, {"hasNextLine", true},
            {"readLine", "blank lines test"}, {"hasNextLine", false}
        });
        test("   \n  \t \n  correct  \n\t\n\t .trim replacement \n\t",
             new Object[][]{
            {"readAllStrings", new String[]{"correct", ".trim", "replacement"}},
            {"hasNextChar", false}
        });
        StringBuilder sb = new StringBuilder();
        Object[][] expected = new Object[2000][2];
        for (int i=0; i<1000; i++) {
            sb.append((char)i);
            sb.append((char)(i+8000)); // include weird non-breaking spaces
            expected[2*i][0] = "readChar";
            expected[2*i+1][0] = "readChar";
            expected[2*i][1] = (char)i;
            expected[2*i+1][1] = (char)(i+8000);
        }
        test(sb.toString(), expected);
        test(" this \n and \that \n ",
             new Object[][]{
            {"readString", "this"}, {"readString", "and"}, {"readChar", ' '},
            {"readString", "hat"}, {"readChar", ' '}, {"isEmpty", true},
            {"hasNextLine", true}, {"readLine", ""}, {"readLine", " "}
        });
        test(" 1 2 3 \n\t 4 5 ",
             new Object[][]{
            {"readAllInts", new int[]{1, 2, 3, 4, 5}}
        });
        test(" 0 1 False true falsE True ",
             new Object[][]{
            {"readBoolean", false}, {"readBoolean", true}, 
            {"readBoolean", false}, {"readBoolean", true}, 
            {"readBoolean", false}, {"readBoolean", true}
        });
        test(" \240\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008"
                 + "\u2009\u200A\u205F\u3000",
             new Object[][]{
            {"readString", "\240"},   // non-breaking space - not java whitespace
            {"readString", "\u2007"}, // similarly
            {"hasNextChar", true}, // there is some stuff left over
            {"isEmpty", true},        // but it is all whitespace
            {"readChar", '\u2008'}    // such as this one
        });
        
        // line sep, par sep, NEL, unit sep, vtab --- first 3 are newlines
        // NB: \205 is treated as a line separator, but not whitespace!
        test("a\u2028b\u2029c\37d\13e\205f", 
             new Object[][]{
            {"readAllStrings", new String[]{"a", "b", "c", "d", "e\205f"}}
        });
        test("a\u2028b\u2029c\37d\13e\205f", 
             new Object[][]{
            {"readLine", "a"},
            {"readLine", "b"},
            {"readLine", "c\37d\13e"},
            {"readLine", "f"}
        });
        test("\u2028\u2029", // line separator, par separator
             new Object[][]{
            {"readLine", ""}, {"hasNextLine", true}, {"hasNextChar", true},
            {"readLine", ""}, {"hasNextLine", false}, {"hasNextChar", false}
        });
        test("\n\n", 
             new Object[][]{
            {"readLine", ""}, {"hasNextLine", true}, {"hasNextChar", true},
            {"readLine", ""}, {"hasNextLine", false}, {"hasNextChar", false}
        });
        test("\r\n\r\n", 
             new Object[][]{
            {"readLine", ""}, {"hasNextLine", true}, {"hasNextChar", true},
            {"readLine", ""}, {"hasNextLine", false}, {"hasNextChar", false}
        });
        test("\n\r", 
             new Object[][]{
            {"readLine", ""}, {"hasNextLine", true}, {"hasNextChar", true},
            {"readLine", ""}, {"hasNextLine", false}, {"hasNextChar", false}
        });
        test("\r\n", 
             new Object[][]{
            {"readLine", ""}, {"hasNextChar", false}, {"hasNextLine", false}
        });
        test("3E4 \t -0.5 \n \t +4", 
             new Object[][]{
            {"readAllDoubles", new double[] {30000, -0.5, 4}}
        });
        test(" whitespace ", 
             new Object[][]{
            {"readString", "whitespace"}, {"readChar", ' '},
            {"hasNextLine", false}
        });
        test(" whitespace \n", 
             new Object[][]{
            {"readString", "whitespace"}, {"readChar", ' '},
            {"readLine", ""}, {"hasNextLine", false}
        });
        test(" whitespace \n ", 
             new Object[][]{
            {"readString", "whitespace"}, {"readChar", ' '},
            {"readLine", ""}, {"hasNextLine", true},
            {"readLine", " "}, {"hasNextLine", false}
        });
        test(" 34 -12983   3.25\n\t foo!",
             new Object[][]{
            {"readByte", (byte)34}, 
            {"readShort", (short)-12983},
            {"readDouble", 3.25},
            {"readAll", "\n\t foo!"}
        });
        test("30000000000  3.5 3e4, foo   \t\t ya",
             new Object[][]{
            {"readLong", 30000000000L},
            {"readFloat", (float)3.5},
            {"readAllStrings", new String[]{"3e4,", "foo", "ya"}}
        });
        // testing consistency of whitespace and read(All)String(s)
        test(" \u0001 foo \u0001 foo \u0001 foo",
             new Object[][]{
            {"readAllStrings", new String[]{
                "\u0001", "foo", "\u0001", "foo", "\u0001", "foo"}}
        });
        test(" \u2005 foo \u2005 foo \u2005 foo",
             new Object[][]{
            {"readAllStrings", new String[]{"foo", "foo", "foo"}}
        });
        test(" \u0001 foo \u0001 foo \u0001 foo",
             new Object[][]{
            {"readString", "\u0001"}, {"readString", "foo"},
            {"readString", "\u0001"}, {"readString", "foo"},
            {"readString", "\u0001"}, {"readString", "foo"}
        });
        test(" \u2005 foo \u2005 foo \u2005 foo",
             new Object[][]{
            {"readString", "foo"}, {"readString", "foo"}, {"readString", "foo"}
        });
        StdOut.printf("Ran %d tests.\n", testCount);
    }