private static void testSysOut(String fs, String exp, Object... args) {
    FileOutputStream fos = null;
    FileInputStream fis = null;
    try {
      PrintStream saveOut = System.out;
      fos = new FileOutputStream("testSysOut");
      System.setOut(new PrintStream(fos));
      System.out.format(Locale.US, fs, args);
      fos.close();

      fis = new FileInputStream("testSysOut");
      byte[] ba = new byte[exp.length()];
      int len = fis.read(ba);
      String got = new String(ba);
      if (len != ba.length) fail(fs, exp, got);
      ck(fs, exp, got);

      System.setOut(saveOut);
    } catch (FileNotFoundException ex) {
      fail(fs, ex.getClass());
    } catch (IOException ex) {
      fail(fs, ex.getClass());
    } finally {
      try {
        if (fos != null) fos.close();
        if (fis != null) fis.close();
      } catch (IOException ex) {
        fail(fs, ex.getClass());
      }
    }
  }
  public static void test() {
    TimeZone.setDefault(TimeZone.getTimeZone("GMT-0800"));

    // Any characters not explicitly defined as conversions, date/time
    // conversion suffixes, or flags are illegal and are reserved for
    // future extensions.  Use of such a character in a format string will
    // cause an UnknownFormatConversionException or
    // UnknownFormatFlagsException to be thrown.
    tryCatch("%q", UnknownFormatConversionException.class);
    tryCatch("%t&", UnknownFormatConversionException.class);
    tryCatch("%&d", UnknownFormatConversionException.class);
    tryCatch("%^b", UnknownFormatConversionException.class);

    // ---------------------------------------------------------------------
    // Formatter.java class javadoc examples
    // ---------------------------------------------------------------------
    test(Locale.FRANCE, "e = %+10.4f", "e =    +2,7183", Math.E);
    test("%4$2s %3$2s %2$2s %1$2s", " d  c  b  a", "a", "b", "c", "d");
    test(
        "Amount gained or lost since last statement: $ %,(.2f",
        "Amount gained or lost since last statement: $ (6,217.58)", (new BigDecimal("-6217.58")));
    Calendar c = new GregorianCalendar(1969, JULY, 20, 16, 17, 0);
    testSysOut("Local time: %tT", "Local time: 16:17:00", c);

    test(
        "Unable to open file '%1$s': %2$s",
        "Unable to open file 'food': No such file or directory",
        "food",
        "No such file or directory");
    Calendar duke = new GregorianCalendar(1995, MAY, 23, 19, 48, 34);
    duke.set(Calendar.MILLISECOND, 584);
    test("Duke's Birthday: %1$tB %1$te, %1$tY", "Duke's Birthday: May 23, 1995", duke);
    test("Duke's Birthday: %1$tB %1$te, %1$tY", "Duke's Birthday: May 23, 1995", duke.getTime());
    test(
        "Duke's Birthday: %1$tB %1$te, %1$tY",
        "Duke's Birthday: May 23, 1995", duke.getTimeInMillis());

    test("%4$s %3$s %2$s %1$s %4$s %3$s %2$s %1$s", "d c b a d c b a", "a", "b", "c", "d");
    test("%s %s %<s %<s", "a b b b", "a", "b", "c", "d");
    test("%s %s %s %s", "a b c d", "a", "b", "c", "d");
    test("%2$s %s %<s %s", "b a a b", "a", "b", "c", "d");

    // ---------------------------------------------------------------------
    // %b
    //
    // General conversion applicable to any argument.
    // ---------------------------------------------------------------------
    test("%b", "true", true);
    test("%b", "false", false);
    test("%B", "TRUE", true);
    test("%B", "FALSE", false);
    test("%b", "true", Boolean.TRUE);
    test("%b", "false", Boolean.FALSE);
    test("%B", "TRUE", Boolean.TRUE);
    test("%B", "FALSE", Boolean.FALSE);
    test("%14b", "          true", true);
    test("%-14b", "true          ", true);
    test("%5.1b", "    f", false);
    test("%-5.1b", "f    ", false);

    test("%b", "true", "foo");
    test("%b", "false", (Object) null);

    // Boolean.java hardcodes the Strings for "true" and "false", so no
    // localization is possible.
    test(Locale.FRANCE, "%b", "true", true);
    test(Locale.FRANCE, "%b", "false", false);

    // If you pass in a single array to a varargs method, the compiler
    // uses it as the array of arguments rather than treating it as a
    // single array-type argument.
    test("%b", "false", (Object[]) new String[2]);
    test("%b", "true", new String[2], new String[2]);

    int[] ia = {1, 2, 3};
    test("%b", "true", ia);

    // ---------------------------------------------------------------------
    // %b - errors
    // ---------------------------------------------------------------------
    tryCatch("%#b", FormatFlagsConversionMismatchException.class);
    tryCatch("%-b", MissingFormatWidthException.class);
    // correct or side-effect of implementation?
    tryCatch("%.b", UnknownFormatConversionException.class);
    tryCatch("%,b", FormatFlagsConversionMismatchException.class);

    // ---------------------------------------------------------------------
    // %c
    //
    // General conversion applicable to any argument.
    // ---------------------------------------------------------------------
    test("%c", "i", 'i');
    test("%C", "I", 'i');
    test("%4c", "   i", 'i');
    test("%-4c", "i   ", 'i');
    test("%4C", "   I", 'i');
    test("%-4C", "I   ", 'i');
    test("%c", "i", new Character('i'));
    test("%c", "H", (byte) 72);
    test("%c", "i", (short) 105);
    test("%c", "!", (int) 33);
    test("%c", "\u007F", Byte.MAX_VALUE);
    test("%c", new String(Character.toChars(Short.MAX_VALUE)), Short.MAX_VALUE);
    test("%c", "null", (Object) null);

    // ---------------------------------------------------------------------
    // %c - errors
    // ---------------------------------------------------------------------
    tryCatch("%c", IllegalFormatConversionException.class, Boolean.TRUE);
    tryCatch("%c", IllegalFormatConversionException.class, (float) 0.1);
    tryCatch("%c", IllegalFormatConversionException.class, new Object());
    tryCatch("%c", IllegalFormatCodePointException.class, Byte.MIN_VALUE);
    tryCatch("%c", IllegalFormatCodePointException.class, Short.MIN_VALUE);
    tryCatch("%c", IllegalFormatCodePointException.class, Integer.MIN_VALUE);
    tryCatch("%c", IllegalFormatCodePointException.class, Integer.MAX_VALUE);

    tryCatch("%#c", FormatFlagsConversionMismatchException.class);
    tryCatch("%,c", FormatFlagsConversionMismatchException.class);
    tryCatch("%(c", FormatFlagsConversionMismatchException.class);
    tryCatch("%$c", UnknownFormatConversionException.class);
    tryCatch("%.2c", IllegalFormatPrecisionException.class);

    // ---------------------------------------------------------------------
    // %s
    //
    // General conversion applicable to any argument.
    // ---------------------------------------------------------------------
    test("%s", "Hello, Duke", "Hello, Duke");
    test("%S", "HELLO, DUKE", "Hello, Duke");
    test("%20S", "         HELLO, DUKE", "Hello, Duke");
    test("%20s", "         Hello, Duke", "Hello, Duke");
    test("%-20s", "Hello, Duke         ", "Hello, Duke");
    test("%-20.5s", "Hello               ", "Hello, Duke");
    test("%s", "null", (Object) null);

    StringBuffer sb = new StringBuffer("foo bar");
    test("%s", sb.toString(), sb);
    test("%S", sb.toString().toUpperCase(), sb);

    // ---------------------------------------------------------------------
    // %s - errors
    // ---------------------------------------------------------------------
    tryCatch("%-s", MissingFormatWidthException.class);
    tryCatch("%--s", DuplicateFormatFlagsException.class);
    tryCatch("%#s", FormatFlagsConversionMismatchException.class, 0);
    tryCatch("%#s", FormatFlagsConversionMismatchException.class, 0.5f);
    tryCatch("%#s", FormatFlagsConversionMismatchException.class, "hello");
    tryCatch("%#s", FormatFlagsConversionMismatchException.class, null);

    // ---------------------------------------------------------------------
    // %h
    //
    // General conversion applicable to any argument.
    // ---------------------------------------------------------------------
    test("%h", Integer.toHexString("Hello, Duke".hashCode()), "Hello, Duke");
    test("%10h", "  ddf63471", "Hello, Duke");
    test("%-10h", "ddf63471  ", "Hello, Duke");
    test("%-10H", "DDF63471  ", "Hello, Duke");
    test("%10h", "  402e0000", 15.0);
    test("%10H", "  402E0000", 15.0);

    // ---------------------------------------------------------------------
    // %h - errors
    // ---------------------------------------------------------------------
    tryCatch("%#h", FormatFlagsConversionMismatchException.class);

    // ---------------------------------------------------------------------
    // flag/conversion errors
    // ---------------------------------------------------------------------
    tryCatch("%F", UnknownFormatConversionException.class);

    tryCatch("%#g", FormatFlagsConversionMismatchException.class);

    // ---------------------------------------------------------------------
    // BigInteger - errors
    // ---------------------------------------------------------------------
    tryCatch("%f", IllegalFormatConversionException.class, new BigInteger("1"));

    // ---------------------------------------------------------------------
    // %d - BigInteger
    // ---------------------------------------------------------------------
    test("%d", "null", (Object) null);
    test("%d", "1234567", new BigInteger("1234567", 10));
    test("%,d", "1,234,567", new BigInteger("1234567", 10));
    test(Locale.FRANCE, "%,d", "1\u00a0234\u00a0567", new BigInteger("1234567", 10));
    test("%,d", "-1,234,567", new BigInteger("-1234567", 10));
    test("%(d", "1234567", new BigInteger("1234567", 10));
    test("%(d", "(1234567)", new BigInteger("-1234567", 10));
    test("% d", " 1234567", new BigInteger("1234567", 10));
    test("% d", "-1234567", new BigInteger("-1234567", 10));
    test("%+d", "+1234567", new BigInteger("1234567", 10));
    test("%+d", "-1234567", new BigInteger("-1234567", 10));
    test("%010d", "0001234567", new BigInteger("1234567", 10));
    test("%010d", "-001234567", new BigInteger("-1234567", 10));
    test("%(10d", " (1234567)", new BigInteger("-1234567", 10));
    test("%+d", "+1234567", new BigInteger("1234567", 10));
    test("%+d", "-1234567", new BigInteger("-1234567", 10));
    test("%-10d", "1234567   ", new BigInteger("1234567", 10));
    test("%-10d", "-1234567  ", new BigInteger("-1234567", 10));

    // ---------------------------------------------------------------------
    // %o - BigInteger
    // ---------------------------------------------------------------------
    test("%o", "null", (Object) null);
    test("%o", "1234567", new BigInteger("1234567", 8));
    test("%(o", "1234567", new BigInteger("1234567", 8));
    test("%(o", "(1234567)", new BigInteger("-1234567", 8));
    test("% o", " 1234567", new BigInteger("1234567", 8));
    test("% o", "-1234567", new BigInteger("-1234567", 8));
    test("%+o", "+1234567", new BigInteger("1234567", 8));
    test("%+o", "-1234567", new BigInteger("-1234567", 8));
    test("%010o", "0001234567", new BigInteger("1234567", 8));
    test("%010o", "-001234567", new BigInteger("-1234567", 8));
    test("%(10o", " (1234567)", new BigInteger("-1234567", 8));
    test("%+o", "+1234567", new BigInteger("1234567", 8));
    test("%+o", "-1234567", new BigInteger("-1234567", 8));
    test("%-10o", "1234567   ", new BigInteger("1234567", 8));
    test("%-10o", "-1234567  ", new BigInteger("-1234567", 8));
    test("%#10o", "  01234567", new BigInteger("1234567", 8));
    test("%#10o", " -01234567", new BigInteger("-1234567", 8));

    // ---------------------------------------------------------------------
    // %x - BigInteger
    // ---------------------------------------------------------------------
    test("%x", "null", (Object) null);
    test("%x", "1234567", new BigInteger("1234567", 16));
    test("%(x", "1234567", new BigInteger("1234567", 16));
    test("%(x", "(1234567)", new BigInteger("-1234567", 16));
    test("% x", " 1234567", new BigInteger("1234567", 16));
    test("% x", "-1234567", new BigInteger("-1234567", 16));
    test("%+x", "+1234567", new BigInteger("1234567", 16));
    test("%+x", "-1234567", new BigInteger("-1234567", 16));
    test("%010x", "0001234567", new BigInteger("1234567", 16));
    test("%010x", "-001234567", new BigInteger("-1234567", 16));
    test("%(10x", " (1234567)", new BigInteger("-1234567", 16));
    test("%+x", "+1234567", new BigInteger("1234567", 16));
    test("%+x", "-1234567", new BigInteger("-1234567", 16));
    test("%-10x", "1234567   ", new BigInteger("1234567", 16));
    test("%-10x", "-1234567  ", new BigInteger("-1234567", 16));
    test("%#10x", " 0x1234567", new BigInteger("1234567", 16));
    test("%#10x", "-0x1234567", new BigInteger("-1234567", 16));
    test("%#10X", " 0X1234567", new BigInteger("1234567", 16));
    test("%#10X", "-0X1234567", new BigInteger("-1234567", 16));
    test("%X", "1234567A", new BigInteger("1234567a", 16));
    test("%X", "-1234567A", new BigInteger("-1234567a", 16));

    // ---------------------------------------------------------------------
    // %t
    //
    // Date/Time conversions applicable to Calendar, Date, and long.
    // ---------------------------------------------------------------------
    test("%tA", "null", (Object) null);
    test("%TA", "NULL", (Object) null);

    // ---------------------------------------------------------------------
    // %t - errors
    // ---------------------------------------------------------------------
    tryCatch("%t", UnknownFormatConversionException.class);
    tryCatch("%T", UnknownFormatConversionException.class);
    tryCatch("%tP", UnknownFormatConversionException.class);
    tryCatch("%TP", UnknownFormatConversionException.class);
    tryCatch("%.5tB", IllegalFormatPrecisionException.class);
    tryCatch("%#tB", FormatFlagsConversionMismatchException.class);
    tryCatch("%-tB", MissingFormatWidthException.class);

    // ---------------------------------------------------------------------
    // %n
    // ---------------------------------------------------------------------
    test("%n", System.getProperty("line.separator"), (Object) null);
    test("%n", System.getProperty("line.separator"), "");

    tryCatch("%,n", IllegalFormatFlagsException.class);
    tryCatch("%.n", UnknownFormatConversionException.class);
    tryCatch("%5.n", UnknownFormatConversionException.class);
    tryCatch("%5n", IllegalFormatWidthException.class);
    tryCatch("%.7n", IllegalFormatPrecisionException.class);
    tryCatch("%<n", IllegalFormatFlagsException.class);

    // ---------------------------------------------------------------------
    // %%
    // ---------------------------------------------------------------------
    test("%%", "%", (Object) null);
    test("%%", "%", "");
    tryCatch("%%%", UnknownFormatConversionException.class);
    // perhaps an IllegalFormatArgumentIndexException should be defined?
    tryCatch("%<%", IllegalFormatFlagsException.class);
  }