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