1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 package com.google.protobuf; 9 10 import static com.google.common.truth.Truth.assertThat; 11 import static com.google.common.truth.Truth.assertWithMessage; 12 13 import protobuf_unittest.UnittestOptimizeFor; 14 import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize; 15 import protobuf_unittest.UnittestOptimizeFor.TestRequiredOptimizedForSize; 16 import protobuf_unittest.UnittestProto; 17 import protobuf_unittest.UnittestProto.ForeignMessage; 18 import protobuf_unittest.UnittestProto.TestAllTypes; 19 import protobuf_unittest.UnittestProto.TestEmptyMessage; 20 import protobuf_unittest.UnittestProto.TestMergeException; 21 import protobuf_unittest.UnittestProto.TestParsingMerge; 22 import protobuf_unittest.UnittestProto.TestRequired; 23 import java.io.ByteArrayInputStream; 24 import java.io.ByteArrayOutputStream; 25 import java.io.IOException; 26 import java.io.InputStream; 27 import java.io.InterruptedIOException; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 import org.junit.runners.JUnit4; 31 32 /** Unit test for {@link Parser}. */ 33 @RunWith(JUnit4.class) 34 public class ParserTest { 35 36 @Test testGeneratedMessageParserSingleton()37 public void testGeneratedMessageParserSingleton() throws Exception { 38 for (int i = 0; i < 10; i++) { 39 assertThat(TestUtil.getAllSet().getParserForType()).isEqualTo(TestAllTypes.parser()); 40 } 41 } 42 assertRoundTripEquals(MessageLite message, ExtensionRegistryLite registry)43 private void assertRoundTripEquals(MessageLite message, ExtensionRegistryLite registry) 44 throws Exception { 45 final byte[] data = message.toByteArray(); 46 final int offset = 20; 47 final int length = data.length; 48 final int padding = 30; 49 Parser<? extends MessageLite> parser = message.getParserForType(); 50 assertMessageEquals(message, parser.parseFrom(data, registry)); 51 assertMessageEquals( 52 message, 53 parser.parseFrom(generatePaddingArray(data, offset, padding), offset, length, registry)); 54 assertMessageEquals(message, parser.parseFrom(message.toByteString(), registry)); 55 assertMessageEquals(message, parser.parseFrom(new ByteArrayInputStream(data), registry)); 56 assertMessageEquals(message, parser.parseFrom(CodedInputStream.newInstance(data), registry)); 57 assertMessageEquals( 58 message, parser.parseFrom(message.toByteString().asReadOnlyByteBuffer(), registry)); 59 } 60 61 @SuppressWarnings("unchecked") assertRoundTripEquals(MessageLite message)62 private void assertRoundTripEquals(MessageLite message) throws Exception { 63 final byte[] data = message.toByteArray(); 64 final int offset = 20; 65 final int length = data.length; 66 final int padding = 30; 67 68 Parser<MessageLite> parser = (Parser<MessageLite>) message.getParserForType(); 69 assertMessageEquals(message, parser.parseFrom(data)); 70 assertMessageEquals( 71 message, parser.parseFrom(generatePaddingArray(data, offset, padding), offset, length)); 72 assertMessageEquals(message, parser.parseFrom(message.toByteString())); 73 assertMessageEquals(message, parser.parseFrom(new ByteArrayInputStream(data))); 74 assertMessageEquals(message, parser.parseFrom(CodedInputStream.newInstance(data))); 75 assertMessageEquals(message, parser.parseFrom(message.toByteString().asReadOnlyByteBuffer())); 76 } 77 assertMessageEquals(MessageLite expected, MessageLite actual)78 private void assertMessageEquals(MessageLite expected, MessageLite actual) throws Exception { 79 if (expected instanceof Message) { 80 assertThat(actual).isEqualTo(expected); 81 } else { 82 assertThat(actual.toByteString()).isEqualTo(expected.toByteString()); 83 } 84 } 85 generatePaddingArray(byte[] data, int offset, int padding)86 private byte[] generatePaddingArray(byte[] data, int offset, int padding) { 87 byte[] result = new byte[offset + data.length + padding]; 88 System.arraycopy(data, 0, result, offset, data.length); 89 return result; 90 } 91 92 @Test testNormalMessage()93 public void testNormalMessage() throws Exception { 94 assertRoundTripEquals(TestUtil.getAllSet()); 95 } 96 97 @Test testParsePartial()98 public void testParsePartial() throws Exception { 99 assertParsePartial(TestRequired.parser(), TestRequired.newBuilder().setA(1).buildPartial()); 100 } 101 assertParsePartial(Parser<T> parser, T partialMessage)102 private <T extends MessageLite> void assertParsePartial(Parser<T> parser, T partialMessage) 103 throws Exception { 104 final String errorString = "Should throw exceptions when the parsed message isn't initialized."; 105 106 // parsePartialFrom should pass. 107 byte[] data = partialMessage.toByteArray(); 108 assertThat(parser.parsePartialFrom(data)).isEqualTo(partialMessage); 109 assertThat(parser.parsePartialFrom(partialMessage.toByteString())).isEqualTo(partialMessage); 110 assertThat(parser.parsePartialFrom(new ByteArrayInputStream(data))).isEqualTo(partialMessage); 111 assertThat(parser.parsePartialFrom(CodedInputStream.newInstance(data))) 112 .isEqualTo(partialMessage); 113 114 // parseFrom(ByteArray) 115 try { 116 parser.parseFrom(partialMessage.toByteArray()); 117 assertWithMessage(errorString).fail(); 118 } catch (InvalidProtocolBufferException e) { 119 // pass. 120 } 121 122 // parseFrom(ByteString) 123 try { 124 parser.parseFrom(partialMessage.toByteString()); 125 assertWithMessage(errorString).fail(); 126 } catch (InvalidProtocolBufferException e) { 127 // pass. 128 } 129 130 // parseFrom(InputStream) 131 try { 132 parser.parseFrom(new ByteArrayInputStream(partialMessage.toByteArray())); 133 assertWithMessage(errorString).fail(); 134 } catch (IOException e) { 135 // pass. 136 } 137 138 // parseFrom(CodedInputStream) 139 try { 140 parser.parseFrom(CodedInputStream.newInstance(partialMessage.toByteArray())); 141 assertWithMessage(errorString).fail(); 142 } catch (IOException e) { 143 // pass. 144 } 145 } 146 147 @Test testParseExtensions()148 public void testParseExtensions() throws Exception { 149 assertRoundTripEquals(TestUtil.getAllExtensionsSet(), TestUtil.getExtensionRegistry()); 150 } 151 152 @Test testParsePacked()153 public void testParsePacked() throws Exception { 154 assertRoundTripEquals(TestUtil.getPackedSet()); 155 assertRoundTripEquals(TestUtil.getPackedExtensionsSet(), TestUtil.getExtensionRegistry()); 156 } 157 158 @Test testParseDelimitedTo()159 public void testParseDelimitedTo() throws Exception { 160 // Write normal Message. 161 TestAllTypes normalMessage = TestUtil.getAllSet(); 162 ByteArrayOutputStream output = new ByteArrayOutputStream(); 163 normalMessage.writeDelimitedTo(output); 164 normalMessage.writeDelimitedTo(output); 165 166 InputStream input = new ByteArrayInputStream(output.toByteArray()); 167 assertMessageEquals(normalMessage, normalMessage.getParserForType().parseDelimitedFrom(input)); 168 assertMessageEquals(normalMessage, normalMessage.getParserForType().parseDelimitedFrom(input)); 169 } 170 171 @Test testParseUnknownFields()172 public void testParseUnknownFields() throws Exception { 173 // All fields will be treated as unknown fields in emptyMessage. 174 TestEmptyMessage emptyMessage = TestEmptyMessage.parseFrom(TestUtil.getAllSet().toByteString()); 175 assertThat(emptyMessage.toByteString()).isEqualTo(TestUtil.getAllSet().toByteString()); 176 } 177 178 @Test testOptimizeForSize()179 public void testOptimizeForSize() throws Exception { 180 TestOptimizedForSize.Builder builder = TestOptimizedForSize.newBuilder(); 181 builder.setI(12).setMsg(ForeignMessage.newBuilder().setC(34).build()); 182 builder.setExtension(TestOptimizedForSize.testExtension, 56); 183 builder.setExtension( 184 TestOptimizedForSize.testExtension2, 185 TestRequiredOptimizedForSize.newBuilder().setX(78).build()); 186 187 TestOptimizedForSize message = builder.build(); 188 ExtensionRegistry registry = ExtensionRegistry.newInstance(); 189 UnittestOptimizeFor.registerAllExtensions(registry); 190 191 assertRoundTripEquals(message, registry); 192 } 193 194 /** Helper method for {@link #testParsingMerge()}. */ assertMessageMerged(TestAllTypes allTypes)195 private void assertMessageMerged(TestAllTypes allTypes) throws Exception { 196 assertThat(allTypes.getOptionalInt32()).isEqualTo(3); 197 assertThat(allTypes.getOptionalInt64()).isEqualTo(2); 198 assertThat(allTypes.getOptionalString()).isEqualTo("hello"); 199 } 200 201 @Test testParsingMerge()202 public void testParsingMerge() throws Exception { 203 // Build messages. 204 TestAllTypes.Builder builder = TestAllTypes.newBuilder(); 205 TestAllTypes msg1 = builder.setOptionalInt32(1).build(); 206 builder.clear(); 207 TestAllTypes msg2 = builder.setOptionalInt64(2).build(); 208 builder.clear(); 209 TestAllTypes msg3 = builder.setOptionalInt32(3).setOptionalString("hello").build(); 210 211 // Build groups. 212 TestParsingMerge.RepeatedFieldsGenerator.Group1 optionalG1 = 213 TestParsingMerge.RepeatedFieldsGenerator.Group1.newBuilder().setField1(msg1).build(); 214 TestParsingMerge.RepeatedFieldsGenerator.Group1 optionalG2 = 215 TestParsingMerge.RepeatedFieldsGenerator.Group1.newBuilder().setField1(msg2).build(); 216 TestParsingMerge.RepeatedFieldsGenerator.Group1 optionalG3 = 217 TestParsingMerge.RepeatedFieldsGenerator.Group1.newBuilder().setField1(msg3).build(); 218 TestParsingMerge.RepeatedFieldsGenerator.Group2 repeatedG1 = 219 TestParsingMerge.RepeatedFieldsGenerator.Group2.newBuilder().setField1(msg1).build(); 220 TestParsingMerge.RepeatedFieldsGenerator.Group2 repeatedG2 = 221 TestParsingMerge.RepeatedFieldsGenerator.Group2.newBuilder().setField1(msg2).build(); 222 TestParsingMerge.RepeatedFieldsGenerator.Group2 repeatedG3 = 223 TestParsingMerge.RepeatedFieldsGenerator.Group2.newBuilder().setField1(msg3).build(); 224 225 // Assign and serialize RepeatedFieldsGenerator. 226 ByteString data = 227 TestParsingMerge.RepeatedFieldsGenerator.newBuilder() 228 .addField1(msg1) 229 .addField1(msg2) 230 .addField1(msg3) 231 .addField2(msg1) 232 .addField2(msg2) 233 .addField2(msg3) 234 .addField3(msg1) 235 .addField3(msg2) 236 .addField3(msg3) 237 .addGroup1(optionalG1) 238 .addGroup1(optionalG2) 239 .addGroup1(optionalG3) 240 .addGroup2(repeatedG1) 241 .addGroup2(repeatedG2) 242 .addGroup2(repeatedG3) 243 .addExt1(msg1) 244 .addExt1(msg2) 245 .addExt1(msg3) 246 .addExt2(msg1) 247 .addExt2(msg2) 248 .addExt2(msg3) 249 .build() 250 .toByteString(); 251 252 // Parse TestParsingMerge. 253 ExtensionRegistry registry = ExtensionRegistry.newInstance(); 254 UnittestProto.registerAllExtensions(registry); 255 TestParsingMerge parsingMerge = TestParsingMerge.parseFrom(data, registry); 256 257 // Required and optional fields should be merged. 258 assertMessageMerged(parsingMerge.getRequiredAllTypes()); 259 assertMessageMerged(parsingMerge.getOptionalAllTypes()); 260 assertMessageMerged(parsingMerge.getOptionalGroup().getOptionalGroupAllTypes()); 261 assertMessageMerged(parsingMerge.getExtension(TestParsingMerge.optionalExt)); 262 263 // Repeated fields should not be merged. 264 assertThat(parsingMerge.getRepeatedAllTypesCount()).isEqualTo(3); 265 assertThat(parsingMerge.getRepeatedGroupCount()).isEqualTo(3); 266 assertThat(parsingMerge.getExtensionCount(TestParsingMerge.repeatedExt)).isEqualTo(3); 267 } 268 269 @Test testExceptionWhenMergingExtendedMessagesMissingRequiredFields()270 public void testExceptionWhenMergingExtendedMessagesMissingRequiredFields() { 271 // create a TestMergeException message (missing required fields) that looks like 272 // all_extensions { 273 // [TestRequired.single] { 274 // } 275 // } 276 TestMergeException.Builder message = TestMergeException.newBuilder(); 277 message 278 .getAllExtensionsBuilder() 279 .setExtension(TestRequired.single, TestRequired.newBuilder().buildPartial()); 280 ByteString byteString = message.buildPartial().toByteString(); 281 282 // duplicate the bytestring to make the `all_extensions` field repeat twice, so that it will 283 // need merging when parsing back 284 ByteString duplicatedByteString = byteString.concat(byteString); 285 286 byte[] bytes = duplicatedByteString.toByteArray(); 287 ExtensionRegistry registry = ExtensionRegistry.newInstance(); 288 UnittestProto.registerAllExtensions(registry); 289 290 // `parseFrom` should throw InvalidProtocolBufferException, not UninitializedMessageException, 291 // for each of the 5 possible input types: 292 293 // parseFrom(ByteString) 294 try { 295 TestMergeException.parseFrom(duplicatedByteString, registry); 296 assertWithMessage("Expected InvalidProtocolBufferException").fail(); 297 } catch (Exception e) { 298 assertThat(e.getClass()).isEqualTo(InvalidProtocolBufferException.class); 299 } 300 301 // parseFrom(ByteArray) 302 try { 303 TestMergeException.parseFrom(bytes, registry); 304 assertWithMessage("Expected InvalidProtocolBufferException").fail(); 305 } catch (Exception e) { 306 assertThat(e.getClass()).isEqualTo(InvalidProtocolBufferException.class); 307 } 308 309 // parseFrom(InputStream) 310 try { 311 TestMergeException.parseFrom(new ByteArrayInputStream(bytes), registry); 312 assertWithMessage("Expected InvalidProtocolBufferException").fail(); 313 } catch (Exception e) { 314 assertThat(e.getClass()).isEqualTo(InvalidProtocolBufferException.class); 315 } 316 317 // parseFrom(CodedInputStream) 318 try { 319 TestMergeException.parseFrom(CodedInputStream.newInstance(bytes), registry); 320 assertWithMessage("Expected InvalidProtocolBufferException").fail(); 321 } catch (Exception e) { 322 assertThat(e.getClass()).isEqualTo(InvalidProtocolBufferException.class); 323 } 324 325 // parseFrom(ByteBuffer) 326 try { 327 TestMergeException.parseFrom(duplicatedByteString.asReadOnlyByteBuffer(), registry); 328 assertWithMessage("Expected InvalidProtocolBufferException").fail(); 329 } catch (Exception e) { 330 assertThat(e.getClass()).isEqualTo(InvalidProtocolBufferException.class); 331 } 332 } 333 334 @Test testParseDelimitedFrom_firstByteInterrupted_preservesCause()335 public void testParseDelimitedFrom_firstByteInterrupted_preservesCause() { 336 try { 337 TestAllTypes.parseDelimitedFrom( 338 new InputStream() { 339 @Override 340 public int read() throws IOException { 341 throw new InterruptedIOException(); 342 } 343 }); 344 assertWithMessage("Expected InterruptedIOException").fail(); 345 } catch (Exception e) { 346 assertThat(e.getClass()).isEqualTo(InterruptedIOException.class); 347 } 348 } 349 350 @Test testParseDelimitedFrom_secondByteInterrupted_preservesCause()351 public void testParseDelimitedFrom_secondByteInterrupted_preservesCause() { 352 try { 353 TestAllTypes.parseDelimitedFrom( 354 new InputStream() { 355 private int i; 356 357 @Override 358 public int read() throws IOException { 359 switch (i++) { 360 case 0: 361 return 1; 362 case 1: 363 throw new InterruptedIOException(); 364 default: 365 throw new AssertionError(); 366 } 367 } 368 }); 369 assertWithMessage("Expected InterruptedIOException").fail(); 370 } catch (Exception e) { 371 assertThat(e.getClass()).isEqualTo(InterruptedIOException.class); 372 } 373 } 374 } 375