• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
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 com.android.test.protoinputstream;
18 
19 import android.util.proto.ProtoInputStream;
20 import android.util.proto.ProtoStream;
21 import android.util.proto.WireTypeMismatchException;
22 
23 import com.android.test.protoinputstream.nano.Test;
24 
25 import com.google.protobuf.nano.MessageNano;
26 
27 import junit.framework.TestCase;
28 
29 import java.io.ByteArrayInputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 
33 public class ProtoInputStreamStringTest extends TestCase {
34 
testRead()35     public void testRead() throws IOException {
36         testRead(0);
37         testRead(1);
38         testRead(5);
39     }
40 
testRead(int chunkSize)41     private void testRead(int chunkSize) throws IOException {
42         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
43 
44         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
45         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
46         final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
47         final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
48         final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
49 
50         final byte[] protobuf = new byte[]{
51                 // 1 -> null - default value, not written
52                 // 2 -> "" - default value, not written
53                 // 3 -> "abcd\u3110!"
54                 (byte) 0x1a,
55                 (byte) 0x08,
56                 (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
57                 (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
58                 // 5 -> "Hi"
59                 (byte) 0x2a,
60                 (byte) 0x02,
61                 (byte) 0x48, (byte) 0x69,
62                 // 4 -> "Hi"
63                 (byte) 0x22,
64                 (byte) 0x02,
65                 (byte) 0x48, (byte) 0x69,
66         };
67 
68         InputStream stream = new ByteArrayInputStream(protobuf);
69         final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
70         String[] results = new String[4];
71         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
72 
73             switch (pi.getFieldNumber()) {
74                 case (int) fieldId1:
75                     results[0] = pi.readString(fieldId1);
76                     break;
77                 case (int) fieldId2:
78                     results[1] = pi.readString(fieldId2);
79                     break;
80                 case (int) fieldId3:
81                     results[2] = pi.readString(fieldId3);
82                     break;
83                 case (int) fieldId4:
84                     results[3] = pi.readString(fieldId4);
85                     break;
86                 case (int) fieldId5:
87                     // Intentionally don't read the data. Parse should continue normally
88                     break;
89                 default:
90                     fail("Unexpected field id " + pi.getFieldNumber());
91             }
92         }
93         stream.close();
94 
95         assertNull(results[0]);
96         assertNull(results[1]);
97         assertEquals("abcd\u3110!", results[2]);
98         assertEquals("Hi", results[3]);
99     }
100 
101 
102     /**
103      * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
104      */
testReadCompat()105     public void testReadCompat() throws Exception {
106         testReadCompat("");
107         testReadCompat("abcd\u3110!");
108     }
109 
110     /**
111      * Implementation of testReadCompat with a given value.
112      */
testReadCompat(String val)113     private void testReadCompat(String val) throws Exception {
114         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
115         final long fieldId = fieldFlags | ((long) 140 & 0x0ffffffffL);
116 
117         final Test.All all = new Test.All();
118         all.stringField = val;
119 
120         final byte[] proto = MessageNano.toByteArray(all);
121 
122         final ProtoInputStream pi = new ProtoInputStream(proto);
123         final Test.All readback = Test.All.parseFrom(proto);
124 
125         String result = "";
126         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
127             switch (pi.getFieldNumber()) {
128                 case (int) fieldId:
129                     result = pi.readString(fieldId);
130                     break;
131                 default:
132                     fail("Unexpected field id " + pi.getFieldNumber());
133             }
134         }
135 
136         assertEquals(readback.stringField, result);
137     }
138 
139 
testRepeated()140     public void testRepeated() throws IOException {
141         testRepeated(0);
142         testRepeated(1);
143         testRepeated(5);
144     }
145 
testRepeated(int chunkSize)146     private void testRepeated(int chunkSize) throws IOException {
147         final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_STRING;
148 
149         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
150         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
151         final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
152         final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
153         final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
154 
155         final byte[] protobuf = new byte[]{
156                 // 1 -> null - default value, written when repeated
157                 (byte) 0x0a,
158                 (byte) 0x00,
159                 // 2 -> "" - default value, written when repeated
160                 (byte) 0x12,
161                 (byte) 0x00,
162                 // 3 -> "abcd\u3110!"
163                 (byte) 0x1a,
164                 (byte) 0x08,
165                 (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
166                 (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
167                 // 4 -> "Hi"
168                 (byte) 0x22,
169                 (byte) 0x02,
170                 (byte) 0x48, (byte) 0x69,
171 
172                 // 5 -> "Hi"
173                 (byte) 0x2a,
174                 (byte) 0x02,
175                 (byte) 0x48, (byte) 0x69,
176 
177                 // 1 -> null - default value, written when repeated
178                 (byte) 0x0a,
179                 (byte) 0x00,
180                 // 2 -> "" - default value, written when repeated
181                 (byte) 0x12,
182                 (byte) 0x00,
183                 // 3 -> "abcd\u3110!"
184                 (byte) 0x1a,
185                 (byte) 0x08,
186                 (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
187                 (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
188                 // 4 -> "Hi"
189                 (byte) 0x22,
190                 (byte) 0x02,
191                 (byte) 0x48, (byte) 0x69,
192         };
193 
194         InputStream stream = new ByteArrayInputStream(protobuf);
195         final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
196         String[][] results = new String[4][2];
197         int[] indices = new int[4];
198         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
199 
200             switch (pi.getFieldNumber()) {
201                 case (int) fieldId1:
202                     results[0][indices[0]++] = pi.readString(fieldId1);
203                     break;
204                 case (int) fieldId2:
205                     results[1][indices[1]++] = pi.readString(fieldId2);
206                     break;
207                 case (int) fieldId3:
208                     results[2][indices[2]++] = pi.readString(fieldId3);
209                     break;
210                 case (int) fieldId4:
211                     results[3][indices[3]++] = pi.readString(fieldId4);
212                     break;
213                 case (int) fieldId5:
214                     // Intentionally don't read the data. Parse should continue normally
215                     break;
216                 default:
217                     fail("Unexpected field id " + pi.getFieldNumber());
218             }
219         }
220         stream.close();
221 
222 
223         assertEquals("", results[0][0]);
224         assertEquals("", results[0][1]);
225         assertEquals("", results[1][0]);
226         assertEquals("", results[1][1]);
227         assertEquals("abcd\u3110!", results[2][0]);
228         assertEquals("abcd\u3110!", results[2][1]);
229         assertEquals("Hi", results[3][0]);
230         assertEquals("Hi", results[3][1]);
231     }
232 
233     /**
234      * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
235      */
testRepeatedCompat()236     public void testRepeatedCompat() throws Exception {
237         testRepeatedCompat(new String[0]);
238         testRepeatedCompat(new String[]{"", "abcd\u3110!", "Hi"});
239     }
240 
241     /**
242      * Implementation of testRepeatedCompat with a given value.
243      */
testRepeatedCompat(String[] val)244     private void testRepeatedCompat(String[] val) throws Exception {
245         final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_STRING;
246         final long fieldId = fieldFlags | ((long) 141 & 0x0ffffffffL);
247 
248         final Test.All all = new Test.All();
249         all.stringFieldRepeated = val;
250 
251         final byte[] proto = MessageNano.toByteArray(all);
252 
253         final ProtoInputStream pi = new ProtoInputStream(proto);
254         final Test.All readback = Test.All.parseFrom(proto);
255 
256         String[] result = new String[val.length];
257         int index = 0;
258         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
259             switch (pi.getFieldNumber()) {
260                 case (int) fieldId:
261                     result[index++] = pi.readString(fieldId);
262                     break;
263                 default:
264                     fail("Unexpected field id " + pi.getFieldNumber());
265             }
266         }
267 
268         assertEquals(readback.stringFieldRepeated.length, result.length);
269         for (int i = 0; i < result.length; i++) {
270             assertEquals(readback.stringFieldRepeated[i], result[i]);
271         }
272     }
273 
274     /**
275      * Test that using the wrong read method throws an exception
276      */
testBadReadType()277     public void testBadReadType() throws IOException {
278         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
279 
280         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
281 
282         final byte[] protobuf = new byte[]{
283                 // 1 -> {1}
284                 (byte) 0x0a,
285                 (byte) 0x01,
286                 (byte) 0x01,
287         };
288 
289         ProtoInputStream pi = new ProtoInputStream(protobuf);
290         pi.nextField();
291         try {
292             pi.readFloat(fieldId1);
293             fail("Should have thrown IllegalArgumentException");
294         } catch (IllegalArgumentException iae) {
295             // good
296         }
297 
298         pi = new ProtoInputStream(protobuf);
299         pi.nextField();
300         try {
301             pi.readDouble(fieldId1);
302             fail("Should have thrown IllegalArgumentException");
303         } catch (IllegalArgumentException iae) {
304             // good
305         }
306 
307         pi = new ProtoInputStream(protobuf);
308         pi.nextField();
309         try {
310             pi.readInt(fieldId1);
311             fail("Should have thrown IllegalArgumentException");
312         } catch (IllegalArgumentException iae) {
313             // good
314         }
315 
316         pi = new ProtoInputStream(protobuf);
317         pi.nextField();
318         try {
319             pi.readLong(fieldId1);
320             fail("Should have thrown IllegalArgumentException");
321         } catch (IllegalArgumentException iae) {
322             // good
323         }
324 
325         pi = new ProtoInputStream(protobuf);
326         pi.nextField();
327         try {
328             pi.readBytes(fieldId1);
329             fail("Should have thrown IllegalArgumentException");
330         } catch (IllegalArgumentException iae) {
331             // good
332         }
333 
334         pi = new ProtoInputStream(protobuf);
335         pi.nextField();
336         try {
337             pi.readBoolean(fieldId1);
338             fail("Should have thrown IllegalArgumentException");
339         } catch (IllegalArgumentException iae) {
340             // good
341         }
342     }
343 
344     /**
345      * Test that unexpected wrong wire types will throw an exception
346      */
testBadWireType()347     public void testBadWireType() throws IOException {
348         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
349 
350         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
351         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
352         final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
353         final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
354 
355         final byte[] protobuf = new byte[]{
356                 // 1 : varint -> 1
357                 (byte) 0x08,
358                 (byte) 0x01,
359                 // 2 : fixed64 -> 0x1
360                 (byte) 0x11,
361                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
362                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
363                 // 3 : length delimited -> { 1 }
364                 (byte) 0x1a,
365                 (byte) 0x01,
366                 (byte) 0x01,
367                 // 6 : fixed32
368                 (byte) 0x35,
369                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
370         };
371 
372         InputStream stream = new ByteArrayInputStream(protobuf);
373         final ProtoInputStream pi = new ProtoInputStream(stream);
374 
375         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
376             try {
377                 switch (pi.getFieldNumber()) {
378                     case (int) fieldId1:
379                         pi.readString(fieldId1);
380                         fail("Should have thrown a WireTypeMismatchException");
381                         break;
382                     case (int) fieldId2:
383                         pi.readString(fieldId2);
384                         fail("Should have thrown a WireTypeMismatchException");
385                         break;
386                     case (int) fieldId3:
387                         pi.readString(fieldId3);
388                         // don't fail, length delimited is ok (represents packed booleans)
389                         break;
390                     case (int) fieldId6:
391                         pi.readString(fieldId6);
392                         fail("Should have thrown a WireTypeMismatchException");
393                         break;
394                     default:
395                         fail("Unexpected field id " + pi.getFieldNumber());
396                 }
397             } catch (WireTypeMismatchException wtme) {
398                 // good
399             }
400         }
401         stream.close();
402     }
403 
404 }
405