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 java.io.IOException; 14 import java.nio.ByteBuffer; 15 import java.util.Arrays; 16 import java.util.Collections; 17 import java.util.List; 18 import org.junit.Before; 19 import org.junit.Test; 20 21 public abstract class AbstractSchemaTest<T extends MessageLite> { 22 private Schema<T> schema; 23 24 @Before setup()25 public void setup() { 26 schema = schema(); 27 registerSchemas(); 28 } 29 30 // Subclass should override this method if it needs to register more than one schemas. registerSchemas()31 protected void registerSchemas() { 32 // Register this schema with the runtime to support processing of nested messages. 33 Protobuf.getInstance().registerSchemaOverride(schema.newInstance().getClass(), schema); 34 } 35 schema()36 protected abstract Schema<T> schema(); 37 messageFactory()38 protected abstract ExperimentalMessageFactory<? extends T> messageFactory(); 39 40 @SuppressWarnings("unused") serializedBytesWithInvalidUtf8()41 protected List<ByteBuffer> serializedBytesWithInvalidUtf8() throws IOException { 42 return Collections.emptyList(); 43 } 44 45 @Test randomMessageShouldRoundtrip()46 public void randomMessageShouldRoundtrip() throws IOException { 47 roundtrip("", messageFactory().newMessage()); 48 } 49 50 @Test invalidUtf8StringParsing()51 public void invalidUtf8StringParsing() throws IOException { 52 for (ByteBuffer invalidUtf8Bytes : serializedBytesWithInvalidUtf8()) { 53 Reader reader = BinaryReader.newInstance(invalidUtf8Bytes, /* bufferIsImmutable= */ true); 54 55 T newMsg = schema.newInstance(); 56 try { 57 schema.mergeFrom(newMsg, reader, ExtensionRegistryLite.getEmptyRegistry()); 58 assertWithMessage("should throw invalid").fail(); 59 } catch (InvalidProtocolBufferException expected) { 60 } 61 } 62 } 63 64 @Test mergeFromByteArrayFastPathMayThrowIndexOutOfBoundsException()65 public void mergeFromByteArrayFastPathMayThrowIndexOutOfBoundsException() throws IOException { 66 if (!Android.isOnAndroidDevice()) { 67 // Skip this test if not on Android. 68 return; 69 } 70 byte[] data = messageFactory().newMessage().toByteArray(); 71 int exceptionCount = 0; 72 for (int i = 0; i <= data.length; i++) { 73 byte[] truncatedData = Arrays.copyOf(data, i); 74 try { 75 T message = schema.newInstance(); 76 // Test that this method throws the expected exceptions. 77 schema.mergeFrom(message, truncatedData, 0, i, new ArrayDecoders.Registers()); 78 } catch (InvalidProtocolBufferException e) { 79 // Ignore expected exceptions. 80 } catch (IndexOutOfBoundsException e) { 81 exceptionCount += 1; 82 } 83 } 84 assertThat(exceptionCount).isNotEqualTo(0); 85 } 86 roundtrip( String failureMessage, M msg, Schema<M> schema)87 protected static final <M extends MessageLite> void roundtrip( 88 String failureMessage, M msg, Schema<M> schema) throws IOException { 89 byte[] serializedBytes = ExperimentalSerializationUtil.toByteArray(msg, schema); 90 assertWithMessage(failureMessage) 91 .that(serializedBytes.length) 92 .isEqualTo(msg.getSerializedSize()); 93 94 // Now read it back in and verify it matches the original. 95 if (Android.isOnAndroidDevice()) { 96 // Test the fast path on Android. 97 M newMsg = schema.newInstance(); 98 schema.mergeFrom( 99 newMsg, serializedBytes, 0, serializedBytes.length, new ArrayDecoders.Registers()); 100 schema.makeImmutable(newMsg); 101 assertWithMessage(failureMessage).that(newMsg).isEqualTo(msg); 102 } 103 M newMsg = schema.newInstance(); 104 Reader reader = BinaryReader.newInstance(ByteBuffer.wrap(serializedBytes), true); 105 schema.mergeFrom(newMsg, reader, ExtensionRegistryLite.getEmptyRegistry()); 106 schema.makeImmutable(newMsg); 107 108 assertWithMessage(failureMessage).that(newMsg).isEqualTo(msg); 109 } 110 roundtrip(String failureMessage, T msg)111 protected final void roundtrip(String failureMessage, T msg) throws IOException { 112 roundtrip(failureMessage, msg, schema); 113 } 114 data()115 protected final ExperimentalTestDataProvider data() { 116 return messageFactory().dataProvider(); 117 } 118 newMessagesMissingRequiredFields()119 protected List<T> newMessagesMissingRequiredFields() { 120 return Collections.emptyList(); 121 } 122 123 @SuppressWarnings("unchecked") 124 @Test testRequiredFields()125 public void testRequiredFields() throws Exception { 126 for (T msg : newMessagesMissingRequiredFields()) { 127 if (schema.isInitialized(msg)) { 128 assertThat(msg.toString()).isEmpty(); 129 msg = (T) msg.toBuilder().build(); 130 } 131 assertThat(schema.isInitialized(msg)).isFalse(); 132 } 133 } 134 } 135