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 12 import protobuf_unittest.UnittestMset.RawMessageSet; 13 import protobuf_unittest.UnittestMset.TestMessageSetExtension1; 14 import protobuf_unittest.UnittestMset.TestMessageSetExtension2; 15 import protobuf_unittest.UnittestMset.TestMessageSetExtension3; 16 import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet; 17 import org.junit.Before; 18 import org.junit.Test; 19 import org.junit.runner.RunWith; 20 import org.junit.runners.JUnit4; 21 22 /** Tests related to handling of MessageSets with lazily parsed extensions. */ 23 @RunWith(JUnit4.class) 24 public class LazilyParsedMessageSetTest { 25 private static final int TYPE_ID_1 = 26 TestMessageSetExtension1.getDescriptor().getExtensions().get(0).getNumber(); 27 private static final int TYPE_ID_2 = 28 TestMessageSetExtension2.getDescriptor().getExtensions().get(0).getNumber(); 29 private static final int TYPE_ID_3 = 30 TestMessageSetExtension3.getDescriptor().getExtensions().get(0).getNumber(); 31 private static final ByteString CORRUPTED_MESSAGE_PAYLOAD = 32 ByteString.copyFrom(new byte[] {(byte) 0xff}); 33 34 @Before setUp()35 public void setUp() { 36 ExtensionRegistryLite.setEagerlyParseMessageSets(false); 37 } 38 39 @Test testParseAndUpdateMessageSet_unaccessedLazyFieldsAreNotLoaded()40 public void testParseAndUpdateMessageSet_unaccessedLazyFieldsAreNotLoaded() throws Exception { 41 ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); 42 extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); 43 extensionRegistry.add(TestMessageSetExtension2.messageSetExtension); 44 extensionRegistry.add(TestMessageSetExtension3.messageSetExtension); 45 46 // Set up a TestMessageSet with 2 extensions. The first extension has corrupted payload 47 // data. The test below makes sure that we never load this extension. If we ever do, then we 48 // will handle the exception and replace the value with the default empty message (this behavior 49 // is tested below in testLoadCorruptedLazyField_getsReplacedWithEmptyMessage). Later on we 50 // check that when we serialize the message set, we still have corrupted payload for the first 51 // extension. 52 RawMessageSet inputRaw = 53 RawMessageSet.newBuilder() 54 .addItem( 55 RawMessageSet.Item.newBuilder() 56 .setTypeId(TYPE_ID_1) 57 .setMessage(CORRUPTED_MESSAGE_PAYLOAD)) 58 .addItem( 59 RawMessageSet.Item.newBuilder() 60 .setTypeId(TYPE_ID_2) 61 .setMessage( 62 TestMessageSetExtension2.newBuilder().setStr("foo").build().toByteString())) 63 .build(); 64 65 ByteString inputData = inputRaw.toByteString(); 66 67 // Re-parse as a TestMessageSet, so that all extensions are lazy 68 TestMessageSet messageSet = TestMessageSet.parseFrom(inputData, extensionRegistry); 69 70 // Update one extension and add a new one. 71 TestMessageSet.Builder builder = messageSet.toBuilder(); 72 builder.setExtension( 73 TestMessageSetExtension2.messageSetExtension, 74 TestMessageSetExtension2.newBuilder().setStr("bar").build()); 75 76 // Call .build() in the middle of updating the builder. This triggers a codepath that we want to 77 // make sure preserves lazy fields. 78 TestMessageSet unusedIntermediateMessageSet = builder.build(); 79 80 builder.setExtension( 81 TestMessageSetExtension3.messageSetExtension, 82 TestMessageSetExtension3.newBuilder().setRequiredInt(666).build()); 83 84 TestMessageSet updatedMessageSet = builder.build(); 85 86 // Check that hasExtension call does not load lazy fields. 87 assertThat(updatedMessageSet.hasExtension(TestMessageSetExtension1.messageSetExtension)) 88 .isTrue(); 89 90 // Serialize. The first extension should still be unloaded and will get serialized using the 91 // same corrupted byte array. 92 ByteString outputData = updatedMessageSet.toByteString(); 93 94 // Re-parse as RawMessageSet 95 RawMessageSet actualRaw = 96 RawMessageSet.parseFrom(outputData, ExtensionRegistry.getEmptyRegistry()); 97 98 RawMessageSet expectedRaw = 99 RawMessageSet.newBuilder() 100 .addItem( 101 RawMessageSet.Item.newBuilder() 102 .setTypeId(TYPE_ID_1) 103 // This is the important part -- we want to make sure that the payload of the 104 // 1st extensions is the same corrupted byte array. If we ever load the 105 // extension during our manipulations above, then we would have replaced it with 106 // the default empty message. 107 .setMessage(CORRUPTED_MESSAGE_PAYLOAD)) 108 .addItem( 109 RawMessageSet.Item.newBuilder() 110 .setTypeId(TYPE_ID_2) 111 .setMessage( 112 TestMessageSetExtension2.newBuilder().setStr("bar").build().toByteString())) 113 .addItem( 114 RawMessageSet.Item.newBuilder() 115 .setTypeId(TYPE_ID_3) 116 .setMessage( 117 TestMessageSetExtension3.newBuilder() 118 .setRequiredInt(666) 119 .build() 120 .toByteString())) 121 .build(); 122 123 assertThat(actualRaw).isEqualTo(expectedRaw); 124 } 125 126 @Test testLoadCorruptedLazyField_getsReplacedWithEmptyMessage()127 public void testLoadCorruptedLazyField_getsReplacedWithEmptyMessage() throws Exception { 128 ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); 129 extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); 130 131 RawMessageSet inputRaw = 132 RawMessageSet.newBuilder() 133 .addItem( 134 RawMessageSet.Item.newBuilder() 135 .setTypeId(TYPE_ID_1) 136 .setMessage(CORRUPTED_MESSAGE_PAYLOAD)) 137 .build(); 138 139 ByteString inputData = inputRaw.toByteString(); 140 141 // Re-parse as a TestMessageSet, so that all extensions are lazy 142 TestMessageSet messageSet = TestMessageSet.parseFrom(inputData, extensionRegistry); 143 144 assertThat(messageSet.getExtension(TestMessageSetExtension1.messageSetExtension)) 145 .isEqualTo(TestMessageSetExtension1.getDefaultInstance()); 146 147 // Serialize. The first extension should be serialized as an empty message. 148 ByteString outputData = messageSet.toByteString(); 149 150 // Re-parse as RawMessageSet 151 RawMessageSet actualRaw = 152 RawMessageSet.parseFrom(outputData, ExtensionRegistry.getEmptyRegistry()); 153 154 RawMessageSet expectedRaw = 155 RawMessageSet.newBuilder() 156 .addItem( 157 RawMessageSet.Item.newBuilder().setTypeId(TYPE_ID_1).setMessage(ByteString.empty())) 158 .build(); 159 160 assertThat(actualRaw).isEqualTo(expectedRaw); 161 } 162 } 163