1e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/*
2e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Copyright (C) 2014 Square, Inc.
3e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
4e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
5e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * you may not use this file except in compliance with the License.
6e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * You may obtain a copy of the License at
7e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
8e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
9e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
10e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Unless required by applicable law or agreed to in writing, software
11e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
12e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * See the License for the specific language governing permissions and
14e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * limitations under the License.
15e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */
16e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpackage okio;
17e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
187aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fullerimport java.io.EOFException;
19e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.Test;
20e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
21e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertEquals;
227aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fullerimport static org.junit.Assert.assertFalse;
237aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fullerimport static org.junit.Assert.assertTrue;
247aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fullerimport static org.junit.Assert.fail;
25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpublic final class Utf8Test {
27e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void oneByteCharacters() throws Exception {
28e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("00", 0x00); // Smallest 1-byte character.
29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("20", ' ');
30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("7e", '~');
31e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("7f", 0x7f); // Largest 1-byte character.
32e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
33e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
34e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void twoByteCharacters() throws Exception {
35e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("c280", 0x0080); // Smallest 2-byte character.
36e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("c3bf", 0x00ff);
37e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("c480", 0x0100);
38e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("dfbf", 0x07ff); // Largest 2-byte character.
39e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
41e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void threeByteCharacters() throws Exception {
42e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("e0a080", 0x0800); // Smallest 3-byte character.
43e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("e0bfbf", 0x0fff);
44e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("e18080", 0x1000);
45e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("e1bfbf", 0x1fff);
46e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("ed8080", 0xd000);
47e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("ed9fbf", 0xd7ff); // Largest character lower than the min surrogate.
48e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("ee8080", 0xe000); // Smallest character greater than the max surrogate.
49e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("eebfbf", 0xefff);
50e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("ef8080", 0xf000);
51e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("efbfbf", 0xffff); // Largest 3-byte character.
52e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
53e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
54e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void fourByteCharacters() throws Exception {
55e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("f0908080", 0x010000); // Smallest surrogate pair.
56e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEncoded("f48fbfbf", 0x10ffff); // Largest code point expressible by UTF-16.
57e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
58e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
59e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void danglingHighSurrogate() throws Exception {
607aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertStringEncoded("3f", "\ud800"); // "?"
61e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
62e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
63e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void lowSurrogateWithoutHighSurrogate() throws Exception {
647aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertStringEncoded("3f", "\udc00"); // "?"
65e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
66e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
67e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void highSurrogateFollowedByNonSurrogate() throws Exception {
687aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertStringEncoded("3f61", "\ud800\u0061"); // "?a": Following character is too low.
697aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertStringEncoded("3fee8080", "\ud800\ue000"); // "?\ue000": Following character is too high.
70e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
71e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
72e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void multipleSegmentString() throws Exception {
73e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String a = TestUtil.repeat('a', Segment.SIZE + Segment.SIZE + 1);
74e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer encoded = new Buffer().writeUtf8(a);
75e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer expected = new Buffer().write(a.getBytes(Util.UTF_8));
76e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(expected, encoded);
77e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
78e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
79e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void stringSpansSegments() throws Exception {
80e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Buffer buffer = new Buffer();
81e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String a = TestUtil.repeat('a', Segment.SIZE - 1);
82e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String b = "bb";
83e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String c = TestUtil.repeat('c', Segment.SIZE - 1);
84e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    buffer.writeUtf8(a);
85e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    buffer.writeUtf8(b);
86e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    buffer.writeUtf8(c);
87e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(a + b + c, buffer.readUtf8());
88e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
89e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
907aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void readEmptyBufferThrowsEofException() throws Exception {
917aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer();
927aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    try {
937aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      buffer.readUtf8CodePoint();
947aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      fail();
957aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    } catch (EOFException expected) {
967aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    }
977aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
987aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
997aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void readLeadingContinuationByteReturnsReplacementCharacter() throws Exception {
1007aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer();
1017aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    buffer.writeByte(0xbf);
1027aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint());
1037aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertTrue(buffer.exhausted());
1047aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1057aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1067aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void readMissingContinuationBytesThrowsEofException() throws Exception {
1077aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer();
1087aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    buffer.writeByte(0xdf);
1097aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    try {
1107aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      buffer.readUtf8CodePoint();
1117aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      fail();
1127aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    } catch (EOFException expected) {
1137aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    }
1147aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertFalse(buffer.exhausted()); // Prefix byte wasn't consumed.
1157aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1167aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1177aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void readTooLargeCodepointReturnsReplacementCharacter() throws Exception {
1187aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    // 5-byte and 6-byte code points are not supported.
1197aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer();
1207aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    buffer.write(ByteString.decodeHex("f888808080"));
1217aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint());
1227aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint());
1237aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint());
1247aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint());
1257aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint());
1267aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertTrue(buffer.exhausted());
1277aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1287aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1297aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void readNonContinuationBytesReturnsReplacementCharacter() throws Exception {
1307aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    // Use a non-continuation byte where a continuation byte is expected.
1317aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer();
1327aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    buffer.write(ByteString.decodeHex("df20"));
1337aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint());
1347aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(0x20, buffer.readUtf8CodePoint()); // Non-continuation character not consumed.
1357aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertTrue(buffer.exhausted());
1367aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1377aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1387aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void readCodePointBeyondUnicodeMaximum() throws Exception {
1397aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    // A 4-byte encoding with data above the U+10ffff Unicode maximum.
1407aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer();
1417aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    buffer.write(ByteString.decodeHex("f4908080"));
1427aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint());
1437aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertTrue(buffer.exhausted());
1447aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1457aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1467aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void readSurrogateCodePoint() throws Exception {
1477aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer();
1487aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    buffer.write(ByteString.decodeHex("eda080"));
1497aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint());
1507aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertTrue(buffer.exhausted());
1517aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    buffer.write(ByteString.decodeHex("edbfbf"));
1527aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint());
1537aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertTrue(buffer.exhausted());
1547aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1557aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1567aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void readOverlongCodePoint() throws Exception {
1577aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    // Use 2 bytes to encode data that only needs 1 byte.
1587aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer();
1597aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    buffer.write(ByteString.decodeHex("c080"));
1607aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint());
1617aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertTrue(buffer.exhausted());
1627aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1637aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1647aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void writeSurrogateCodePoint() throws Exception {
1657aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer();
1667aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    buffer.writeUtf8CodePoint(0xd7ff); // Below lowest surrogate is okay.
1677aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    try {
1687aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      buffer.writeUtf8CodePoint(0xd800); // Lowest surrogate throws.
1697aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      fail();
1707aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    } catch (IllegalArgumentException expected) {
1717aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    }
1727aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    try {
1737aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      buffer.writeUtf8CodePoint(0xdfff); // Highest surrogate throws.
1747aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      fail();
1757aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    } catch (IllegalArgumentException expected) {
1767aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    }
1777aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    buffer.writeUtf8CodePoint(0xe000); // Above highest surrogate is okay.
1787aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1797aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1807aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  @Test public void writeCodePointBeyondUnicodeMaximum() throws Exception {
1817aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer();
1827aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    try {
1837aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      buffer.writeUtf8CodePoint(0x110000);
1847aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      fail();
1857aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    } catch (IllegalArgumentException expected) {
1867aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    }
1877aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1887aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertEncoded(String hex, int... codePoints) throws Exception {
1907aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertCodePointEncoded(hex, codePoints);
1917aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertCodePointDecoded(hex, codePoints);
1927aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertStringEncoded(hex, new String(codePoints, 0, codePoints.length));
1937aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
1947aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
1957aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  private void assertCodePointEncoded(String hex, int... codePoints) throws Exception {
1967aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer();
1977aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    for (int codePoint : codePoints) {
1987aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      buffer.writeUtf8CodePoint(codePoint);
1997aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    }
2007aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertEquals(buffer.readByteString(), ByteString.decodeHex(hex));
2017aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  }
2027aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller
2037aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  private void assertCodePointDecoded(String hex, int... codePoints) throws Exception {
2047aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    Buffer buffer = new Buffer().write(ByteString.decodeHex(hex));
2057aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    for (int codePoint : codePoints) {
2067aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller      assertEquals(codePoint, buffer.readUtf8CodePoint());
2077aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    }
2087aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller    assertTrue(buffer.exhausted());
209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
2117aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller  private void assertStringEncoded(String hex, String string) throws Exception {
212e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    ByteString expectedUtf8 = ByteString.decodeHex(hex);
213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Confirm our expectations are consistent with the platform.
215e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    ByteString platformUtf8 = ByteString.of(string.getBytes("UTF-8"));
216e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(expectedUtf8, platformUtf8);
217e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
218e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Confirm our implementation matches those expectations.
219e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    ByteString actualUtf8 = new Buffer().writeUtf8(string).readByteString();
220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(expectedUtf8, actualUtf8);
221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller}
223