1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 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 14 // distribution. 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. 18 // 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. 30 31 package com.google.protobuf; 32 33 import junit.framework.TestCase; 34 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; 44 45 /** 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}. 49 * 50 * @author carlanton@google.com (Carl Haverl) 51 */ 52 public class LiteralByteStringTest extends TestCase { 53 protected static final String UTF_8 = "UTF-8"; 54 55 protected String classUnderTest; 56 protected byte[] referenceBytes; 57 protected ByteString stringUnderTest; 58 protected int expectedHashCode; 59 60 @Override setUp()61 protected void setUp() throws Exception { 62 classUnderTest = "LiteralByteString"; 63 referenceBytes = ByteStringTest.getTestBytes(1234, 11337766L); 64 stringUnderTest = ByteString.copyFrom(referenceBytes); 65 expectedHashCode = 331161852; 66 } 67 testExpectedType()68 public void testExpectedType() { 69 String actualClassName = getActualClassName(stringUnderTest); 70 assertEquals(classUnderTest + " should match type exactly", classUnderTest, actualClassName); 71 } 72 getActualClassName(Object object)73 protected String getActualClassName(Object object) { 74 String actualClassName = object.getClass().getName(); 75 actualClassName = actualClassName.substring(actualClassName.lastIndexOf('.') + 1); 76 return actualClassName; 77 } 78 testByteAt()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)); 83 } 84 assertTrue(classUnderTest + " must capture the right bytes", stillEqual); 85 } 86 testByteIterator()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()); 92 } 93 assertTrue(classUnderTest + " must capture the right bytes", stillEqual); 94 assertFalse(classUnderTest + " must have exhausted the itertor", iter.hasNext()); 95 96 try { 97 iter.nextByte(); 98 fail("Should have thrown an exception."); 99 } catch (NoSuchElementException e) { 100 // This is success 101 } 102 } 103 testByteIterable()104 public void testByteIterable() { 105 boolean stillEqual = true; 106 int j = 0; 107 for (byte quantum : stringUnderTest) { 108 stillEqual = (referenceBytes[j] == quantum); 109 ++j; 110 } 111 assertTrue(classUnderTest + " must capture the right bytes as Bytes", stillEqual); 112 assertEquals(classUnderTest + " iterable character count", referenceBytes.length, j); 113 } 114 testSize()115 public void testSize() { 116 assertEquals(classUnderTest + " must have the expected size", referenceBytes.length, 117 stringUnderTest.size()); 118 } 119 testGetTreeDepth()120 public void testGetTreeDepth() { 121 assertEquals(classUnderTest + " must have depth 0", 0, stringUnderTest.getTreeDepth()); 122 } 123 testIsBalanced()124 public void testIsBalanced() { 125 assertTrue(classUnderTest + " is technically balanced", stringUnderTest.isBalanced()); 126 } 127 testCopyTo_ByteArrayOffsetLength()128 public void testCopyTo_ByteArrayOffsetLength() { 129 int destinationOffset = 50; 130 int length = 100; 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]; 137 } 138 assertTrue(classUnderTest + ".copyTo(4 arg) must give the expected bytes", stillEqual); 139 } 140 testCopyTo_ByteArrayOffsetLengthErrors()141 public void testCopyTo_ByteArrayOffsetLengthErrors() { 142 int destinationOffset = 50; 143 int length = 100; 144 byte[] destination = new byte[destinationOffset + length]; 145 146 try { 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 " 151 + classUnderTest); 152 } catch (IndexOutOfBoundsException expected) { 153 // This is success 154 } 155 156 try { 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 " 160 + classUnderTest); 161 } catch (IndexOutOfBoundsException expected) { 162 // This is success 163 } 164 165 try { 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 " 169 + classUnderTest); 170 } catch (IndexOutOfBoundsException expected) { 171 // This is success 172 } 173 174 try { 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 " 178 + classUnderTest); 179 } catch (IndexOutOfBoundsException expected) { 180 // This is success 181 } 182 183 try { 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 " 187 + classUnderTest); 188 } catch (IndexOutOfBoundsException expected) { 189 // This is success 190 } 191 192 try { 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 " 196 + classUnderTest); 197 } catch (IndexOutOfBoundsException expected) { 198 // This is success 199 } 200 } 201 testCopyTo_ByteBuffer()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())); 207 } 208 testAsReadOnlyByteBuffer()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)); 217 } 218 testAsReadOnlyByteBufferList()219 public void testAsReadOnlyByteBufferList() { 220 List<ByteBuffer> byteBuffers = stringUnderTest.asReadOnlyByteBufferList(); 221 int bytesSeen = 0; 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; 229 } 230 assertTrue(bytesSeen == referenceBytes.length); 231 assertTrue(classUnderTest + ".asReadOnlyByteBufferTest() must give back the same bytes", 232 Arrays.equals(referenceBytes, roundTripBytes)); 233 } 234 testToByteArray()235 public void testToByteArray() { 236 byte[] roundTripBytes = stringUnderTest.toByteArray(); 237 assertTrue(classUnderTest + ".toByteArray() must give back the same bytes", 238 Arrays.equals(referenceBytes, roundTripBytes)); 239 } 240 testWriteTo()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)); 247 } 248 testWriteTo_mutating()249 public void testWriteTo_mutating() throws IOException { 250 OutputStream os = new OutputStream() { 251 @Override 252 public void write(byte[] b, int off, int len) { 253 for (int x = 0; x < len; ++x) { 254 b[off + x] = (byte) 0; 255 } 256 } 257 258 @Override 259 public void write(int b) { 260 // Purposefully left blank. 261 } 262 }; 263 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)); 268 } 269 testNewOutput()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()); 276 output.writeTo(bos); 277 assertTrue("Output.writeTo() must give back the same bytes", 278 Arrays.equals(referenceBytes, bos.toByteArray())); 279 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()); 284 285 output.reset(); 286 assertEquals("Output.reset() resets the output", 0, output.size()); 287 assertEquals("Output.reset() resets the output", 288 ByteString.EMPTY, output.toByteString()); 289 290 } 291 testToString()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); 297 } 298 testEquals()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)); 308 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))); 314 } 315 testHashCode()316 public void testHashCode() { 317 int hash = stringUnderTest.hashCode(); 318 assertEquals(classUnderTest + " must have expected hashCode", expectedHashCode, hash); 319 } 320 testPeekCachedHashCode()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()); 327 } 328 testPartialHash()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); 335 } 336 testNewInput()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()); 345 } 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()); 350 } 351 testNewInput_skip()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()); 361 input.mark(0); 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()); 371 input.reset(); 372 assertEquals("InputStream.reset() succeded", 373 stringSize - skipped1, input.available()); 374 assertEquals("InputStream.reset(), read()", 375 stringUnderTest.byteAt(nearEndIndex) & 0xFF, input.read()); 376 } 377 testNewCodedInput()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()); 384 } 385 386 /** 387 * Make sure we keep things simple when concatenating with empty. See also 388 * {@link ByteStringTest#testConcat_empty()}. 389 */ 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); 395 } 396 } 397