1// Protocol Buffers - Google's data interchange format 2// Copyright 2008 Google Inc. All rights reserved. 3// http://code.google.com/p/protobuf/ 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 31package com.google.protobuf; 32 33import protobuf_unittest.UnittestProto.SparseEnumMessage; 34import protobuf_unittest.UnittestProto.TestAllTypes; 35import protobuf_unittest.UnittestProto.TestPackedTypes; 36import protobuf_unittest.UnittestProto.TestSparseEnum; 37 38import junit.framework.TestCase; 39 40import java.io.ByteArrayOutputStream; 41import java.util.ArrayList; 42import java.util.List; 43 44/** 45 * Unit test for {@link CodedOutputStream}. 46 * 47 * @author kenton@google.com Kenton Varda 48 */ 49public class CodedOutputStreamTest extends TestCase { 50 /** 51 * Helper to construct a byte array from a bunch of bytes. The inputs are 52 * actually ints so that I can use hex notation and not get stupid errors 53 * about precision. 54 */ 55 private byte[] bytes(int... bytesAsInts) { 56 byte[] bytes = new byte[bytesAsInts.length]; 57 for (int i = 0; i < bytesAsInts.length; i++) { 58 bytes[i] = (byte) bytesAsInts[i]; 59 } 60 return bytes; 61 } 62 63 /** Arrays.asList() does not work with arrays of primitives. :( */ 64 private List<Byte> toList(byte[] bytes) { 65 List<Byte> result = new ArrayList<Byte>(); 66 for (byte b : bytes) { 67 result.add(b); 68 } 69 return result; 70 } 71 72 private void assertEqualBytes(byte[] a, byte[] b) { 73 assertEquals(toList(a), toList(b)); 74 } 75 76 /** 77 * Writes the given value using writeRawVarint32() and writeRawVarint64() and 78 * checks that the result matches the given bytes. 79 */ 80 private void assertWriteVarint(byte[] data, long value) throws Exception { 81 // Only do 32-bit write if the value fits in 32 bits. 82 if ((value >>> 32) == 0) { 83 ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); 84 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); 85 output.writeRawVarint32((int) value); 86 output.flush(); 87 assertEqualBytes(data, rawOutput.toByteArray()); 88 89 // Also try computing size. 90 assertEquals(data.length, 91 CodedOutputStream.computeRawVarint32Size((int) value)); 92 } 93 94 { 95 ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); 96 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); 97 output.writeRawVarint64(value); 98 output.flush(); 99 assertEqualBytes(data, rawOutput.toByteArray()); 100 101 // Also try computing size. 102 assertEquals(data.length, 103 CodedOutputStream.computeRawVarint64Size(value)); 104 } 105 106 // Try different block sizes. 107 for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { 108 // Only do 32-bit write if the value fits in 32 bits. 109 if ((value >>> 32) == 0) { 110 ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); 111 CodedOutputStream output = 112 CodedOutputStream.newInstance(rawOutput, blockSize); 113 output.writeRawVarint32((int) value); 114 output.flush(); 115 assertEqualBytes(data, rawOutput.toByteArray()); 116 } 117 118 { 119 ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); 120 CodedOutputStream output = 121 CodedOutputStream.newInstance(rawOutput, blockSize); 122 output.writeRawVarint64(value); 123 output.flush(); 124 assertEqualBytes(data, rawOutput.toByteArray()); 125 } 126 } 127 } 128 129 /** Tests writeRawVarint32() and writeRawVarint64(). */ 130 public void testWriteVarint() throws Exception { 131 assertWriteVarint(bytes(0x00), 0); 132 assertWriteVarint(bytes(0x01), 1); 133 assertWriteVarint(bytes(0x7f), 127); 134 // 14882 135 assertWriteVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7)); 136 // 2961488830 137 assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b), 138 (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | 139 (0x0bL << 28)); 140 141 // 64-bit 142 // 7256456126 143 assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b), 144 (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | 145 (0x1bL << 28)); 146 // 41256202580718336 147 assertWriteVarint( 148 bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49), 149 (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) | 150 (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49)); 151 // 11964378330978735131 152 assertWriteVarint( 153 bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01), 154 (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) | 155 (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) | 156 (0x05L << 49) | (0x26L << 56) | (0x01L << 63)); 157 } 158 159 /** 160 * Parses the given bytes using writeRawLittleEndian32() and checks 161 * that the result matches the given value. 162 */ 163 private void assertWriteLittleEndian32(byte[] data, int value) 164 throws Exception { 165 ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); 166 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); 167 output.writeRawLittleEndian32(value); 168 output.flush(); 169 assertEqualBytes(data, rawOutput.toByteArray()); 170 171 // Try different block sizes. 172 for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { 173 rawOutput = new ByteArrayOutputStream(); 174 output = CodedOutputStream.newInstance(rawOutput, blockSize); 175 output.writeRawLittleEndian32(value); 176 output.flush(); 177 assertEqualBytes(data, rawOutput.toByteArray()); 178 } 179 } 180 181 /** 182 * Parses the given bytes using writeRawLittleEndian64() and checks 183 * that the result matches the given value. 184 */ 185 private void assertWriteLittleEndian64(byte[] data, long value) 186 throws Exception { 187 ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); 188 CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); 189 output.writeRawLittleEndian64(value); 190 output.flush(); 191 assertEqualBytes(data, rawOutput.toByteArray()); 192 193 // Try different block sizes. 194 for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { 195 rawOutput = new ByteArrayOutputStream(); 196 output = CodedOutputStream.newInstance(rawOutput, blockSize); 197 output.writeRawLittleEndian64(value); 198 output.flush(); 199 assertEqualBytes(data, rawOutput.toByteArray()); 200 } 201 } 202 203 /** Tests writeRawLittleEndian32() and writeRawLittleEndian64(). */ 204 public void testWriteLittleEndian() throws Exception { 205 assertWriteLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678); 206 assertWriteLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0); 207 208 assertWriteLittleEndian64( 209 bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), 210 0x123456789abcdef0L); 211 assertWriteLittleEndian64( 212 bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 213 0x9abcdef012345678L); 214 } 215 216 /** Test encodeZigZag32() and encodeZigZag64(). */ 217 public void testEncodeZigZag() throws Exception { 218 assertEquals(0, CodedOutputStream.encodeZigZag32( 0)); 219 assertEquals(1, CodedOutputStream.encodeZigZag32(-1)); 220 assertEquals(2, CodedOutputStream.encodeZigZag32( 1)); 221 assertEquals(3, CodedOutputStream.encodeZigZag32(-2)); 222 assertEquals(0x7FFFFFFE, CodedOutputStream.encodeZigZag32(0x3FFFFFFF)); 223 assertEquals(0x7FFFFFFF, CodedOutputStream.encodeZigZag32(0xC0000000)); 224 assertEquals(0xFFFFFFFE, CodedOutputStream.encodeZigZag32(0x7FFFFFFF)); 225 assertEquals(0xFFFFFFFF, CodedOutputStream.encodeZigZag32(0x80000000)); 226 227 assertEquals(0, CodedOutputStream.encodeZigZag64( 0)); 228 assertEquals(1, CodedOutputStream.encodeZigZag64(-1)); 229 assertEquals(2, CodedOutputStream.encodeZigZag64( 1)); 230 assertEquals(3, CodedOutputStream.encodeZigZag64(-2)); 231 assertEquals(0x000000007FFFFFFEL, 232 CodedOutputStream.encodeZigZag64(0x000000003FFFFFFFL)); 233 assertEquals(0x000000007FFFFFFFL, 234 CodedOutputStream.encodeZigZag64(0xFFFFFFFFC0000000L)); 235 assertEquals(0x00000000FFFFFFFEL, 236 CodedOutputStream.encodeZigZag64(0x000000007FFFFFFFL)); 237 assertEquals(0x00000000FFFFFFFFL, 238 CodedOutputStream.encodeZigZag64(0xFFFFFFFF80000000L)); 239 assertEquals(0xFFFFFFFFFFFFFFFEL, 240 CodedOutputStream.encodeZigZag64(0x7FFFFFFFFFFFFFFFL)); 241 assertEquals(0xFFFFFFFFFFFFFFFFL, 242 CodedOutputStream.encodeZigZag64(0x8000000000000000L)); 243 244 // Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1) 245 // were chosen semi-randomly via keyboard bashing. 246 assertEquals(0, 247 CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(0))); 248 assertEquals(1, 249 CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(1))); 250 assertEquals(-1, 251 CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-1))); 252 assertEquals(14927, 253 CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(14927))); 254 assertEquals(-3612, 255 CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-3612))); 256 257 assertEquals(0, 258 CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(0))); 259 assertEquals(1, 260 CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(1))); 261 assertEquals(-1, 262 CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-1))); 263 assertEquals(14927, 264 CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(14927))); 265 assertEquals(-3612, 266 CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-3612))); 267 268 assertEquals(856912304801416L, 269 CodedOutputStream.encodeZigZag64( 270 CodedInputStream.decodeZigZag64( 271 856912304801416L))); 272 assertEquals(-75123905439571256L, 273 CodedOutputStream.encodeZigZag64( 274 CodedInputStream.decodeZigZag64( 275 -75123905439571256L))); 276 } 277 278 /** Tests writing a whole message with every field type. */ 279 public void testWriteWholeMessage() throws Exception { 280 TestAllTypes message = TestUtil.getAllSet(); 281 282 byte[] rawBytes = message.toByteArray(); 283 assertEqualBytes(TestUtil.getGoldenMessage().toByteArray(), rawBytes); 284 285 // Try different block sizes. 286 for (int blockSize = 1; blockSize < 256; blockSize *= 2) { 287 ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); 288 CodedOutputStream output = 289 CodedOutputStream.newInstance(rawOutput, blockSize); 290 message.writeTo(output); 291 output.flush(); 292 assertEqualBytes(rawBytes, rawOutput.toByteArray()); 293 } 294 } 295 296 /** Tests writing a whole message with every packed field type. Ensures the 297 * wire format of packed fields is compatible with C++. */ 298 public void testWriteWholePackedFieldsMessage() throws Exception { 299 TestPackedTypes message = TestUtil.getPackedSet(); 300 301 byte[] rawBytes = message.toByteArray(); 302 assertEqualBytes(TestUtil.getGoldenPackedFieldsMessage().toByteArray(), 303 rawBytes); 304 } 305 306 /** Test writing a message containing a negative enum value. This used to 307 * fail because the size was not properly computed as a sign-extended varint. 308 */ 309 public void testWriteMessageWithNegativeEnumValue() throws Exception { 310 SparseEnumMessage message = SparseEnumMessage.newBuilder() 311 .setSparseEnum(TestSparseEnum.SPARSE_E) .build(); 312 assertTrue(message.getSparseEnum().getNumber() < 0); 313 byte[] rawBytes = message.toByteArray(); 314 SparseEnumMessage message2 = SparseEnumMessage.parseFrom(rawBytes); 315 assertEquals(TestSparseEnum.SPARSE_E, message2.getSparseEnum()); 316 } 317} 318