1 /* 2 * Copyright 2015 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.protobuf.lite; 18 19 import static org.junit.Assert.assertArrayEquals; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertSame; 23 import static org.junit.Assert.fail; 24 25 import com.google.common.io.ByteStreams; 26 import com.google.protobuf.ByteString; 27 import com.google.protobuf.Empty; 28 import com.google.protobuf.Enum; 29 import com.google.protobuf.InvalidProtocolBufferException; 30 import com.google.protobuf.Type; 31 import io.grpc.Drainable; 32 import io.grpc.KnownLength; 33 import io.grpc.Metadata; 34 import io.grpc.MethodDescriptor.Marshaller; 35 import io.grpc.MethodDescriptor.PrototypeMarshaller; 36 import io.grpc.Status; 37 import io.grpc.StatusRuntimeException; 38 import io.grpc.internal.GrpcUtil; 39 import java.io.ByteArrayInputStream; 40 import java.io.ByteArrayOutputStream; 41 import java.io.IOException; 42 import java.io.InputStream; 43 import java.util.Arrays; 44 import org.junit.Rule; 45 import org.junit.Test; 46 import org.junit.rules.ExpectedException; 47 import org.junit.runner.RunWith; 48 import org.junit.runners.JUnit4; 49 50 /** Unit tests for {@link ProtoLiteUtils}. */ 51 @RunWith(JUnit4.class) 52 public class ProtoLiteUtilsTest { 53 54 @Rule public final ExpectedException thrown = ExpectedException.none(); 55 56 private Marshaller<Type> marshaller = ProtoLiteUtils.marshaller(Type.getDefaultInstance()); 57 private Type proto = Type.newBuilder().setName("name").build(); 58 59 @Test testPassthrough()60 public void testPassthrough() { 61 assertSame(proto, marshaller.parse(marshaller.stream(proto))); 62 } 63 64 @Test testRoundtrip()65 public void testRoundtrip() throws Exception { 66 InputStream is = marshaller.stream(proto); 67 is = new ByteArrayInputStream(ByteStreams.toByteArray(is)); 68 assertEquals(proto, marshaller.parse(is)); 69 } 70 71 @Test testInvalidatedMessage()72 public void testInvalidatedMessage() throws Exception { 73 InputStream is = marshaller.stream(proto); 74 // Invalidates message, and drains all bytes 75 byte[] unused = ByteStreams.toByteArray(is); 76 try { 77 ((ProtoInputStream) is).message(); 78 fail("Expected exception"); 79 } catch (IllegalStateException ex) { 80 // expected 81 } 82 // Zero bytes is the default message 83 assertEquals(Type.getDefaultInstance(), marshaller.parse(is)); 84 } 85 86 @Test parseInvalid()87 public void parseInvalid() throws Exception { 88 InputStream is = new ByteArrayInputStream(new byte[] {-127}); 89 try { 90 marshaller.parse(is); 91 fail("Expected exception"); 92 } catch (StatusRuntimeException ex) { 93 assertEquals(Status.Code.INTERNAL, ex.getStatus().getCode()); 94 assertNotNull(((InvalidProtocolBufferException) ex.getCause()).getUnfinishedMessage()); 95 } 96 } 97 98 @Test testMismatch()99 public void testMismatch() throws Exception { 100 Marshaller<Enum> enumMarshaller = ProtoLiteUtils.marshaller(Enum.getDefaultInstance()); 101 // Enum's name and Type's name are both strings with tag 1. 102 Enum altProto = Enum.newBuilder().setName(proto.getName()).build(); 103 assertEquals(proto, marshaller.parse(enumMarshaller.stream(altProto))); 104 } 105 106 @Test introspection()107 public void introspection() throws Exception { 108 Marshaller<Enum> enumMarshaller = ProtoLiteUtils.marshaller(Enum.getDefaultInstance()); 109 PrototypeMarshaller<Enum> prototypeMarshaller = (PrototypeMarshaller<Enum>) enumMarshaller; 110 assertSame(Enum.getDefaultInstance(), prototypeMarshaller.getMessagePrototype()); 111 assertSame(Enum.class, prototypeMarshaller.getMessageClass()); 112 } 113 114 @Test marshallerShouldNotLimitProtoSize()115 public void marshallerShouldNotLimitProtoSize() throws Exception { 116 // The default limit is 64MB. Using a larger proto to verify that the limit is not enforced. 117 byte[] bigName = new byte[70 * 1024 * 1024]; 118 Arrays.fill(bigName, (byte) 32); 119 120 proto = Type.newBuilder().setNameBytes(ByteString.copyFrom(bigName)).build(); 121 122 // Just perform a round trip to verify that it works. 123 testRoundtrip(); 124 } 125 126 @Test testAvailable()127 public void testAvailable() throws Exception { 128 InputStream is = marshaller.stream(proto); 129 assertEquals(proto.getSerializedSize(), is.available()); 130 is.read(); 131 assertEquals(proto.getSerializedSize() - 1, is.available()); 132 while (is.read() != -1) {} 133 assertEquals(-1, is.read()); 134 assertEquals(0, is.available()); 135 } 136 137 @Test testEmpty()138 public void testEmpty() throws IOException { 139 Marshaller<Empty> marshaller = ProtoLiteUtils.marshaller(Empty.getDefaultInstance()); 140 InputStream is = marshaller.stream(Empty.getDefaultInstance()); 141 assertEquals(0, is.available()); 142 byte[] b = new byte[10]; 143 assertEquals(-1, is.read(b)); 144 assertArrayEquals(new byte[10], b); 145 // Do the same thing again, because the internal state may be different 146 assertEquals(-1, is.read(b)); 147 assertArrayEquals(new byte[10], b); 148 assertEquals(-1, is.read()); 149 assertEquals(0, is.available()); 150 } 151 152 @Test testDrainTo_all()153 public void testDrainTo_all() throws Exception { 154 byte[] golden = ByteStreams.toByteArray(marshaller.stream(proto)); 155 InputStream is = marshaller.stream(proto); 156 Drainable d = (Drainable) is; 157 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 158 int drained = d.drainTo(baos); 159 assertEquals(baos.size(), drained); 160 assertArrayEquals(golden, baos.toByteArray()); 161 assertEquals(0, is.available()); 162 } 163 164 @Test testDrainTo_partial()165 public void testDrainTo_partial() throws Exception { 166 final byte[] golden; 167 { 168 InputStream is = marshaller.stream(proto); 169 is.read(); 170 golden = ByteStreams.toByteArray(is); 171 } 172 InputStream is = marshaller.stream(proto); 173 is.read(); 174 Drainable d = (Drainable) is; 175 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 176 int drained = d.drainTo(baos); 177 assertEquals(baos.size(), drained); 178 assertArrayEquals(golden, baos.toByteArray()); 179 assertEquals(0, is.available()); 180 } 181 182 @Test testDrainTo_none()183 public void testDrainTo_none() throws Exception { 184 InputStream is = marshaller.stream(proto); 185 byte[] unused = ByteStreams.toByteArray(is); 186 Drainable d = (Drainable) is; 187 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 188 assertEquals(0, d.drainTo(baos)); 189 assertArrayEquals(new byte[0], baos.toByteArray()); 190 assertEquals(0, is.available()); 191 } 192 193 @Test metadataMarshaller_roundtrip()194 public void metadataMarshaller_roundtrip() { 195 Metadata.BinaryMarshaller<Type> metadataMarshaller = 196 ProtoLiteUtils.metadataMarshaller(Type.getDefaultInstance()); 197 assertEquals(proto, metadataMarshaller.parseBytes(metadataMarshaller.toBytes(proto))); 198 } 199 200 @Test metadataMarshaller_invalid()201 public void metadataMarshaller_invalid() { 202 Metadata.BinaryMarshaller<Type> metadataMarshaller = 203 ProtoLiteUtils.metadataMarshaller(Type.getDefaultInstance()); 204 try { 205 metadataMarshaller.parseBytes(new byte[] {-127}); 206 fail("Expected exception"); 207 } catch (IllegalArgumentException ex) { 208 assertNotNull(((InvalidProtocolBufferException) ex.getCause()).getUnfinishedMessage()); 209 } 210 } 211 212 @Test extensionRegistry_notNull()213 public void extensionRegistry_notNull() { 214 thrown.expect(NullPointerException.class); 215 thrown.expectMessage("newRegistry"); 216 217 ProtoLiteUtils.setExtensionRegistry(null); 218 } 219 220 @Test parseFromKnowLengthInputStream()221 public void parseFromKnowLengthInputStream() throws Exception { 222 Marshaller<Type> marshaller = ProtoLiteUtils.marshaller(Type.getDefaultInstance()); 223 Type expect = Type.newBuilder().setName("expected name").build(); 224 225 Type result = marshaller.parse(new CustomKnownLengthInputStream(expect.toByteArray())); 226 assertEquals(expect, result); 227 } 228 229 @Test defaultMaxMessageSize()230 public void defaultMaxMessageSize() { 231 assertEquals(GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE, ProtoLiteUtils.DEFAULT_MAX_MESSAGE_SIZE); 232 } 233 234 private static class CustomKnownLengthInputStream extends InputStream implements KnownLength { 235 private int position = 0; 236 private byte[] source; 237 CustomKnownLengthInputStream(byte[] source)238 private CustomKnownLengthInputStream(byte[] source) { 239 this.source = source; 240 } 241 242 @Override available()243 public int available() throws IOException { 244 return source.length - position; 245 } 246 247 @Override read()248 public int read() throws IOException { 249 if (position == source.length) { 250 return -1; 251 } 252 253 return source[position++]; 254 } 255 } 256 } 257