Remove Test Api annotation from ProtoInputStream
Also move cts tests to unit tests
Test: atest ProtoInputStreamTests
Fixes: 115635242
Change-Id: I9aff1227328aad6ec2bec3471f73ae90293a028d
diff --git a/tests/ProtoInputStreamTests/Android.mk b/tests/ProtoInputStreamTests/Android.mk
new file mode 100644
index 0000000..eb747cc
--- /dev/null
+++ b/tests/ProtoInputStreamTests/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := ProtoInputStreamTests
+LOCAL_PROTOC_OPTIMIZE_TYPE := nano
+LOCAL_MODULE_TAGS := tests optional
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-proto-files-under, src)
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_CERTIFICATE := platform
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ androidx.test.rules \
+ frameworks-base-testutils \
+ mockito-target-minus-junit4
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/tests/ProtoInputStreamTests/AndroidManifest.xml b/tests/ProtoInputStreamTests/AndroidManifest.xml
new file mode 100644
index 0000000..c11aa73
--- /dev/null
+++ b/tests/ProtoInputStreamTests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.protoinputstream">
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.test.protoinputstream"
+ android:label="ProtoInputStream Tests">
+ </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/tests/ProtoInputStreamTests/AndroidTest.xml b/tests/ProtoInputStreamTests/AndroidTest.xml
new file mode 100644
index 0000000..51ab88e
--- /dev/null
+++ b/tests/ProtoInputStreamTests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Configuration for ProtoInputStream Tests">
+ <option name="test-suite-tag" value="ProtoInputStreamTests" />
+ <option name="config-descriptor:metadata" key="component" value="metrics" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="ProtoInputStreamTests.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.test.protoinputstream" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/tests/ProtoInputStreamTests/TEST_MAPPING b/tests/ProtoInputStreamTests/TEST_MAPPING
new file mode 100644
index 0000000..cf9f077
--- /dev/null
+++ b/tests/ProtoInputStreamTests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "ProtoInputStreamTests"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBoolTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBoolTest.java
new file mode 100644
index 0000000..c21c403
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBoolTest.java
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamBoolTest extends TestCase {
+
+ /**
+ * Test reading single bool field
+ */
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ /**
+ * Implementation of testRead with a given chunkSize.
+ */
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 3 -> 1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ boolean[] results = new boolean[2];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readBoolean(fieldId2);
+ break;
+ case (int) fieldId3:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(false, results[0]);
+ assertEquals(true, results[1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(false);
+ testReadCompat(true);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(boolean val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+ final long fieldId = fieldFlags | ((long) 130 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.boolField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ boolean result = false; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readBoolean(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.boolField, result);
+ }
+
+
+ /**
+ * Test reading repeated bool field
+ */
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ /**
+ * Implementation of testRepeated with a given chunkSize.
+ */
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+
+ // 4 -> 0
+ (byte) 0x20,
+ (byte) 0x00,
+ // 4 -> 1
+ (byte) 0x20,
+ (byte) 0x01,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+
+ // 3 -> 0
+ (byte) 0x18,
+ (byte) 0x00,
+ // 3 -> 1
+ (byte) 0x18,
+ (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ boolean[][] results = new boolean[3][2];
+ int[] indices = new int[3];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readBoolean(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readBoolean(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readBoolean(fieldId3);
+ break;
+ case (int) fieldId4:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(false, results[0][0]);
+ assertEquals(false, results[0][1]);
+ assertEquals(true, results[1][0]);
+ assertEquals(true, results[1][1]);
+ assertEquals(false, results[2][0]);
+ assertEquals(true, results[2][1]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new boolean[0]);
+ testRepeatedCompat(new boolean[]{false, true});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(boolean[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BOOL;
+ final long fieldId = fieldFlags | ((long) 131 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.boolFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ boolean[] result = new boolean[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readBoolean(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.boolFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.boolFieldRepeated[i], result[i]);
+ }
+ }
+
+ /**
+ * Test reading packed bool field
+ */
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ /**
+ * Implementation of testPacked with a given chunkSize.
+ */
+ public void testPacked(int chunkSize) throws IOException {
+
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 4 -> 0,1
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x01,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 3 -> 0,1
+ (byte) 0x1a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x01,
+ };
+
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ boolean[][] results = new boolean[3][2];
+ int[] indices = new int[3];
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readBoolean(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readBoolean(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readBoolean(fieldId3);
+ break;
+ case (int) fieldId4:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(false, results[0][0]);
+ assertEquals(false, results[0][1]);
+ assertEquals(true, results[1][0]);
+ assertEquals(true, results[1][1]);
+ assertEquals(false, results[2][0]);
+ assertEquals(true, results[2][1]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new boolean[0]);
+ testPackedCompat(new boolean[]{false, true});
+ }
+
+ /**
+ * Implementation of testPackedCompat with a given value.
+ */
+ private void testPackedCompat(boolean[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_BOOL;
+ final long fieldId = fieldFlags | ((long) 132 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.boolFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ boolean[] result = new boolean[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readBoolean(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.boolFieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.boolFieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readBoolean(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readBoolean(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readBoolean(fieldId3);
+ // don't fail, length delimited is ok (represents packed booleans)
+ break;
+ case (int) fieldId6:
+ pi.readBoolean(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBytesTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBytesTest.java
new file mode 100644
index 0000000..09fe40e
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBytesTest.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+public class ProtoInputStreamBytesTest extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> { } - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 5 -> { 0, 1, 2, 3, 4 }
+ (byte) 0x2a,
+ (byte) 0x05,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+ // 3 -> { 0, 1, 2, 3, 4, 5 }
+ (byte) 0x1a,
+ (byte) 0x06,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
+ // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc }
+ (byte) 0x22,
+ (byte) 0x04,
+ (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ byte[][] results = new byte[4][];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0] = pi.readBytes(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readBytes(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readBytes(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readBytes(fieldId4);
+ break;
+ case (int) fieldId5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertTrue("Expected: [] Actual: " + Arrays.toString(results[0]),
+ Arrays.equals(new byte[]{}, results[0]));
+ assertTrue("Expected: [] Actual: " + Arrays.toString(results[1]),
+ Arrays.equals(new byte[]{}, results[1]));
+ assertTrue("Expected: [0, 1, 2, 3, 4, 5] Actual: " + Arrays.toString(results[2]),
+ Arrays.equals(new byte[]{0, 1, 2, 3, 4, 5}, results[2]));
+ assertTrue("Expected: [-1, -2, -3, -4] Actual: " + Arrays.toString(results[3]),
+ Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc},
+ results[3]));
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(new byte[0]);
+ testReadCompat(new byte[]{1, 2, 3, 4});
+ testReadCompat(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc});
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(byte[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+ final long fieldId = fieldFlags | ((long) 150 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.bytesField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ byte[] result = new byte[val.length];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readBytes(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.bytesField.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.bytesField[i], result[i]);
+ }
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> { } - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> { 0, 1, 2, 3, 4, 5 }
+ (byte) 0x1a,
+ (byte) 0x06,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
+ // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc }
+ (byte) 0x22,
+ (byte) 0x04,
+ (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc,
+
+ // 5 -> { 0, 1, 2, 3, 4}
+ (byte) 0x2a,
+ (byte) 0x05,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> { } - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> { 0, 1, 2, 3, 4, 5 }
+ (byte) 0x1a,
+ (byte) 0x06,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
+ // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc }
+ (byte) 0x22,
+ (byte) 0x04,
+ (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ byte[][][] results = new byte[4][2][];
+ int[] indices = new int[4];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readBytes(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readBytes(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readBytes(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readBytes(fieldId4);
+ break;
+ case (int) fieldId5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assert (Arrays.equals(new byte[]{}, results[0][0]));
+ assert (Arrays.equals(new byte[]{}, results[0][1]));
+ assert (Arrays.equals(new byte[]{}, results[1][0]));
+ assert (Arrays.equals(new byte[]{}, results[1][1]));
+ assert (Arrays.equals(new byte[]{1, 2, 3, 4}, results[2][0]));
+ assert (Arrays.equals(new byte[]{1, 2, 3, 4}, results[2][1]));
+ assert (Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc},
+ results[3][0]));
+ assert (Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc},
+ results[3][1]));
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new byte[0][]);
+ testRepeatedCompat(new byte[][]{
+ new byte[0],
+ new byte[]{1, 2, 3, 4},
+ new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc}
+ });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(byte[][] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BYTES;
+ final long fieldId = fieldFlags | ((long) 151 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.bytesFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ byte[][] result = new byte[val.length][]; // start off with default value
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readBytes(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.bytesFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.bytesFieldRepeated[i].length, result[i].length);
+ for (int j = 0; j < result[i].length; j++) {
+ assertEquals(readback.bytesFieldRepeated[i][j], result[i][j]);
+ }
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> {1}
+ (byte) 0x0a,
+ (byte) 0x01,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readBytes(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readBytes(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readBytes(fieldId3);
+ // don't fail, length delimited is ok
+ break;
+ case (int) fieldId6:
+ pi.readBytes(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamDoubleTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamDoubleTest.java
new file mode 100644
index 0000000..118fe34
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamDoubleTest.java
@@ -0,0 +1,728 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamDoubleTest extends TestCase {
+
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x51,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x19,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x21,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x29,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x39,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x41,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x49,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ double[] results = new double[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readDouble(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readDouble(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readDouble(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readDouble(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readDouble(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readDouble(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7] = pi.readDouble(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8] = pi.readDouble(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0, results[0]);
+ assertEquals(1.0, results[1]);
+ assertEquals(-1234.432, results[2]);
+ assertEquals(42.42, results[3]);
+ assertEquals(Double.MIN_NORMAL, results[4]);
+ assertEquals(Double.MIN_VALUE, results[5]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6]);
+ assertEquals(Double.NaN, results[7]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1234.432);
+ testReadCompat(42.42);
+ testReadCompat(Double.MIN_NORMAL);
+ testReadCompat(Double.MIN_VALUE);
+ testReadCompat(Double.NEGATIVE_INFINITY);
+ testReadCompat(Double.NaN);
+ testReadCompat(Double.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(double val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+ final long fieldId = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.doubleField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ double result = 0.0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readDouble(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.doubleField, result);
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x19,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x21,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x29,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x39,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x41,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x49,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ // 10 -> 1
+ (byte) 0x51,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x19,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x21,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x29,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x39,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x41,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x49,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ double[][] results = new double[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readDouble(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readDouble(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readDouble(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readDouble(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readDouble(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readDouble(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readDouble(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7][indices[7]++] = pi.readDouble(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8][indices[8]++] = pi.readDouble(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0, results[0][0]);
+ assertEquals(0.0, results[0][1]);
+ assertEquals(1.0, results[1][0]);
+ assertEquals(1.0, results[1][1]);
+ assertEquals(-1234.432, results[2][0]);
+ assertEquals(-1234.432, results[2][1]);
+ assertEquals(42.42, results[3][0]);
+ assertEquals(42.42, results[3][1]);
+ assertEquals(Double.MIN_NORMAL, results[4][0]);
+ assertEquals(Double.MIN_NORMAL, results[4][1]);
+ assertEquals(Double.MIN_VALUE, results[5][0]);
+ assertEquals(Double.MIN_VALUE, results[5][1]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Double.NaN, results[7][0]);
+ assertEquals(Double.NaN, results[7][1]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new double[0]);
+ testRepeatedCompat(new double[]{0, 1, -1234.432, 42.42,
+ Double.MIN_NORMAL, Double.MIN_VALUE, Double.NEGATIVE_INFINITY, Double.NaN,
+ Double.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(double[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_DOUBLE;
+ final long fieldId = fieldFlags | ((long) 11 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.doubleFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ double[] result = new double[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readDouble(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.doubleFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.doubleFieldRepeated[i], result[i]);
+ }
+ }
+
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x52,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1a,
+ (byte) 0x10,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e,
+ (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0,
+ // 4 -> 42.42
+ (byte) 0x22,
+ (byte) 0x10,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f,
+ (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40,
+ // 5 -> Double.MIN_NORMAL
+ (byte) 0x2a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Double.NEGATIVE_INFINITY
+ (byte) 0x3a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff,
+ // 8 -> Double.NaN
+ (byte) 0x42,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f,
+ // 9 -> Double.POSITIVE_INFINITY
+ (byte) 0x4a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ double[][] results = new double[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readDouble(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readDouble(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readDouble(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readDouble(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readDouble(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readDouble(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readDouble(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7][indices[7]++] = pi.readDouble(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8][indices[8]++] = pi.readDouble(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0, results[0][0]);
+ assertEquals(0.0, results[0][1]);
+ assertEquals(1.0, results[1][0]);
+ assertEquals(1.0, results[1][1]);
+ assertEquals(-1234.432, results[2][0]);
+ assertEquals(-1234.432, results[2][1]);
+ assertEquals(42.42, results[3][0]);
+ assertEquals(42.42, results[3][1]);
+ assertEquals(Double.MIN_NORMAL, results[4][0]);
+ assertEquals(Double.MIN_NORMAL, results[4][1]);
+ assertEquals(Double.MIN_VALUE, results[5][0]);
+ assertEquals(Double.MIN_VALUE, results[5][1]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Double.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Double.NaN, results[7][0]);
+ assertEquals(Double.NaN, results[7][1]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Double.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new double[0]);
+ testPackedCompat(new double[]{0, 1, -1234.432, 42.42,
+ Double.MIN_NORMAL, Double.MIN_VALUE, Double.NEGATIVE_INFINITY, Double.NaN,
+ Double.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testPackedCompat with a given value.
+ */
+ private void testPackedCompat(double[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_DOUBLE;
+ final long fieldId = fieldFlags | ((long) 12 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.doubleFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ double[] result = new double[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readDouble(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.doubleFieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.doubleFieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x09,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readDouble(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readDouble(fieldId2);
+ // don't fail, fixed64 is ok
+ break;
+ case (int) fieldId3:
+ pi.readDouble(fieldId3);
+ // don't fail, length delimited is ok (represents packed doubles)
+ break;
+ case (int) fieldId6:
+ pi.readDouble(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamEnumTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamEnumTest.java
new file mode 100644
index 0000000..f55d951
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamEnumTest.java
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamEnumTest extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+ final long fieldId = fieldFlags | ((long) 160 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.outsideField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ // Nano proto drops values that are outside the range, so compare against val
+ assertEquals(val, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[]{});
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_ENUM;
+ final long fieldId = fieldFlags | ((long) 161 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.outsideFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ // Nano proto drops values that are outside the range, so compare against val
+ assertEquals(val.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(val[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[]{});
+ testPackedCompat(new int[]{0, 1});
+
+ // Nano proto has a bug. It gets the size with computeInt32SizeNoTag (correctly)
+ // but incorrectly uses writeRawVarint32 to write the value for negative numbers.
+ //testPackedCompat(new int[] { 0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_ENUM;
+ final long fieldId = fieldFlags | ((long) 162 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.outsideFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ // Nano proto drops values that are outside the range, so compare against val
+ assertEquals(val.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(val[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed enums)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed32Test.java
new file mode 100644
index 0000000..df68476
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed32Test.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamFixed32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+ final long fieldId = fieldFlags | ((long) 90 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32;
+ final long fieldId = fieldFlags | ((long) 91 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x32,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32;
+ final long fieldId = fieldFlags | ((long) 92 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x0d,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x04,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed fixed32)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ // don't fail, fixed32 is ok
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed64Test.java
new file mode 100644
index 0000000..af4130b
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed64Test.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamFixed64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+ final long fieldId = fieldFlags | ((long) 100 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64;
+ final long fieldId = fieldFlags | ((long) 101 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 8 -> 1
+ (byte) 0x42,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64;
+ final long fieldId = fieldFlags | ((long) 102 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.fixed64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.fixed64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.fixed64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x09,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readLong(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readLong(fieldId2);
+ // don't fail, fixed64 is ok
+ break;
+ case (int) fieldId3:
+ pi.readLong(fieldId3);
+ // don't fail, length delimited is ok (represents packed fixed64)
+ break;
+ case (int) fieldId6:
+ pi.readLong(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFloatTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFloatTest.java
new file mode 100644
index 0000000..9bc07dc
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFloatTest.java
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamFloatTest extends TestCase {
+
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x55,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1d,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x25,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x45,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ float[] results = new float[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readFloat(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readFloat(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readFloat(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readFloat(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readFloat(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readFloat(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7] = pi.readFloat(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8] = pi.readFloat(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0f, results[0]);
+ assertEquals(1.0f, results[1]);
+ assertEquals(-1234.432f, results[2]);
+ assertEquals(42.42f, results[3]);
+ assertEquals(Float.MIN_NORMAL, results[4]);
+ assertEquals(Float.MIN_VALUE, results[5]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6]);
+ assertEquals(Float.NaN, results[7]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1234.432f);
+ testReadCompat(42.42f);
+ testReadCompat(Float.MIN_NORMAL);
+ testReadCompat(Float.MIN_VALUE);
+ testReadCompat(Float.NEGATIVE_INFINITY);
+ testReadCompat(Float.NaN);
+ testReadCompat(Float.POSITIVE_INFINITY);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(float val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+ final long fieldId = fieldFlags | ((long) 20 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.floatField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ float result = 0.0f; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readFloat(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.floatField, result);
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1d,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x25,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x45,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+
+ // 10 -> 1
+ (byte) 0x55,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1d,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x25,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x45,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ float[][] results = new float[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readFloat(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readFloat(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readFloat(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readFloat(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readFloat(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readFloat(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readFloat(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7][indices[7]++] = pi.readFloat(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8][indices[8]++] = pi.readFloat(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0f, results[0][0]);
+ assertEquals(0.0f, results[0][1]);
+ assertEquals(1.0f, results[1][0]);
+ assertEquals(1.0f, results[1][1]);
+ assertEquals(-1234.432f, results[2][0]);
+ assertEquals(-1234.432f, results[2][1]);
+ assertEquals(42.42f, results[3][0]);
+ assertEquals(42.42f, results[3][1]);
+ assertEquals(Float.MIN_NORMAL, results[4][0]);
+ assertEquals(Float.MIN_NORMAL, results[4][1]);
+ assertEquals(Float.MIN_VALUE, results[5][0]);
+ assertEquals(Float.MIN_VALUE, results[5][1]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Float.NaN, results[7][0]);
+ assertEquals(Float.NaN, results[7][1]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new float[0]);
+ testRepeatedCompat(new float[]{0, 1, -1234.432f, 42.42f,
+ Float.MIN_NORMAL, Float.MIN_VALUE, Float.NEGATIVE_INFINITY, Float.NaN,
+ Float.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(float[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FLOAT;
+ final long fieldId = fieldFlags | ((long) 21 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.floatFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ float[] result = new float[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readFloat(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.floatFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.floatFieldRepeated[i], result[i]);
+ }
+ }
+
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+ final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL);
+ final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 10 -> 1
+ (byte) 0x52,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f,
+ // 3 -> -1234.432
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4,
+ // 4 -> 42.42
+ (byte) 0x22,
+ (byte) 0x08,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42,
+ // 5 -> Float.MIN_NORMAL
+ (byte) 0x2a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00,
+ // 6 -> DOUBLE.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 7 -> Float.NEGATIVE_INFINITY
+ (byte) 0x3a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff,
+ // 8 -> Float.NaN
+ (byte) 0x42,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f,
+ // 9 -> Float.POSITIVE_INFINITY
+ (byte) 0x4a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ float[][] results = new float[9][2];
+ int[] indices = new int[9];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readFloat(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readFloat(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readFloat(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readFloat(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readFloat(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readFloat(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readFloat(fieldId7);
+ break;
+ case (int) fieldId8:
+ results[7][indices[7]++] = pi.readFloat(fieldId8);
+ break;
+ case (int) fieldId9:
+ results[8][indices[8]++] = pi.readFloat(fieldId9);
+ break;
+ case (int) fieldId10:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+ assertEquals(0.0f, results[0][0]);
+ assertEquals(0.0f, results[0][1]);
+ assertEquals(1.0f, results[1][0]);
+ assertEquals(1.0f, results[1][1]);
+ assertEquals(-1234.432f, results[2][0]);
+ assertEquals(-1234.432f, results[2][1]);
+ assertEquals(42.42f, results[3][0]);
+ assertEquals(42.42f, results[3][1]);
+ assertEquals(Float.MIN_NORMAL, results[4][0]);
+ assertEquals(Float.MIN_NORMAL, results[4][1]);
+ assertEquals(Float.MIN_VALUE, results[5][0]);
+ assertEquals(Float.MIN_VALUE, results[5][1]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][0]);
+ assertEquals(Float.NEGATIVE_INFINITY, results[6][1]);
+ assertEquals(Float.NaN, results[7][0]);
+ assertEquals(Float.NaN, results[7][1]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][0]);
+ assertEquals(Float.POSITIVE_INFINITY, results[8][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new float[0]);
+ testPackedCompat(new float[]{0, 1, -1234.432f, 42.42f,
+ Float.MIN_NORMAL, Float.MIN_VALUE, Float.NEGATIVE_INFINITY, Float.NaN,
+ Float.POSITIVE_INFINITY,
+ });
+ }
+
+ /**
+ * Implementation of testPackedCompat with a given value.
+ */
+ private void testPackedCompat(float[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FLOAT;
+ final long fieldId = fieldFlags | ((long) 22 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.floatFieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ float[] result = new float[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readFloat(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.floatFieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.floatFieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x0d,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x04,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readFloat(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readFloat(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readFloat(fieldId3);
+ // don't fail, length delimited is ok (represents packed floats)
+ break;
+ case (int) fieldId6:
+ pi.readFloat(fieldId6);
+ // don't fail, fixed32 is ok
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt32Test.java
new file mode 100644
index 0000000..0065870
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt32Test.java
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamInt32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+ final long fieldId = fieldFlags | ((long) 30 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32;
+ final long fieldId = fieldFlags | ((long) 31 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_INT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32;
+ final long fieldId = fieldFlags | ((long) 32 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed int32)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt64Test.java
new file mode 100644
index 0000000..4d6d105
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt64Test.java
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamInt64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 8 -> Long.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+ final long fieldId = fieldFlags | ((long) 40 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> Long.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64;
+ final long fieldId = fieldFlags | ((long) 41 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_INT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 8 -> Long.MAX_VALUE
+ (byte) 0x42,
+ (byte) 0x12,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x12,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64;
+ final long fieldId = fieldFlags | ((long) 42 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.int64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.int64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.int64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readLong(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readLong(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readLong(fieldId3);
+ // don't fail, length delimited is ok (represents packed int64)
+ break;
+ case (int) fieldId6:
+ pi.readLong(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamObjectTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamObjectTest.java
new file mode 100644
index 0000000..5e49eea
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamObjectTest.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamObjectTest extends TestCase {
+
+
+ class SimpleObject {
+ public char mChar;
+ public char mLargeChar;
+ public String mString;
+ public SimpleObject mNested;
+
+ void parseProto(ProtoInputStream pi) throws IOException {
+ final long uintFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long stringFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+ final long charId = uintFieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long largeCharId = uintFieldFlags | ((long) 5000 & 0x0ffffffffL);
+ final long stringId = stringFieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long nestedId = messageFieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) charId:
+ mChar = (char) pi.readInt(charId);
+ break;
+ case (int) largeCharId:
+ mLargeChar = (char) pi.readInt(largeCharId);
+ break;
+ case (int) stringId:
+ mString = pi.readString(stringId);
+ break;
+ case (int) nestedId:
+ long token = pi.start(nestedId);
+ mNested = new SimpleObject();
+ mNested.parseProto(pi);
+ pi.end(token);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Test reading an object with one char in it.
+ */
+ public void testObjectOneChar() throws IOException {
+ testObjectOneChar(0);
+ testObjectOneChar(1);
+ testObjectOneChar(5);
+ }
+
+ /**
+ * Implementation of testObjectOneChar for a given chunkSize.
+ */
+ private void testObjectOneChar(int chunkSize) throws IOException {
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long messageId1 = messageFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // Message 2 : { char 2 : 'c' }
+ (byte) 0x12, (byte) 0x02, (byte) 0x10, (byte) 0x63,
+ // Message 1 : { char 2 : 'b' }
+ (byte) 0x0a, (byte) 0x02, (byte) 0x10, (byte) 0x62,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject result = null;
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) messageId1:
+ final long token = pi.start(messageId1);
+ result = new SimpleObject();
+ result.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) messageId2:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNotNull(result);
+ assertEquals('b', result.mChar);
+ }
+
+ /**
+ * Test reading an object with one multibyte unicode char in it.
+ */
+ public void testObjectOneLargeChar() throws IOException {
+ testObjectOneLargeChar(0);
+ testObjectOneLargeChar(1);
+ testObjectOneLargeChar(5);
+ }
+
+ /**
+ * Implementation of testObjectOneLargeChar for a given chunkSize.
+ */
+ private void testObjectOneLargeChar(int chunkSize) throws IOException {
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long messageId1 = messageFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // Message 2 : { char 5000 : '\u3110' }
+ (byte) 0x12, (byte) 0x05, (byte) 0xc0, (byte) 0xb8,
+ (byte) 0x02, (byte) 0x90, (byte) 0x62,
+ // Message 1 : { char 5000 : '\u3110' }
+ (byte) 0x0a, (byte) 0x05, (byte) 0xc0, (byte) 0xb8,
+ (byte) 0x02, (byte) 0x90, (byte) 0x62,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject result = null;
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) messageId1:
+ final long token = pi.start(messageId1);
+ result = new SimpleObject();
+ result.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) messageId2:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNotNull(result);
+ assertEquals('\u3110', result.mLargeChar);
+ }
+
+ /**
+ * Test reading a char, then an object, then a char.
+ */
+ public void testObjectAndTwoChars() throws IOException {
+ testObjectAndTwoChars(0);
+ testObjectAndTwoChars(1);
+ testObjectAndTwoChars(5);
+ }
+
+ /**
+ * Implementation of testObjectAndTwoChars for a given chunkSize.
+ */
+ private void testObjectAndTwoChars(int chunkSize) throws IOException {
+ final long uintFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long charId1 = uintFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long charId4 = uintFieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 'a'
+ (byte) 0x08, (byte) 0x61,
+ // Message 1 : { char 2 : 'b' }
+ (byte) 0x12, (byte) 0x02, (byte) 0x10, (byte) 0x62,
+ // 4 -> 'c'
+ (byte) 0x20, (byte) 0x63,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject obj = null;
+ char char1 = '\0';
+ char char4 = '\0';
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) charId1:
+ char1 = (char) pi.readInt(charId1);
+ break;
+ case (int) messageId2:
+ final long token = pi.start(messageId2);
+ obj = new SimpleObject();
+ obj.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) charId4:
+ char4 = (char) pi.readInt(charId4);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals('a', char1);
+ assertNotNull(obj);
+ assertEquals('b', obj.mChar);
+ assertEquals('c', char4);
+ }
+
+ /**
+ * Test reading a char, then an object with an int and a string in it, then a char.
+ */
+ public void testComplexObject() throws IOException {
+ testComplexObject(0);
+ testComplexObject(1);
+ testComplexObject(5);
+ }
+
+ /**
+ * Implementation of testComplexObject for a given chunkSize.
+ */
+ private void testComplexObject(int chunkSize) throws IOException {
+ final long uintFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long charId1 = uintFieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long charId4 = uintFieldFlags | ((long) 4 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 'x'
+ (byte) 0x08, (byte) 0x78,
+ // begin object 2
+ (byte) 0x12, (byte) 0x10,
+ // 2 -> 'y'
+ (byte) 0x10, (byte) 0x79,
+ // 4 -> "abcdefghijkl"
+ (byte) 0x22, (byte) 0x0c,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66,
+ (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6a, (byte) 0x6b, (byte) 0x6c,
+ // 4 -> 'z'
+ (byte) 0x20, (byte) 0x7a,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject obj = null;
+ char char1 = '\0';
+ char char4 = '\0';
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) charId1:
+ char1 = (char) pi.readInt(charId1);
+ break;
+ case (int) messageId2:
+ final long token = pi.start(messageId2);
+ obj = new SimpleObject();
+ obj.parseProto(pi);
+ pi.end(token);
+ break;
+ case (int) charId4:
+ char4 = (char) pi.readInt(charId4);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals('x', char1);
+ assertNotNull(obj);
+ assertEquals('y', obj.mChar);
+ assertEquals("abcdefghijkl", obj.mString);
+ assertEquals('z', char4);
+ }
+
+ /**
+ * Test reading 3 levels deep of objects.
+ */
+ public void testDeepObjects() throws IOException {
+ testDeepObjects(0);
+ testDeepObjects(1);
+ testDeepObjects(5);
+ }
+
+ /**
+ * Implementation of testDeepObjects for a given chunkSize.
+ */
+ private void testDeepObjects(int chunkSize) throws IOException {
+ final long messageFieldFlags =
+ ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+ final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // begin object id 2
+ (byte) 0x12, (byte) 0x1a,
+ // 2 -> 'a'
+ (byte) 0x10, (byte) 0x61,
+ // begin nested object id 5
+ (byte) 0x2a, (byte) 0x15,
+ // 5000 -> '\u3110'
+ (byte) 0xc0, (byte) 0xb8,
+ (byte) 0x02, (byte) 0x90, (byte) 0x62,
+ // begin nested object id 5
+ (byte) 0x2a, (byte) 0x0e,
+ // 4 -> "abcdefghijkl"
+ (byte) 0x22, (byte) 0x0c,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66,
+ (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6a, (byte) 0x6b, (byte) 0x6c,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+
+ SimpleObject obj = null;
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) messageId2:
+ final long token = pi.start(messageId2);
+ obj = new SimpleObject();
+ obj.parseProto(pi);
+ pi.end(token);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNotNull(obj);
+ assertEquals('a', obj.mChar);
+ assertNotNull(obj.mNested);
+ assertEquals('\u3110', obj.mNested.mLargeChar);
+ assertNotNull(obj.mNested.mNested);
+ assertEquals("abcdefghijkl", obj.mNested.mNested.mString);
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> {1}
+ (byte) 0x0a,
+ (byte) 0x01,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readBytes(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readBytes(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readBytes(fieldId3);
+ // don't fail, length delimited is ok
+ break;
+ case (int) fieldId6:
+ pi.readBytes(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed32Test.java
new file mode 100644
index 0000000..75c88a4
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed32Test.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSFixed32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+ final long fieldId = fieldFlags | ((long) 110 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 6 -> 1
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0d,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x15,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x25,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2d,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32;
+ final long fieldId = fieldFlags | ((long) 111 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> 1
+ (byte) 0x32,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x08,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x08,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32;
+ final long fieldId = fieldFlags | ((long) 112 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x04,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed sfixed32)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ // don't fail, fixed32 is ok
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed64Test.java
new file mode 100644
index 0000000..4c65cf4
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed64Test.java
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSFixed64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 8 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+ final long fieldId = fieldFlags | ((long) 120 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> 1
+ (byte) 0x41,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x09,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x19,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x21,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x29,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x31,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x39,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64;
+ final long fieldId = fieldFlags | ((long) 121 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 8 -> 1
+ (byte) 0x42,
+ (byte) 0x10,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x10,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x10,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64;
+ final long fieldId = fieldFlags | ((long) 122 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sfixed64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sfixed64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sfixed64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readLong(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readLong(fieldId2);
+ // don't fail, fixed32 is ok
+ break;
+ case (int) fieldId3:
+ pi.readLong(fieldId3);
+ // don't fail, length delimited is ok (represents packed sfixed64)
+ break;
+ case (int) fieldId6:
+ pi.readLong(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt32Test.java
new file mode 100644
index 0000000..6854cd8
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt32Test.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSInt32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+ final long fieldId = fieldFlags | ((long) 70 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32;
+ final long fieldId = fieldFlags | ((long) 71 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x02,
+ (byte) 0x02,
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32;
+ final long fieldId = fieldFlags | ((long) 72 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed sint32)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt64Test.java
new file mode 100644
index 0000000..c53e9d7
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt64Test.java
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamSInt64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+ final long fieldId = fieldFlags | ((long) 80 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x02,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64;
+ final long fieldId = fieldFlags | ((long) 81 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x02,
+ (byte) 0x02,
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x42,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x14,
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64;
+ final long fieldId = fieldFlags | ((long) 82 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.sint64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.sint64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.sint64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readLong(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readLong(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readLong(fieldId3);
+ // don't fail, length delimited is ok (represents packed sint64)
+ break;
+ case (int) fieldId6:
+ pi.readLong(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamStringTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamStringTest.java
new file mode 100644
index 0000000..816d5f9
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamStringTest.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamStringTest extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, not written
+ // 2 -> "" - default value, not written
+ // 3 -> "abcd\u3110!"
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
+ (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
+ // 5 -> "Hi"
+ (byte) 0x2a,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+ // 4 -> "Hi"
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ String[] results = new String[4];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0] = pi.readString(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readString(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readString(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readString(fieldId4);
+ break;
+ case (int) fieldId5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertNull(results[0]);
+ assertNull(results[1]);
+ assertEquals("abcd\u3110!", results[2]);
+ assertEquals("Hi", results[3]);
+ }
+
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat("");
+ testReadCompat("abcd\u3110!");
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(String val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+ final long fieldId = fieldFlags | ((long) 140 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.stringField = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ String result = "";
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readString(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.stringField, result);
+ }
+
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_STRING;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> "" - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> "abcd\u3110!"
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
+ (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
+ // 4 -> "Hi"
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+
+ // 5 -> "Hi"
+ (byte) 0x2a,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+
+ // 1 -> null - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x00,
+ // 2 -> "" - default value, written when repeated
+ (byte) 0x12,
+ (byte) 0x00,
+ // 3 -> "abcd\u3110!"
+ (byte) 0x1a,
+ (byte) 0x08,
+ (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64,
+ (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21,
+ // 4 -> "Hi"
+ (byte) 0x22,
+ (byte) 0x02,
+ (byte) 0x48, (byte) 0x69,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ String[][] results = new String[4][2];
+ int[] indices = new int[4];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readString(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readString(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readString(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readString(fieldId4);
+ break;
+ case (int) fieldId5:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals("", results[0][0]);
+ assertEquals("", results[0][1]);
+ assertEquals("", results[1][0]);
+ assertEquals("", results[1][1]);
+ assertEquals("abcd\u3110!", results[2][0]);
+ assertEquals("abcd\u3110!", results[2][1]);
+ assertEquals("Hi", results[3][0]);
+ assertEquals("Hi", results[3][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new String[0]);
+ testRepeatedCompat(new String[]{"", "abcd\u3110!", "Hi"});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(String[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_STRING;
+ final long fieldId = fieldFlags | ((long) 141 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.stringFieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ String[] result = new String[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readString(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.stringFieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.stringFieldRepeated[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> {1}
+ (byte) 0x0a,
+ (byte) 0x01,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readString(fieldId1);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId2:
+ pi.readString(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readString(fieldId3);
+ // don't fail, length delimited is ok (represents packed booleans)
+ break;
+ case (int) fieldId6:
+ pi.readString(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt32Test.java
new file mode 100644
index 0000000..50fc537
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt32Test.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamUInt32Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[] results = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(int val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+ final long fieldId = fieldFlags | ((long) 50 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint32Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint32Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 6 -> MAX_VALUE
+ (byte) 0x30,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new int[0]);
+ testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
+ final long fieldId = fieldFlags | ((long) 51 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint32FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint32FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint32FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 6 -> MAX_VALUE
+ (byte) 0x32,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ int[][] results = new int[5][2];
+ int[] indices = new int[5];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readInt(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readInt(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readInt(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readInt(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readInt(fieldId5);
+ break;
+ case (int) fieldId6:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new int[0]);
+ testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(int[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32;
+ final long fieldId = fieldFlags | ((long) 52 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint32FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ int[] result = new int[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readInt(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint32FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint32FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readLong(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readInt(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readInt(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readInt(fieldId3);
+ // don't fail, length delimited is ok (represents packed uint32)
+ break;
+ case (int) fieldId6:
+ pi.readInt(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt64Test.java
new file mode 100644
index 0000000..20969e9
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt64Test.java
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.test.protoinputstream.nano.Test;
+
+import com.google.protobuf.nano.MessageNano;
+
+import junit.framework.TestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ProtoInputStreamUInt64Test extends TestCase {
+
+ public void testRead() throws IOException {
+ testRead(0);
+ testRead(1);
+ testRead(5);
+ }
+
+ private void testRead(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, not written
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[] results = new long[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ fail("Should never reach this");
+ break;
+ case (int) fieldId2:
+ results[1] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0]);
+ assertEquals(1, results[1]);
+ assertEquals(-1, results[2]);
+ assertEquals(Integer.MIN_VALUE, results[3]);
+ assertEquals(Integer.MAX_VALUE, results[4]);
+ assertEquals(Long.MIN_VALUE, results[5]);
+ assertEquals(Long.MAX_VALUE, results[6]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testReadCompat() throws Exception {
+ testReadCompat(0);
+ testReadCompat(1);
+ testReadCompat(-1);
+ testReadCompat(Integer.MIN_VALUE);
+ testReadCompat(Integer.MAX_VALUE);
+ testReadCompat(Long.MIN_VALUE);
+ testReadCompat(Long.MAX_VALUE);
+ }
+
+ /**
+ * Implementation of testReadCompat with a given value.
+ */
+ private void testReadCompat(long val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+ final long fieldId = fieldFlags | ((long) 60 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint64Field = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long result = 0; // start off with default value
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint64Field, result);
+ }
+
+ public void testRepeated() throws IOException {
+ testRepeated(0);
+ testRepeated(1);
+ testRepeated(5);
+ }
+
+ private void testRepeated(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x40,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x08,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x10,
+ (byte) 0x01,
+ // 3 -> -1
+ (byte) 0x18,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x20,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x28,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x30,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x38,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testRepeatedCompat() throws Exception {
+ testRepeatedCompat(new long[0]);
+ testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testRepeatedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64;
+ final long fieldId = fieldFlags | ((long) 61 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint64FieldRepeated = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint64FieldRepeated.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint64FieldRepeated[i], result[i]);
+ }
+ }
+
+ public void testPacked() throws IOException {
+ testPacked(0);
+ testPacked(1);
+ testPacked(5);
+ }
+
+ private void testPacked(int chunkSize) throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL);
+ final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+ final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL);
+ final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 0 - default value, written when repeated
+ (byte) 0x0a,
+ (byte) 0x02,
+ (byte) 0x00,
+ (byte) 0x00,
+ // 2 -> 1
+ (byte) 0x12,
+ (byte) 0x02,
+ (byte) 0x01,
+ (byte) 0x01,
+
+ // 8 -> Integer.MAX_VALUE
+ (byte) 0x42,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 3 -> -1
+ (byte) 0x1a,
+ (byte) 0x14,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 4 -> Integer.MIN_VALUE
+ (byte) 0x22,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01,
+
+ // 5 -> Integer.MAX_VALUE
+ (byte) 0x2a,
+ (byte) 0x0a,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07,
+
+ // 6 -> Long.MIN_VALUE
+ (byte) 0x32,
+ (byte) 0x14,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80,
+ (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01,
+
+ // 7 -> Long.MAX_VALUE
+ (byte) 0x3a,
+ (byte) 0x12,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize);
+ long[][] results = new long[7][2];
+ int[] indices = new int[7];
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ results[0][indices[0]++] = pi.readLong(fieldId1);
+ break;
+ case (int) fieldId2:
+ results[1][indices[1]++] = pi.readLong(fieldId2);
+ break;
+ case (int) fieldId3:
+ results[2][indices[2]++] = pi.readLong(fieldId3);
+ break;
+ case (int) fieldId4:
+ results[3][indices[3]++] = pi.readLong(fieldId4);
+ break;
+ case (int) fieldId5:
+ results[4][indices[4]++] = pi.readLong(fieldId5);
+ break;
+ case (int) fieldId6:
+ results[5][indices[5]++] = pi.readLong(fieldId6);
+ break;
+ case (int) fieldId7:
+ results[6][indices[6]++] = pi.readLong(fieldId7);
+ break;
+ case (int) fieldId8:
+ // Intentionally don't read the data. Parse should continue normally
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+ stream.close();
+
+ assertEquals(0, results[0][0]);
+ assertEquals(0, results[0][1]);
+ assertEquals(1, results[1][0]);
+ assertEquals(1, results[1][1]);
+ assertEquals(-1, results[2][0]);
+ assertEquals(-1, results[2][1]);
+ assertEquals(Integer.MIN_VALUE, results[3][0]);
+ assertEquals(Integer.MIN_VALUE, results[3][1]);
+ assertEquals(Integer.MAX_VALUE, results[4][0]);
+ assertEquals(Integer.MAX_VALUE, results[4][1]);
+ assertEquals(Long.MIN_VALUE, results[5][0]);
+ assertEquals(Long.MIN_VALUE, results[5][1]);
+ assertEquals(Long.MAX_VALUE, results[6][0]);
+ assertEquals(Long.MAX_VALUE, results[6][1]);
+ }
+
+ /**
+ * Test that reading with ProtoInputStream matches, and can read the output of standard proto.
+ */
+ public void testPackedCompat() throws Exception {
+ testPackedCompat(new long[0]);
+ testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE});
+ }
+
+ /**
+ * Implementation of testRepeatedCompat with a given value.
+ */
+ private void testPackedCompat(long[] val) throws Exception {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64;
+ final long fieldId = fieldFlags | ((long) 62 & 0x0ffffffffL);
+
+ final Test.All all = new Test.All();
+ all.uint64FieldPacked = val;
+
+ final byte[] proto = MessageNano.toByteArray(all);
+
+ final ProtoInputStream pi = new ProtoInputStream(proto);
+ final Test.All readback = Test.All.parseFrom(proto);
+
+ long[] result = new long[val.length];
+ int index = 0;
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId:
+ result[index++] = pi.readLong(fieldId);
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ }
+
+ assertEquals(readback.uint64FieldPacked.length, result.length);
+ for (int i = 0; i < result.length; i++) {
+ assertEquals(readback.uint64FieldPacked[i], result[i]);
+ }
+ }
+
+ /**
+ * Test that using the wrong read method throws an exception
+ */
+ public void testBadReadType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ };
+
+ ProtoInputStream pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readFloat(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readDouble(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readInt(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBoolean(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readBytes(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
+ pi = new ProtoInputStream(protobuf);
+ pi.isNextField(fieldId1);
+ try {
+ pi.readString(fieldId1);
+ fail("Should have throw IllegalArgumentException");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+ }
+
+ /**
+ * Test that unexpected wrong wire types will throw an exception
+ */
+ public void testBadWireType() throws IOException {
+ final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64;
+
+ final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
+ final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
+ final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL);
+ final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL);
+
+ final byte[] protobuf = new byte[]{
+ // 1 : varint -> 1
+ (byte) 0x08,
+ (byte) 0x01,
+ // 2 : fixed64 -> 0x1
+ (byte) 0x11,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ // 3 : length delimited -> { 1 }
+ (byte) 0x1a,
+ (byte) 0x01,
+ (byte) 0x01,
+ // 6 : fixed32
+ (byte) 0x35,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ };
+
+ InputStream stream = new ByteArrayInputStream(protobuf);
+ final ProtoInputStream pi = new ProtoInputStream(stream);
+
+ while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ switch (pi.getFieldNumber()) {
+ case (int) fieldId1:
+ pi.readLong(fieldId1);
+ // don't fail, varint is ok
+ break;
+ case (int) fieldId2:
+ pi.readLong(fieldId2);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ case (int) fieldId3:
+ pi.readLong(fieldId3);
+ // don't fail, length delimited is ok (represents packed uint64)
+ break;
+ case (int) fieldId6:
+ pi.readLong(fieldId6);
+ fail("Should have thrown a WireTypeMismatchException");
+ break;
+ default:
+ fail("Unexpected field id " + pi.getFieldNumber());
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // good
+ }
+ }
+ stream.close();
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java
new file mode 100644
index 0000000..cdf6ae2
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.protoinputstream;
+
+import junit.framework.TestSuite;
+
+public class ProtoTests {
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite(ProtoTests.class.getName());
+
+ suite.addTestSuite(ProtoInputStreamDoubleTest.class);
+ suite.addTestSuite(ProtoInputStreamFloatTest.class);
+ suite.addTestSuite(ProtoInputStreamInt32Test.class);
+ suite.addTestSuite(ProtoInputStreamInt64Test.class);
+ suite.addTestSuite(ProtoInputStreamUInt32Test.class);
+ suite.addTestSuite(ProtoInputStreamUInt64Test.class);
+ suite.addTestSuite(ProtoInputStreamSInt32Test.class);
+ suite.addTestSuite(ProtoInputStreamSInt64Test.class);
+ suite.addTestSuite(ProtoInputStreamFixed32Test.class);
+ suite.addTestSuite(ProtoInputStreamFixed64Test.class);
+ suite.addTestSuite(ProtoInputStreamSFixed32Test.class);
+ suite.addTestSuite(ProtoInputStreamSFixed64Test.class);
+ suite.addTestSuite(ProtoInputStreamBoolTest.class);
+ suite.addTestSuite(ProtoInputStreamStringTest.class);
+ suite.addTestSuite(ProtoInputStreamBytesTest.class);
+ suite.addTestSuite(ProtoInputStreamEnumTest.class);
+ suite.addTestSuite(ProtoInputStreamObjectTest.class);
+
+ return suite;
+ }
+}
diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/test.proto b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/test.proto
new file mode 100644
index 0000000..9ff1d7e
--- /dev/null
+++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/test.proto
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.test.protoinputstream;
+
+/**
+ * Enum that outside the scope of any classes.
+ */
+enum Outside {
+ OUTSIDE_0 = 0;
+ OUTSIDE_1 = 1;
+};
+
+/**
+ * Message that is recursive.
+ */
+message Nested {
+ optional int32 data = 10001;
+ optional Nested nested = 10002;
+};
+
+/**
+ * Message with all of the field types.
+ */
+message All {
+ /**
+ * Enum that is inside the scope of a class.
+ */
+ enum Inside {
+ option allow_alias = true;
+ INSIDE_0 = 0;
+ INSIDE_1 = 1;
+ INSIDE_1A = 1;
+ };
+
+ optional double double_field = 10;
+ repeated double double_field_repeated = 11;
+ repeated double double_field_packed = 12 [packed=true];
+
+ optional float float_field = 20;
+ repeated float float_field_repeated = 21;
+ repeated float float_field_packed = 22 [packed=true];
+
+ optional int32 int32_field = 30;
+ repeated int32 int32_field_repeated = 31;
+ repeated int32 int32_field_packed = 32 [packed=true];
+
+ optional int64 int64_field = 40;
+ repeated int64 int64_field_repeated = 41;
+ repeated int64 int64_field_packed = 42 [packed=true];
+
+ optional uint32 uint32_field = 50;
+ repeated uint32 uint32_field_repeated = 51;
+ repeated uint32 uint32_field_packed = 52 [packed=true];
+
+ optional uint64 uint64_field = 60;
+ repeated uint64 uint64_field_repeated = 61;
+ repeated uint64 uint64_field_packed = 62 [packed=true];
+
+ optional sint32 sint32_field = 70;
+ repeated sint32 sint32_field_repeated = 71;
+ repeated sint32 sint32_field_packed = 72 [packed=true];
+
+ optional sint64 sint64_field = 80;
+ repeated sint64 sint64_field_repeated = 81;
+ repeated sint64 sint64_field_packed = 82 [packed=true];
+
+ optional fixed32 fixed32_field = 90;
+ repeated fixed32 fixed32_field_repeated = 91;
+ repeated fixed32 fixed32_field_packed = 92 [packed=true];
+
+ optional fixed64 fixed64_field = 100;
+ repeated fixed64 fixed64_field_repeated = 101;
+ repeated fixed64 fixed64_field_packed = 102 [packed=true];
+
+ optional sfixed32 sfixed32_field = 110;
+ repeated sfixed32 sfixed32_field_repeated = 111;
+ repeated sfixed32 sfixed32_field_packed = 112 [packed=true];
+
+ optional sfixed64 sfixed64_field = 120;
+ repeated sfixed64 sfixed64_field_repeated = 121;
+ repeated sfixed64 sfixed64_field_packed = 122 [packed=true];
+
+ optional bool bool_field = 130;
+ repeated bool bool_field_repeated = 131;
+ repeated bool bool_field_packed = 132 [packed=true];
+
+ optional string string_field = 140;
+ repeated string string_field_repeated = 141;
+
+ optional bytes bytes_field = 150;
+ repeated bytes bytes_field_repeated = 151;
+
+ optional Outside outside_field = 160;
+ repeated Outside outside_field_repeated = 161;
+ repeated Outside outside_field_packed = 162 [packed=true];
+
+ optional Nested nested_field = 170;
+ repeated Nested nested_field_repeated = 171;
+};