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