• 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 ProtoInputStreamUInt32Test 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_UINT32;
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         final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
50 
51         final byte[] protobuf = new byte[]{
52                 // 1 -> 0 - default value, not written
53                 // 2 -> 1
54                 (byte) 0x10,
55                 (byte) 0x01,
56                 // 6 -> MAX_VALUE
57                 (byte) 0x30,
58                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
59                 // 3 -> -1
60                 (byte) 0x18,
61                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
62                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
63                 // 4 -> MIN_VALUE
64                 (byte) 0x20,
65                 (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
66                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
67                 // 5 -> MAX_VALUE
68                 (byte) 0x28,
69                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
70         };
71 
72         InputStream stream = new ByteArrayInputStream(protobuf);
73         final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
74         int[] results = new int[5];
75         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
76             switch (pi.getFieldNumber()) {
77                 case (int) fieldId1:
78                     fail("Should never reach this");
79                     break;
80                 case (int) fieldId2:
81                     results[1] = pi.readInt(fieldId2);
82                     break;
83                 case (int) fieldId3:
84                     results[2] = pi.readInt(fieldId3);
85                     break;
86                 case (int) fieldId4:
87                     results[3] = pi.readInt(fieldId4);
88                     break;
89                 case (int) fieldId5:
90                     results[4] = pi.readInt(fieldId5);
91                     break;
92                 case (int) fieldId6:
93                     // Intentionally don't read the data. Parse should continue normally
94                     break;
95                 default:
96                     fail("Unexpected field id " + pi.getFieldNumber());
97             }
98         }
99         stream.close();
100 
101         assertEquals(0, results[0]);
102         assertEquals(1, results[1]);
103         assertEquals(-1, results[2]);
104         assertEquals(Integer.MIN_VALUE, results[3]);
105         assertEquals(Integer.MAX_VALUE, results[4]);
106     }
107 
108     /**
109      * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
110      */
testReadCompat()111     public void testReadCompat() throws Exception {
112         testReadCompat(0);
113         testReadCompat(1);
114         testReadCompat(-1);
115         testReadCompat(Integer.MIN_VALUE);
116         testReadCompat(Integer.MAX_VALUE);
117     }
118 
119     /**
120      * Implementation of testReadCompat with a given value.
121      */
testReadCompat(int val)122     private void testReadCompat(int val) throws Exception {
123         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
124         final long fieldId = fieldFlags | ((long) 50 & 0x0ffffffffL);
125 
126         final Test.All all = new Test.All();
127         all.uint32Field = val;
128 
129         final byte[] proto = MessageNano.toByteArray(all);
130 
131         final ProtoInputStream pi = new ProtoInputStream(proto);
132         final Test.All readback = Test.All.parseFrom(proto);
133 
134         int result = 0; // start off with default value
135         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
136             switch (pi.getFieldNumber()) {
137                 case (int) fieldId:
138                     result = pi.readInt(fieldId);
139                     break;
140                 default:
141                     fail("Unexpected field id " + pi.getFieldNumber());
142             }
143         }
144 
145         assertEquals(readback.uint32Field, result);
146     }
147 
testRepeated()148     public void testRepeated() throws IOException {
149         testRepeated(0);
150         testRepeated(1);
151         testRepeated(5);
152     }
153 
testRepeated(int chunkSize)154     private void testRepeated(int chunkSize) throws IOException {
155         final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
156 
157         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
158         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
159         final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
160         final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
161         final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
162         final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
163 
164         final byte[] protobuf = new byte[]{
165                 // 1 -> 0 - default value, written when repeated
166                 (byte) 0x08,
167                 (byte) 0x00,
168                 // 2 -> 1
169                 (byte) 0x10,
170                 (byte) 0x01,
171                 // 6 -> MAX_VALUE
172                 (byte) 0x30,
173                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
174                 // 3 -> -1
175                 (byte) 0x18,
176                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
177                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
178                 // 4 -> MIN_VALUE
179                 (byte) 0x20,
180                 (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
181                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
182                 // 5 -> MAX_VALUE
183                 (byte) 0x28,
184                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
185 
186                 // 1 -> 0 - default value, written when repeated
187                 (byte) 0x08,
188                 (byte) 0x00,
189                 // 2 -> 1
190                 (byte) 0x10,
191                 (byte) 0x01,
192                 // 3 -> -1
193                 (byte) 0x18,
194                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
195                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
196                 // 4 -> MIN_VALUE
197                 (byte) 0x20,
198                 (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
199                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
200                 // 5 -> MAX_VALUE
201                 (byte) 0x28,
202                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
203         };
204 
205         InputStream stream = new ByteArrayInputStream(protobuf);
206         final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
207         int[][] results = new int[5][2];
208         int[] indices = new int[5];
209         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
210 
211             switch (pi.getFieldNumber()) {
212                 case (int) fieldId1:
213                     results[0][indices[0]++] = pi.readInt(fieldId1);
214                     break;
215                 case (int) fieldId2:
216                     results[1][indices[1]++] = pi.readInt(fieldId2);
217                     break;
218                 case (int) fieldId3:
219                     results[2][indices[2]++] = pi.readInt(fieldId3);
220                     break;
221                 case (int) fieldId4:
222                     results[3][indices[3]++] = pi.readInt(fieldId4);
223                     break;
224                 case (int) fieldId5:
225                     results[4][indices[4]++] = pi.readInt(fieldId5);
226                     break;
227                 case (int) fieldId6:
228                     // Intentionally don't read the data. Parse should continue normally
229                     break;
230                 default:
231                     fail("Unexpected field id " + pi.getFieldNumber());
232             }
233         }
234         stream.close();
235 
236 
237         assertEquals(0, results[0][0]);
238         assertEquals(0, results[0][1]);
239         assertEquals(1, results[1][0]);
240         assertEquals(1, results[1][1]);
241         assertEquals(-1, results[2][0]);
242         assertEquals(-1, results[2][1]);
243         assertEquals(Integer.MIN_VALUE, results[3][0]);
244         assertEquals(Integer.MIN_VALUE, results[3][1]);
245         assertEquals(Integer.MAX_VALUE, results[4][0]);
246         assertEquals(Integer.MAX_VALUE, results[4][1]);
247     }
248 
249     /**
250      * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
251      */
testRepeatedCompat()252     public void testRepeatedCompat() throws Exception {
253         testRepeatedCompat(new int[0]);
254         testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
255     }
256 
257     /**
258      * Implementation of testRepeatedCompat with a given value.
259      */
testRepeatedCompat(int[] val)260     private void testRepeatedCompat(int[] val) throws Exception {
261         final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
262         final long fieldId = fieldFlags | ((long) 51 & 0x0ffffffffL);
263 
264         final Test.All all = new Test.All();
265         all.uint32FieldRepeated = val;
266 
267         final byte[] proto = MessageNano.toByteArray(all);
268 
269         final ProtoInputStream pi = new ProtoInputStream(proto);
270         final Test.All readback = Test.All.parseFrom(proto);
271 
272         int[] result = new int[val.length];
273         int index = 0;
274         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
275             switch (pi.getFieldNumber()) {
276                 case (int) fieldId:
277                     result[index++] = pi.readInt(fieldId);
278                     break;
279                 default:
280                     fail("Unexpected field id " + pi.getFieldNumber());
281             }
282         }
283 
284         assertEquals(readback.uint32FieldRepeated.length, result.length);
285         for (int i = 0; i < result.length; i++) {
286             assertEquals(readback.uint32FieldRepeated[i], result[i]);
287         }
288     }
289 
testPacked()290     public void testPacked() throws IOException {
291         testPacked(0);
292         testPacked(1);
293         testPacked(5);
294     }
295 
testPacked(int chunkSize)296     private void testPacked(int chunkSize) throws IOException {
297         final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_UINT32;
298 
299         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
300         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
301         final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
302         final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
303         final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
304         final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
305 
306         final byte[] protobuf = new byte[]{
307                 // 1 -> 0 - default value, written when repeated
308                 (byte) 0x0a,
309                 (byte) 0x02,
310                 (byte) 0x00,
311                 (byte) 0x00,
312                 // 2 -> 1
313                 (byte) 0x12,
314                 (byte) 0x02,
315                 (byte) 0x01,
316                 (byte) 0x01,
317 
318                 // 6 -> MAX_VALUE
319                 (byte) 0x32,
320                 (byte) 0x0a,
321                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
322 
323                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
324 
325                 // 3 -> -1
326                 (byte) 0x1a,
327                 (byte) 0x14,
328                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
329                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
330 
331                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
332                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
333 
334                 // 4 -> MIN_VALUE
335                 (byte) 0x22,
336                 (byte) 0x14,
337                 (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
338                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
339 
340                 (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
341                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
342 
343                 // 5 -> MAX_VALUE
344                 (byte) 0x2a,
345                 (byte) 0x0a,
346                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
347 
348                 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
349         };
350 
351         InputStream stream = new ByteArrayInputStream(protobuf);
352         final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
353         int[][] results = new int[5][2];
354         int[] indices = new int[5];
355         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
356 
357             switch (pi.getFieldNumber()) {
358                 case (int) fieldId1:
359                     results[0][indices[0]++] = pi.readInt(fieldId1);
360                     break;
361                 case (int) fieldId2:
362                     results[1][indices[1]++] = pi.readInt(fieldId2);
363                     break;
364                 case (int) fieldId3:
365                     results[2][indices[2]++] = pi.readInt(fieldId3);
366                     break;
367                 case (int) fieldId4:
368                     results[3][indices[3]++] = pi.readInt(fieldId4);
369                     break;
370                 case (int) fieldId5:
371                     results[4][indices[4]++] = pi.readInt(fieldId5);
372                     break;
373                 case (int) fieldId6:
374                     // Intentionally don't read the data. Parse should continue normally
375                     break;
376                 default:
377                     fail("Unexpected field id " + pi.getFieldNumber());
378             }
379         }
380         stream.close();
381 
382 
383         assertEquals(0, results[0][0]);
384         assertEquals(0, results[0][1]);
385         assertEquals(1, results[1][0]);
386         assertEquals(1, results[1][1]);
387         assertEquals(-1, results[2][0]);
388         assertEquals(-1, results[2][1]);
389         assertEquals(Integer.MIN_VALUE, results[3][0]);
390         assertEquals(Integer.MIN_VALUE, results[3][1]);
391         assertEquals(Integer.MAX_VALUE, results[4][0]);
392         assertEquals(Integer.MAX_VALUE, results[4][1]);
393     }
394 
395     /**
396      * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
397      */
testPackedCompat()398     public void testPackedCompat() throws Exception {
399         testPackedCompat(new int[0]);
400         testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
401     }
402 
403     /**
404      * Implementation of testRepeatedCompat with a given value.
405      */
testPackedCompat(int[] val)406     private void testPackedCompat(int[] val) throws Exception {
407         final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
408         final long fieldId = fieldFlags | ((long) 52 & 0x0ffffffffL);
409 
410         final Test.All all = new Test.All();
411         all.uint32FieldPacked = val;
412 
413         final byte[] proto = MessageNano.toByteArray(all);
414 
415         final ProtoInputStream pi = new ProtoInputStream(proto);
416         final Test.All readback = Test.All.parseFrom(proto);
417 
418         int[] result = new int[val.length];
419         int index = 0;
420         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
421             switch (pi.getFieldNumber()) {
422                 case (int) fieldId:
423                     result[index++] = pi.readInt(fieldId);
424                     break;
425                 default:
426                     fail("Unexpected field id " + pi.getFieldNumber());
427             }
428         }
429 
430         assertEquals(readback.uint32FieldPacked.length, result.length);
431         for (int i = 0; i < result.length; i++) {
432             assertEquals(readback.uint32FieldPacked[i], result[i]);
433         }
434     }
435 
436     /**
437      * Test that using the wrong read method throws an exception
438      */
testBadReadType()439     public void testBadReadType() throws IOException {
440         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
441 
442         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
443 
444         final byte[] protobuf = new byte[]{
445                 // 1 -> 1
446                 (byte) 0x08,
447                 (byte) 0x01,
448         };
449 
450         ProtoInputStream pi = new ProtoInputStream(protobuf);
451         pi.nextField();
452         try {
453             pi.readFloat(fieldId1);
454             fail("Should have thrown IllegalArgumentException");
455         } catch (IllegalArgumentException iae) {
456             // good
457         }
458 
459         pi = new ProtoInputStream(protobuf);
460         pi.nextField();
461         try {
462             pi.readDouble(fieldId1);
463             fail("Should have thrown IllegalArgumentException");
464         } catch (IllegalArgumentException iae) {
465             // good
466         }
467 
468         pi = new ProtoInputStream(protobuf);
469         pi.nextField();
470         try {
471             pi.readBoolean(fieldId1);
472             fail("Should have thrown IllegalArgumentException");
473         } catch (IllegalArgumentException iae) {
474             // good
475         }
476 
477         pi = new ProtoInputStream(protobuf);
478         pi.nextField();
479         try {
480             pi.readLong(fieldId1);
481             fail("Should have thrown IllegalArgumentException");
482         } catch (IllegalArgumentException iae) {
483             // good
484         }
485 
486         pi = new ProtoInputStream(protobuf);
487         pi.nextField();
488         try {
489             pi.readBytes(fieldId1);
490             fail("Should have thrown IllegalArgumentException");
491         } catch (IllegalArgumentException iae) {
492             // good
493         }
494 
495         pi = new ProtoInputStream(protobuf);
496         pi.nextField();
497         try {
498             pi.readString(fieldId1);
499             fail("Should have thrown IllegalArgumentException");
500         } catch (IllegalArgumentException iae) {
501             // good
502         }
503     }
504 
505     /**
506      * Test that unexpected wrong wire types will throw an exception
507      */
testBadWireType()508     public void testBadWireType() throws IOException {
509         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
510 
511         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
512         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
513         final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
514         final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
515 
516         final byte[] protobuf = new byte[]{
517                 // 1 : varint -> 1
518                 (byte) 0x08,
519                 (byte) 0x01,
520                 // 2 : fixed64 -> 0x1
521                 (byte) 0x11,
522                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
523                 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
524                 // 3 : length delimited -> { 1 }
525                 (byte) 0x1a,
526                 (byte) 0x01,
527                 (byte) 0x01,
528                 // 6 : fixed32
529                 (byte) 0x35,
530                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
531         };
532 
533         InputStream stream = new ByteArrayInputStream(protobuf);
534         final ProtoInputStream pi = new ProtoInputStream(stream);
535 
536         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
537             try {
538                 switch (pi.getFieldNumber()) {
539                     case (int) fieldId1:
540                         pi.readInt(fieldId1);
541                         // don't fail, varint is ok
542                         break;
543                     case (int) fieldId2:
544                         pi.readInt(fieldId2);
545                         fail("Should have thrown a WireTypeMismatchException");
546                         break;
547                     case (int) fieldId3:
548                         pi.readInt(fieldId3);
549                         // don't fail, length delimited is ok (represents packed uint32)
550                         break;
551                     case (int) fieldId6:
552                         pi.readInt(fieldId6);
553                         fail("Should have thrown a WireTypeMismatchException");
554                         break;
555                     default:
556                         fail("Unexpected field id " + pi.getFieldNumber());
557                 }
558             } catch (WireTypeMismatchException wtme) {
559                 // good
560             }
561         }
562         stream.close();
563     }
564 }
565