@Test
public void testUrlDecoding() throws Exception {

    final String caffe = new String(
            // "Caffé" but instead of putting the literal E-acute in the
            // source file, we directly use the UTF-8 encoding so as to
            // not rely on the platform's default encoding (not portable).
            new byte[] {'C', 'a', 'f', 'f', (byte) 0xC3, (byte) 0xA9},
            "UTF-8");
    final String[] tests = {
        // Encoded   ->   Decoded or error message substring
        "",               "",
        "foo",            "foo",
        "f+o",            "f o",
        "f++",            "f  ",
        "fo%",            "unterminated escape sequence at index 2 of: fo%",
        "%42",            "B",
        "%5f",            "_",
        "f%4",            "unterminated escape sequence at index 1 of: f%4",
        "%x2",            "invalid hex byte 'x2' at index 1 of '%x2'",
        "%4x",            "invalid hex byte '4x' at index 1 of '%4x'",
        "Caff%C3%A9",     caffe,
        "случайный праздник",               "случайный праздник",
        "случайный%20праздник",             "случайный праздник",
        "случайный%20праздник%20%E2%98%BA", "случайный праздник ☺",
    };
    for (int i = 0; i < tests.length; i += 2) {
        final String encoded = tests[i];
        final String expected = tests[i + 1];
        try {
            final String decoded = QueryStringDecoder.decodeComponent(encoded);
            Assert.assertEquals(expected, decoded);
        } catch (IllegalArgumentException e) {
            Assert.assertEquals(expected, e.getMessage());
        }
    }
}