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