1/*
2 * Copyright 2014 Square Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package okio;
17
18import java.io.ByteArrayInputStream;
19import java.io.ByteArrayOutputStream;
20import java.io.InputStream;
21import java.util.ArrayList;
22import java.util.Arrays;
23import java.util.Collections;
24import java.util.List;
25import java.util.Random;
26import org.junit.Test;
27
28import static okio.TestUtil.assertByteArraysEquals;
29import static okio.TestUtil.assertEquivalent;
30import static org.junit.Assert.assertEquals;
31import static org.junit.Assert.assertFalse;
32import static org.junit.Assert.assertSame;
33import static org.junit.Assert.assertTrue;
34import static org.junit.Assert.fail;
35
36public class ByteStringTest {
37  @Test public void ofCopyRange() {
38    byte[] bytes = "Hello, World!".getBytes(Util.UTF_8);
39    ByteString byteString = ByteString.of(bytes, 2, 9);
40    // Verify that the bytes were copied out.
41    bytes[4] = (byte) 'a';
42    assertEquals("llo, Worl", byteString.utf8());
43  }
44
45  @Test public void getByte() throws Exception {
46    ByteString byteString = ByteString.decodeHex("ab12");
47    assertEquals(-85, byteString.getByte(0));
48    assertEquals(18, byteString.getByte(1));
49  }
50
51  @Test public void getByteOutOfBounds() throws Exception {
52    ByteString byteString = ByteString.decodeHex("ab12");
53    try {
54      byteString.getByte(2);
55      fail();
56    } catch (IndexOutOfBoundsException expected) {
57    }
58  }
59
60  @Test public void equals() throws Exception {
61    ByteString byteString = ByteString.decodeHex("000102");
62    assertTrue(byteString.equals(byteString));
63    assertTrue(byteString.equals(ByteString.decodeHex("000102")));
64    assertTrue(ByteString.of().equals(ByteString.EMPTY));
65    assertTrue(ByteString.EMPTY.equals(ByteString.of()));
66    assertFalse(byteString.equals(new Object()));
67    assertFalse(byteString.equals(ByteString.decodeHex("000201")));
68  }
69
70  private final String bronzeHorseman = "На берегу пустынных волн";
71
72  @Test public void utf8() throws Exception {
73    ByteString byteString = ByteString.encodeUtf8(bronzeHorseman);
74    assertByteArraysEquals(byteString.toByteArray(), bronzeHorseman.getBytes(Util.UTF_8));
75    assertTrue(byteString.equals(ByteString.of(bronzeHorseman.getBytes(Util.UTF_8))));
76    assertEquals(byteString.utf8(), bronzeHorseman);
77  }
78
79  @Test public void md5() {
80    assertEquals("6cd3556deb0da54bca060b4c39479839",
81        ByteString.encodeUtf8("Hello, world!").md5().hex());
82    assertEquals("c71dc6df4b2e434b8c74fd6dd6ca3f85",
83        ByteString.encodeUtf8("One Two Three").md5().hex());
84    assertEquals("37b69fb926e239e049d7e43987974b99",
85        ByteString.encodeUtf8(bronzeHorseman).md5().hex());
86  }
87
88  @Test public void sha256() {
89    assertEquals("315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3",
90        ByteString.encodeUtf8("Hello, world!").sha256().hex());
91    assertEquals("641e54ba5e49e169408148a25bef8ca8fa4f8aab222fe8ce4b3535a570ddd68e",
92        ByteString.encodeUtf8("One Two Three").sha256().hex());
93    assertEquals("4d869e1c3d94568a5344235d9e4f187b8d5d78d06c5c622854c669f2f582d33e",
94        ByteString.encodeUtf8(bronzeHorseman).sha256().hex());
95  }
96
97  @Test public void testHashCode() throws Exception {
98    ByteString byteString = ByteString.decodeHex("0102");
99    assertEquals(byteString.hashCode(), byteString.hashCode());
100    assertEquals(byteString.hashCode(), ByteString.decodeHex("0102").hashCode());
101  }
102
103  @Test public void read() throws Exception {
104    InputStream in = new ByteArrayInputStream("abc".getBytes(Util.UTF_8));
105    assertEquals(ByteString.decodeHex("6162"), ByteString.read(in, 2));
106    assertEquals(ByteString.decodeHex("63"), ByteString.read(in, 1));
107    assertEquals(ByteString.of(), ByteString.read(in, 0));
108  }
109
110  @Test public void readAndToLowercase() throws Exception {
111    InputStream in = new ByteArrayInputStream("ABC".getBytes(Util.UTF_8));
112    assertEquals(ByteString.encodeUtf8("ab"), ByteString.read(in, 2).toAsciiLowercase());
113    assertEquals(ByteString.encodeUtf8("c"), ByteString.read(in, 1).toAsciiLowercase());
114    assertEquals(ByteString.EMPTY, ByteString.read(in, 0).toAsciiLowercase());
115  }
116
117  @Test public void toAsciiLowerCaseNoUppercase() throws Exception {
118    ByteString s = ByteString.encodeUtf8("a1_+");
119    assertSame(s, s.toAsciiLowercase());
120  }
121
122  @Test public void toAsciiAllUppercase() throws Exception {
123    assertEquals(ByteString.encodeUtf8("ab"), ByteString.encodeUtf8("AB").toAsciiLowercase());
124  }
125
126  @Test public void toAsciiStartsLowercaseEndsUppercase() throws Exception {
127    assertEquals(ByteString.encodeUtf8("abcd"), ByteString.encodeUtf8("abCD").toAsciiLowercase());
128  }
129
130  @Test public void readAndToUppercase() throws Exception {
131    InputStream in = new ByteArrayInputStream("abc".getBytes(Util.UTF_8));
132    assertEquals(ByteString.encodeUtf8("AB"), ByteString.read(in, 2).toAsciiUppercase());
133    assertEquals(ByteString.encodeUtf8("C"), ByteString.read(in, 1).toAsciiUppercase());
134    assertEquals(ByteString.EMPTY, ByteString.read(in, 0).toAsciiUppercase());
135  }
136
137  @Test public void toAsciiStartsUppercaseEndsLowercase() throws Exception {
138    assertEquals(ByteString.encodeUtf8("ABCD"), ByteString.encodeUtf8("ABcd").toAsciiUppercase());
139  }
140
141  @Test public void substring() throws Exception {
142    ByteString byteString = ByteString.encodeUtf8("Hello, World!");
143
144    assertEquals(byteString.substring(0), byteString);
145    assertEquals(byteString.substring(0, 5), ByteString.encodeUtf8("Hello"));
146    assertEquals(byteString.substring(7), ByteString.encodeUtf8("World!"));
147    assertEquals(byteString.substring(6, 6), ByteString.encodeUtf8(""));
148  }
149
150  @Test public void substringWithInvalidBounds() throws Exception {
151    ByteString byteString = ByteString.encodeUtf8("Hello, World!");
152
153    try {
154      byteString.substring(-1);
155      fail();
156    } catch (IllegalArgumentException expected) {
157    }
158
159    try {
160      byteString.substring(0, 14);
161      fail();
162    } catch (IllegalArgumentException expected) {
163    }
164
165    try {
166      byteString.substring(8, 7);
167      fail();
168    } catch (IllegalArgumentException expected) {
169    }
170  }
171
172  @Test public void write() throws Exception {
173    ByteArrayOutputStream out = new ByteArrayOutputStream();
174    ByteString.decodeHex("616263").write(out);
175    assertByteArraysEquals(new byte[] { 0x61, 0x62, 0x63 }, out.toByteArray());
176  }
177
178  @Test public void encodeBase64() {
179    assertEquals("", ByteString.encodeUtf8("").base64());
180    assertEquals("AA==", ByteString.encodeUtf8("\u0000").base64());
181    assertEquals("AAA=", ByteString.encodeUtf8("\u0000\u0000").base64());
182    assertEquals("AAAA", ByteString.encodeUtf8("\u0000\u0000\u0000").base64());
183    assertEquals("SG93IG1hbnkgbGluZXMgb2YgY29kZSBhcmUgdGhlcmU/ICdib3V0IDIgbWlsbGlvbi4=",
184        ByteString.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64());
185  }
186
187  @Test public void encodeBase64Url() {
188    assertEquals("", ByteString.encodeUtf8("").base64Url());
189    assertEquals("AA==", ByteString.encodeUtf8("\u0000").base64Url());
190    assertEquals("AAA=", ByteString.encodeUtf8("\u0000\u0000").base64Url());
191    assertEquals("AAAA", ByteString.encodeUtf8("\u0000\u0000\u0000").base64Url());
192    assertEquals("SG93IG1hbnkgbGluZXMgb2YgY29kZSBhcmUgdGhlcmU_ICdib3V0IDIgbWlsbGlvbi4=",
193        ByteString.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64Url());
194  }
195
196  @Test public void ignoreUnnecessaryPadding() {
197    assertEquals("", ByteString.decodeBase64("====").utf8());
198    assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64("AAAA====").utf8());
199  }
200
201  @Test public void decodeBase64() {
202    assertEquals("", ByteString.decodeBase64("").utf8());
203    assertEquals(null, ByteString.decodeBase64("/===")); // Can't do anything with 6 bits!
204    assertEquals(ByteString.decodeHex("ff"), ByteString.decodeBase64("//=="));
205    assertEquals(ByteString.decodeHex("ff"), ByteString.decodeBase64("__=="));
206    assertEquals(ByteString.decodeHex("ffff"), ByteString.decodeBase64("///="));
207    assertEquals(ByteString.decodeHex("ffff"), ByteString.decodeBase64("___="));
208    assertEquals(ByteString.decodeHex("ffffff"), ByteString.decodeBase64("////"));
209    assertEquals(ByteString.decodeHex("ffffff"), ByteString.decodeBase64("____"));
210    assertEquals(ByteString.decodeHex("ffffffffffff"), ByteString.decodeBase64("////////"));
211    assertEquals(ByteString.decodeHex("ffffffffffff"), ByteString.decodeBase64("________"));
212    assertEquals("What's to be scared about? It's just a little hiccup in the power...",
213        ByteString.decodeBase64("V2hhdCdzIHRvIGJlIHNjYXJlZCBhYm91dD8gSXQncyBqdXN0IGEgbGl0dGxlIGhpY2"
214            + "N1cCBpbiB0aGUgcG93ZXIuLi4=").utf8());
215    // Uses two encoding styles. Malformed, but supported as a side-effect.
216    assertEquals(ByteString.decodeHex("ffffff"), ByteString.decodeBase64("__//"));
217  }
218
219  @Test public void decodeBase64WithWhitespace() {
220    assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA AA ").utf8());
221    assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA A\r\nA ").utf8());
222    assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64("AA AA").utf8());
223    assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA AA ").utf8());
224    assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA A\r\nA ").utf8());
225    assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64("A    AAA").utf8());
226    assertEquals("", ByteString.decodeBase64("    ").utf8());
227  }
228
229  @Test public void encodeHex() throws Exception {
230    assertEquals("000102", ByteString.of((byte) 0x0, (byte) 0x1, (byte) 0x2).hex());
231  }
232
233  @Test public void decodeHex() throws Exception {
234    assertEquals(ByteString.of((byte) 0x0, (byte) 0x1, (byte) 0x2), ByteString.decodeHex("000102"));
235  }
236
237  @Test public void decodeHexOddNumberOfChars() throws Exception {
238    try {
239      ByteString.decodeHex("aaa");
240      fail();
241    } catch (IllegalArgumentException expected) {
242    }
243  }
244
245  @Test public void decodeHexInvalidChar() throws Exception {
246    try {
247      ByteString.decodeHex("a\u0000");
248      fail();
249    } catch (IllegalArgumentException expected) {
250    }
251  }
252
253  @Test public void toStringOnEmptyByteString() {
254    assertEquals("ByteString[size=0]", ByteString.of().toString());
255  }
256
257  @Test public void toStringOnSmallByteStringIncludesContents() {
258    assertEquals("ByteString[size=16 data=a1b2c3d4e5f61a2b3c4d5e6f10203040]",
259        ByteString.decodeHex("a1b2c3d4e5f61a2b3c4d5e6f10203040").toString());
260  }
261
262  @Test public void toStringOnLargeByteStringIncludesMd5() {
263    assertEquals("ByteString[size=17 md5=2c9728a2138b2f25e9f89f99bdccf8db]",
264        ByteString.encodeUtf8("12345678901234567").toString());
265  }
266
267  @Test public void javaSerializationTestNonEmpty() throws Exception {
268    ByteString byteString = ByteString.encodeUtf8(bronzeHorseman);
269    assertEquivalent(byteString, TestUtil.reserialize(byteString));
270  }
271
272  @Test public void javaSerializationTestEmpty() throws Exception {
273    ByteString byteString = ByteString.of();
274    assertEquivalent(byteString, TestUtil.reserialize(byteString));
275  }
276
277  @Test public void compareToSingleBytes() throws Exception {
278    List<ByteString> originalByteStrings = Arrays.asList(
279        ByteString.decodeHex("00"),
280        ByteString.decodeHex("01"),
281        ByteString.decodeHex("7e"),
282        ByteString.decodeHex("7f"),
283        ByteString.decodeHex("80"),
284        ByteString.decodeHex("81"),
285        ByteString.decodeHex("fe"),
286        ByteString.decodeHex("ff"));
287
288    List<ByteString> sortedByteStrings = new ArrayList<>(originalByteStrings);
289    Collections.shuffle(sortedByteStrings, new Random(0));
290    Collections.sort(sortedByteStrings);
291
292    assertEquals(originalByteStrings, sortedByteStrings);
293  }
294
295  @Test public void compareToMultipleBytes() throws Exception {
296    List<ByteString> originalByteStrings = Arrays.asList(
297        ByteString.decodeHex(""),
298        ByteString.decodeHex("00"),
299        ByteString.decodeHex("0000"),
300        ByteString.decodeHex("000000"),
301        ByteString.decodeHex("00000000"),
302        ByteString.decodeHex("0000000000"),
303        ByteString.decodeHex("0000000001"),
304        ByteString.decodeHex("000001"),
305        ByteString.decodeHex("00007f"),
306        ByteString.decodeHex("0000ff"),
307        ByteString.decodeHex("000100"),
308        ByteString.decodeHex("000101"),
309        ByteString.decodeHex("007f00"),
310        ByteString.decodeHex("00ff00"),
311        ByteString.decodeHex("010000"),
312        ByteString.decodeHex("010001"),
313        ByteString.decodeHex("01007f"),
314        ByteString.decodeHex("0100ff"),
315        ByteString.decodeHex("010100"),
316        ByteString.decodeHex("01010000"),
317        ByteString.decodeHex("0101000000"),
318        ByteString.decodeHex("0101000001"),
319        ByteString.decodeHex("010101"),
320        ByteString.decodeHex("7f0000"),
321        ByteString.decodeHex("7f0000ffff"),
322        ByteString.decodeHex("ffffff"));
323
324    List<ByteString> sortedByteStrings = new ArrayList<>(originalByteStrings);
325    Collections.shuffle(sortedByteStrings, new Random(0));
326    Collections.sort(sortedByteStrings);
327
328    assertEquals(originalByteStrings, sortedByteStrings);
329  }
330}
331