• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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