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