1
// Protocol Buffers - Google's data interchange format
2
// Copyright 2008 Google Inc. All rights reserved.
3
// http://code.google.com/p/protobuf/
5
// Redistribution and use in source and binary forms, with or without
6
// modification, are permitted provided that the following conditions are
9
// * Redistributions of source code must retain the above copyright
10
// notice, this list of conditions and the following disclaimer.
11
// * Redistributions in binary form must reproduce the above
12
// copyright notice, this list of conditions and the following disclaimer
13
// in the documentation and/or other materials provided with the
15
// * Neither the name of Google Inc. nor the names of its
16
// contributors may be used to endorse or promote products derived from
17
// this software without specific prior written permission.
19
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
package com.google.protobuf;
33
import junit.framework.TestCase;
35
import java.io.ByteArrayOutputStream;
36
import java.io.IOException;
37
import java.io.InputStream;
38
import java.io.OutputStream;
39
import java.io.UnsupportedEncodingException;
40
import java.nio.ByteBuffer;
41
import java.util.Arrays;
42
import java.util.List;
43
import java.util.NoSuchElementException;
46
* Test {@link LiteralByteString} by setting up a reference string in {@link #setUp()}.
47
* This class is designed to be extended for testing extensions of {@link LiteralByteString}
48
* such as {@link BoundedByteString}, see {@link BoundedByteStringTest}.
50
* @author carlanton@google.com (Carl Haverl)
52
public class LiteralByteStringTest extends TestCase {
53
protected static final String UTF_8 = "UTF-8";
55
protected String classUnderTest;
56
protected byte[] referenceBytes;
57
protected ByteString stringUnderTest;
58
protected int expectedHashCode;
61
protected void setUp() throws Exception {
62
classUnderTest = "LiteralByteString";
63
referenceBytes = ByteStringTest.getTestBytes(1234, 11337766L);
64
stringUnderTest = ByteString.copyFrom(referenceBytes);
65
expectedHashCode = 331161852;
68
public void testExpectedType() {
69
String actualClassName = getActualClassName(stringUnderTest);
70
assertEquals(classUnderTest + " should match type exactly", classUnderTest, actualClassName);
73
protected String getActualClassName(Object object) {
74
String actualClassName = object.getClass().getName();
75
actualClassName = actualClassName.substring(actualClassName.lastIndexOf('.') + 1);
76
return actualClassName;
79
public void testByteAt() {
80
boolean stillEqual = true;
81
for (int i = 0; stillEqual && i < referenceBytes.length; ++i) {
82
stillEqual = (referenceBytes[i] == stringUnderTest.byteAt(i));
84
assertTrue(classUnderTest + " must capture the right bytes", stillEqual);
87
public void testByteIterator() {
88
boolean stillEqual = true;
89
ByteString.ByteIterator iter = stringUnderTest.iterator();
90
for (int i = 0; stillEqual && i < referenceBytes.length; ++i) {
91
stillEqual = (iter.hasNext() && referenceBytes[i] == iter.nextByte());
93
assertTrue(classUnderTest + " must capture the right bytes", stillEqual);
94
assertFalse(classUnderTest + " must have exhausted the itertor", iter.hasNext());
98
fail("Should have thrown an exception.");
99
} catch (NoSuchElementException e) {
104
public void testByteIterable() {
105
boolean stillEqual = true;
107
for (byte quantum : stringUnderTest) {
108
stillEqual = (referenceBytes[j] == quantum);
111
assertTrue(classUnderTest + " must capture the right bytes as Bytes", stillEqual);
112
assertEquals(classUnderTest + " iterable character count", referenceBytes.length, j);
115
public void testSize() {
116
assertEquals(classUnderTest + " must have the expected size", referenceBytes.length,
117
stringUnderTest.size());
120
public void testGetTreeDepth() {
121
assertEquals(classUnderTest + " must have depth 0", 0, stringUnderTest.getTreeDepth());
124
public void testIsBalanced() {
125
assertTrue(classUnderTest + " is technically balanced", stringUnderTest.isBalanced());
128
public void testCopyTo_ByteArrayOffsetLength() {
129
int destinationOffset = 50;
131
byte[] destination = new byte[destinationOffset + length];
132
int sourceOffset = 213;
133
stringUnderTest.copyTo(destination, sourceOffset, destinationOffset, length);
134
boolean stillEqual = true;
135
for (int i = 0; stillEqual && i < length; ++i) {
136
stillEqual = referenceBytes[i + sourceOffset] == destination[i + destinationOffset];
138
assertTrue(classUnderTest + ".copyTo(4 arg) must give the expected bytes", stillEqual);
141
public void testCopyTo_ByteArrayOffsetLengthErrors() {
142
int destinationOffset = 50;
144
byte[] destination = new byte[destinationOffset + length];
147
// Copy one too many bytes
148
stringUnderTest.copyTo(destination, stringUnderTest.size() + 1 - length,
149
destinationOffset, length);
150
fail("Should have thrown an exception when copying too many bytes of a "
152
} catch (IndexOutOfBoundsException expected) {
157
// Copy with illegal negative sourceOffset
158
stringUnderTest.copyTo(destination, -1, destinationOffset, length);
159
fail("Should have thrown an exception when given a negative sourceOffset in "
161
} catch (IndexOutOfBoundsException expected) {
166
// Copy with illegal negative destinationOffset
167
stringUnderTest.copyTo(destination, 0, -1, length);
168
fail("Should have thrown an exception when given a negative destinationOffset in "
170
} catch (IndexOutOfBoundsException expected) {
175
// Copy with illegal negative size
176
stringUnderTest.copyTo(destination, 0, 0, -1);
177
fail("Should have thrown an exception when given a negative size in "
179
} catch (IndexOutOfBoundsException expected) {
184
// Copy with illegal too-large sourceOffset
185
stringUnderTest.copyTo(destination, 2 * stringUnderTest.size(), 0, length);
186
fail("Should have thrown an exception when the destinationOffset is too large in "
188
} catch (IndexOutOfBoundsException expected) {
193
// Copy with illegal too-large destinationOffset
194
stringUnderTest.copyTo(destination, 0, 2 * destination.length, length);
195
fail("Should have thrown an exception when the destinationOffset is too large in "
197
} catch (IndexOutOfBoundsException expected) {
202
public void testCopyTo_ByteBuffer() {
203
ByteBuffer myBuffer = ByteBuffer.allocate(referenceBytes.length);
204
stringUnderTest.copyTo(myBuffer);
205
assertTrue(classUnderTest + ".copyTo(ByteBuffer) must give back the same bytes",
206
Arrays.equals(referenceBytes, myBuffer.array()));
209
public void testAsReadOnlyByteBuffer() {
210
ByteBuffer byteBuffer = stringUnderTest.asReadOnlyByteBuffer();
211
byte[] roundTripBytes = new byte[referenceBytes.length];
212
assertTrue(byteBuffer.remaining() == referenceBytes.length);
213
assertTrue(byteBuffer.isReadOnly());
214
byteBuffer.get(roundTripBytes);
215
assertTrue(classUnderTest + ".asReadOnlyByteBuffer() must give back the same bytes",
216
Arrays.equals(referenceBytes, roundTripBytes));
219
public void testAsReadOnlyByteBufferList() {
220
List<ByteBuffer> byteBuffers = stringUnderTest.asReadOnlyByteBufferList();
222
byte[] roundTripBytes = new byte[referenceBytes.length];
223
for (ByteBuffer byteBuffer : byteBuffers) {
224
int thisLength = byteBuffer.remaining();
225
assertTrue(byteBuffer.isReadOnly());
226
assertTrue(bytesSeen + thisLength <= referenceBytes.length);
227
byteBuffer.get(roundTripBytes, bytesSeen, thisLength);
228
bytesSeen += thisLength;
230
assertTrue(bytesSeen == referenceBytes.length);
231
assertTrue(classUnderTest + ".asReadOnlyByteBufferTest() must give back the same bytes",
232
Arrays.equals(referenceBytes, roundTripBytes));
235
public void testToByteArray() {
236
byte[] roundTripBytes = stringUnderTest.toByteArray();
237
assertTrue(classUnderTest + ".toByteArray() must give back the same bytes",
238
Arrays.equals(referenceBytes, roundTripBytes));
241
public void testWriteTo() throws IOException {
242
ByteArrayOutputStream bos = new ByteArrayOutputStream();
243
stringUnderTest.writeTo(bos);
244
byte[] roundTripBytes = bos.toByteArray();
245
assertTrue(classUnderTest + ".writeTo() must give back the same bytes",
246
Arrays.equals(referenceBytes, roundTripBytes));
249
public void testWriteTo_mutating() throws IOException {
250
OutputStream os = new OutputStream() {
252
public void write(byte[] b, int off, int len) {
253
for (int x = 0; x < len; ++x) {
254
b[off + x] = (byte) 0;
259
public void write(int b) {
260
// Purposefully left blank.
264
stringUnderTest.writeTo(os);
265
byte[] newBytes = stringUnderTest.toByteArray();
266
assertTrue(classUnderTest + ".writeTo() must not grant access to underlying array",
267
Arrays.equals(referenceBytes, newBytes));
270
public void testNewOutput() throws IOException {
271
ByteArrayOutputStream bos = new ByteArrayOutputStream();
272
ByteString.Output output = ByteString.newOutput();
273
stringUnderTest.writeTo(output);
274
assertEquals("Output Size returns correct result",
275
output.size(), stringUnderTest.size());
277
assertTrue("Output.writeTo() must give back the same bytes",
278
Arrays.equals(referenceBytes, bos.toByteArray()));
280
// write the output stream to itself! This should cause it to double
281
output.writeTo(output);
282
assertEquals("Writing an output stream to itself is successful",
283
stringUnderTest.concat(stringUnderTest), output.toByteString());
286
assertEquals("Output.reset() resets the output", 0, output.size());
287
assertEquals("Output.reset() resets the output",
288
ByteString.EMPTY, output.toByteString());
292
public void testToString() throws UnsupportedEncodingException {
293
String testString = "I love unicode \u1234\u5678 characters";
294
LiteralByteString unicode = new LiteralByteString(testString.getBytes(UTF_8));
295
String roundTripString = unicode.toString(UTF_8);
296
assertEquals(classUnderTest + " unicode must match", testString, roundTripString);
299
public void testEquals() {
300
assertEquals(classUnderTest + " must not equal null", false, stringUnderTest.equals(null));
301
assertEquals(classUnderTest + " must equal self", stringUnderTest, stringUnderTest);
302
assertFalse(classUnderTest + " must not equal the empty string",
303
stringUnderTest.equals(ByteString.EMPTY));
304
assertEquals(classUnderTest + " empty strings must be equal",
305
new LiteralByteString(new byte[]{}), stringUnderTest.substring(55, 55));
306
assertEquals(classUnderTest + " must equal another string with the same value",
307
stringUnderTest, new LiteralByteString(referenceBytes));
309
byte[] mungedBytes = new byte[referenceBytes.length];
310
System.arraycopy(referenceBytes, 0, mungedBytes, 0, referenceBytes.length);
311
mungedBytes[mungedBytes.length - 5] ^= 0xFF;
312
assertFalse(classUnderTest + " must not equal every string with the same length",
313
stringUnderTest.equals(new LiteralByteString(mungedBytes)));
316
public void testHashCode() {
317
int hash = stringUnderTest.hashCode();
318
assertEquals(classUnderTest + " must have expected hashCode", expectedHashCode, hash);
321
public void testPeekCachedHashCode() {
322
assertEquals(classUnderTest + ".peekCachedHashCode() should return zero at first", 0,
323
stringUnderTest.peekCachedHashCode());
324
stringUnderTest.hashCode();
325
assertEquals(classUnderTest + ".peekCachedHashCode should return zero at first",
326
expectedHashCode, stringUnderTest.peekCachedHashCode());
329
public void testPartialHash() {
330
// partialHash() is more strenuously tested elsewhere by testing hashes of substrings.
331
// This test would fail if the expected hash were 1. It's not.
332
int hash = stringUnderTest.partialHash(stringUnderTest.size(), 0, stringUnderTest.size());
333
assertEquals(classUnderTest + ".partialHash() must yield expected hashCode",
334
expectedHashCode, hash);
337
public void testNewInput() throws IOException {
338
InputStream input = stringUnderTest.newInput();
339
assertEquals("InputStream.available() returns correct value",
340
stringUnderTest.size(), input.available());
341
boolean stillEqual = true;
342
for (byte referenceByte : referenceBytes) {
343
int expectedInt = (referenceByte & 0xFF);
344
stillEqual = (expectedInt == input.read());
346
assertEquals("InputStream.available() returns correct value",
347
0, input.available());
348
assertTrue(classUnderTest + " must give the same bytes from the InputStream", stillEqual);
349
assertEquals(classUnderTest + " InputStream must now be exhausted", -1, input.read());
352
public void testNewInput_skip() throws IOException {
353
InputStream input = stringUnderTest.newInput();
354
int stringSize = stringUnderTest.size();
355
int nearEndIndex = stringSize * 2 / 3;
356
long skipped1 = input.skip(nearEndIndex);
357
assertEquals("InputStream.skip()", skipped1, nearEndIndex);
358
assertEquals("InputStream.available()",
359
stringSize - skipped1, input.available());
360
assertTrue("InputStream.mark() is available", input.markSupported());
362
assertEquals("InputStream.skip(), read()",
363
stringUnderTest.byteAt(nearEndIndex) & 0xFF, input.read());
364
assertEquals("InputStream.available()",
365
stringSize - skipped1 - 1, input.available());
366
long skipped2 = input.skip(stringSize);
367
assertEquals("InputStream.skip() incomplete",
368
skipped2, stringSize - skipped1 - 1);
369
assertEquals("InputStream.skip(), no more input", 0, input.available());
370
assertEquals("InputStream.skip(), no more input", -1, input.read());
372
assertEquals("InputStream.reset() succeded",
373
stringSize - skipped1, input.available());
374
assertEquals("InputStream.reset(), read()",
375
stringUnderTest.byteAt(nearEndIndex) & 0xFF, input.read());
378
public void testNewCodedInput() throws IOException {
379
CodedInputStream cis = stringUnderTest.newCodedInput();
380
byte[] roundTripBytes = cis.readRawBytes(referenceBytes.length);
381
assertTrue(classUnderTest + " must give the same bytes back from the CodedInputStream",
382
Arrays.equals(referenceBytes, roundTripBytes));
383
assertTrue(classUnderTest + " CodedInputStream must now be exhausted", cis.isAtEnd());
387
* Make sure we keep things simple when concatenating with empty. See also
388
* {@link ByteStringTest#testConcat_empty()}.
390
public void testConcat_empty() {
391
assertSame(classUnderTest + " concatenated with empty must give " + classUnderTest,
392
stringUnderTest.concat(ByteString.EMPTY), stringUnderTest);
393
assertSame("empty concatenated with " + classUnderTest + " must give " + classUnderTest,
394
ByteString.EMPTY.concat(stringUnderTest), stringUnderTest);