Merge "Add unit tests to libprotoutil."
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
index a36bf80..14b2e41 100644
--- a/libs/protoutil/Android.bp
+++ b/libs/protoutil/Android.bp
@@ -42,16 +42,20 @@
cc_test {
name: "libprotoutil_test",
- srcs: [
- "tests/EncodedBuffer_test.cpp",
- ],
+ srcs: ["tests/*"],
shared_libs: [
+ "libbase",
"libcutils",
"libprotoutil",
+ "libprotobuf-cpp-full",
],
static_libs: [
"libgmock",
],
+
+ proto: {
+ type: "full",
+ }
}
diff --git a/libs/protoutil/tests/EncodedBuffer_test.cpp b/libs/protoutil/tests/EncodedBuffer_test.cpp
index 615ab4a..964fc8e 100644
--- a/libs/protoutil/tests/EncodedBuffer_test.cpp
+++ b/libs/protoutil/tests/EncodedBuffer_test.cpp
@@ -17,9 +17,138 @@
using namespace android::util;
+constexpr size_t TEST_CHUNK_SIZE = 16UL;
+constexpr size_t TEST_CHUNK_HALF_SIZE = TEST_CHUNK_SIZE / 2;
+constexpr size_t TEST_CHUNK_3X_SIZE = 3 * TEST_CHUNK_SIZE;
+
+static void expectPointer(EncodedBuffer::Pointer* p, size_t pos) {
+ EXPECT_EQ(p->pos(), pos);
+ EXPECT_EQ(p->index(), pos / TEST_CHUNK_SIZE);
+ EXPECT_EQ(p->offset(), pos % TEST_CHUNK_SIZE);
+}
+
+TEST(EncodedBufferTest, WriteSimple) {
+ EncodedBuffer buffer(TEST_CHUNK_SIZE);
+ EXPECT_EQ(buffer.size(), 0UL);
+ expectPointer(buffer.wp(), 0);
+ EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_SIZE);
+ for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) {
+ buffer.writeRawByte(50 + i);
+ }
+ EXPECT_EQ(buffer.size(), TEST_CHUNK_HALF_SIZE);
+ expectPointer(buffer.wp(), TEST_CHUNK_HALF_SIZE);
+ EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE);
+ for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) {
+ buffer.writeRawByte(80 + i);
+ }
+ EXPECT_EQ(buffer.size(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
+ expectPointer(buffer.wp(), TEST_CHUNK_SIZE + TEST_CHUNK_HALF_SIZE);
+ EXPECT_EQ(buffer.currentToWrite(), TEST_CHUNK_HALF_SIZE);
+
+ // verifies the buffer's data
+ expectPointer(buffer.ep(), 0);
+ for (size_t i = 0; i < TEST_CHUNK_HALF_SIZE; i++) {
+ EXPECT_EQ(buffer.readRawByte(), 50 + i);
+ }
+ for (size_t i = 0; i < TEST_CHUNK_SIZE; i++) {
+ EXPECT_EQ(buffer.readRawByte(), 80 + i);
+ }
+
+ // clears the buffer
+ buffer.clear();
+ EXPECT_EQ(buffer.size(), 0UL);
+ expectPointer(buffer.wp(), 0);
+}
+
+TEST(EncodedBufferTest, WriteVarint) {
+ EncodedBuffer buffer(TEST_CHUNK_SIZE);
+ size_t expected_buffer_size = 0;
+ EXPECT_EQ(buffer.writeRawVarint32(13), 1);
+ expected_buffer_size += 1;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+ EXPECT_EQ(buffer.writeRawVarint32(UINT32_C(-1)), 5);
+ expected_buffer_size += 5;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+ EXPECT_EQ(buffer.writeRawVarint64(200), 2);
+ expected_buffer_size += 2;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+ EXPECT_EQ(buffer.writeRawVarint64(UINT64_C(-1)), 10);
+ expected_buffer_size += 10;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+ buffer.writeRawFixed32(UINT32_C(-1));
+ expected_buffer_size += 4;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+ buffer.writeRawFixed64(UINT64_C(-1));
+ expected_buffer_size += 8;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+ EXPECT_EQ(buffer.writeHeader(32, 2), 2);
+ expected_buffer_size += 2;
+ EXPECT_EQ(buffer.size(), expected_buffer_size);
+
+ // verify data are correctly written to the buffer.
+ expectPointer(buffer.ep(), 0);
+ EXPECT_EQ(buffer.readRawVarint(), UINT32_C(13));
+ EXPECT_EQ(buffer.readRawVarint(), UINT32_C(-1));
+ EXPECT_EQ(buffer.readRawVarint(), UINT64_C(200));
+ EXPECT_EQ(buffer.readRawVarint(), UINT64_C(-1));
+ EXPECT_EQ(buffer.readRawFixed32(), UINT32_C(-1));
+ EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(-1));
+ EXPECT_EQ(buffer.readRawVarint(), UINT64_C((32 << 3) + 2));
+ expectPointer(buffer.ep(), expected_buffer_size);
+}
+
+TEST(EncodedBufferTest, Edit) {
+ EncodedBuffer buffer(TEST_CHUNK_SIZE);
+ buffer.writeRawFixed64(0xdeadbeefdeadbeef);
+ EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0xdeadbeefdeadbeef));
+
+ buffer.editRawFixed32(4, 0x12345678);
+ // fixed 64 is little endian order.
+ buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0
+ EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678deadbeef));
+
+ buffer.wp()->rewind();
+ expectPointer(buffer.wp(), 0);
+ buffer.copy(4, 3);
+ buffer.ep()->rewind(); // rewind ep for readRawFixed64 from 0
+ EXPECT_EQ(buffer.readRawFixed64(), UINT64_C(0x12345678de345678));
+}
+
+TEST(EncodedBufferTest, ReadSimple) {
+ EncodedBuffer buffer(TEST_CHUNK_SIZE);
+ for (size_t i = 0; i < TEST_CHUNK_3X_SIZE; i++) {
+ buffer.writeRawByte(i);
+ }
+ auto iter = buffer.begin();
+ EXPECT_EQ(iter.size(), TEST_CHUNK_3X_SIZE);
+ EXPECT_EQ(iter.bytesRead(), 0);
+
+ expectPointer(iter.rp(), 0);
+ while (iter.readBuffer() != NULL) {
+ iter.rp()->move(iter.currentToRead());
+ }
+ EXPECT_EQ(iter.bytesRead(), TEST_CHUNK_3X_SIZE);
+ expectPointer(iter.rp(), TEST_CHUNK_3X_SIZE);
+
+ iter.rp()->rewind();
+ expectPointer(iter.rp(), 0);
+ uint8_t val = 0;
+ while (iter.hasNext()) {
+ EXPECT_EQ(iter.next(), val);
+ val++;
+ }
+ EXPECT_EQ(iter.bytesRead(), TEST_CHUNK_3X_SIZE);
+ expectPointer(iter.rp(), TEST_CHUNK_3X_SIZE);
+}
+
TEST(EncodedBufferTest, ReadVarint) {
EncodedBuffer buffer;
uint64_t val = UINT64_C(1522865904593);
- buffer.writeRawVarint64(val);
- EXPECT_EQ(val, buffer.begin().readRawVarint());
+ size_t len = buffer.writeRawVarint64(val);
+ auto iter = buffer.begin();
+ EXPECT_EQ(iter.size(), len);
+ EXPECT_EQ(iter.readRawVarint(), val);
}
diff --git a/libs/protoutil/tests/ProtoOutputStream_test.cpp b/libs/protoutil/tests/ProtoOutputStream_test.cpp
new file mode 100644
index 0000000..e8e1557
--- /dev/null
+++ b/libs/protoutil/tests/ProtoOutputStream_test.cpp
@@ -0,0 +1,170 @@
+// 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.
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <android/util/protobuf.h>
+#include <android/util/ProtoOutputStream.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "frameworks/base/libs/protoutil/tests/test.pb.h"
+
+using namespace android::base;
+using namespace android::util;
+using ::testing::StrEq;
+
+static std::string flushToString(ProtoOutputStream* proto) {
+ TemporaryFile tf;
+ std::string content;
+
+ EXPECT_NE(tf.fd, -1);
+ EXPECT_TRUE(proto->flush(tf.fd));
+ EXPECT_TRUE(ReadFileToString(tf.path, &content));
+ return content;
+}
+
+static std::string iterateToString(ProtoOutputStream* proto) {
+ std::string content;
+ content.reserve(proto->size());
+ auto iter = proto->data();
+ while (iter.hasNext()) {
+ content.push_back(iter.next());
+ }
+ return content;
+}
+
+TEST(ProtoOutputStreamTest, Primitives) {
+ std::string s = "hello";
+ const char b[5] = { 'a', 'p', 'p', 'l', 'e' };
+
+ ProtoOutputStream proto;
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | PrimitiveProto::kValInt32FieldNumber, 123));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT64 | PrimitiveProto::kValInt64FieldNumber, -1LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_FLOAT | PrimitiveProto::kValFloatFieldNumber, -23.5f));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_DOUBLE | PrimitiveProto::kValDoubleFieldNumber, 324.5));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_UINT32 | PrimitiveProto::kValUint32FieldNumber, 3424));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_UINT64 | PrimitiveProto::kValUint64FieldNumber, 57LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED32 | PrimitiveProto::kValFixed32FieldNumber, -20));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_FIXED64 | PrimitiveProto::kValFixed64FieldNumber, -37LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BOOL | PrimitiveProto::kValBoolFieldNumber, true));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | PrimitiveProto::kValStringFieldNumber, s));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | PrimitiveProto::kValBytesFieldNumber, b, 5));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED32 | PrimitiveProto::kValSfixed32FieldNumber, 63));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SFIXED64 | PrimitiveProto::kValSfixed64FieldNumber, -54));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SINT32 | PrimitiveProto::kValSint32FieldNumber, -533));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_SINT64 | PrimitiveProto::kValSint64FieldNumber, -61224762453LL));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_ENUM | PrimitiveProto::kValEnumFieldNumber, 2));
+
+ PrimitiveProto primitives;
+ ASSERT_TRUE(primitives.ParseFromString(flushToString(&proto)));
+ EXPECT_EQ(primitives.val_int32(), 123);
+ EXPECT_EQ(primitives.val_int64(), -1);
+ EXPECT_EQ(primitives.val_float(), -23.5f);
+ EXPECT_EQ(primitives.val_double(), 324.5f);
+ EXPECT_EQ(primitives.val_uint32(), 3424);
+ EXPECT_EQ(primitives.val_uint64(), 57);
+ EXPECT_EQ(primitives.val_fixed32(), -20);
+ EXPECT_EQ(primitives.val_fixed64(), -37);
+ EXPECT_EQ(primitives.val_bool(), true);
+ EXPECT_THAT(primitives.val_string(), StrEq(s.c_str()));
+ EXPECT_THAT(primitives.val_bytes(), StrEq("apple"));
+ EXPECT_EQ(primitives.val_sfixed32(), 63);
+ EXPECT_EQ(primitives.val_sfixed64(), -54);
+ EXPECT_EQ(primitives.val_sint32(), -533);
+ EXPECT_EQ(primitives.val_sint64(), -61224762453LL);
+ EXPECT_EQ(primitives.val_enum(), PrimitiveProto_Count_TWO);
+}
+
+TEST(ProtoOutputStreamTest, Complex) {
+ std::string name1 = "cat";
+ std::string name2 = "dog";
+ const char data1[6] = { 'f', 'u', 'n', 'n', 'y', '!' };
+ const char data2[4] = { 'f', 'o', 'o', 'd' };
+
+ ProtoOutputStream proto;
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 23));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 101));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, -72));
+ uint64_t token1 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 12));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name1));
+ // specify the length to test the write(id, bytes, length) function.
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data1, 5));
+ proto.end(token1);
+ uint64_t token2 = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 98));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_STRING | ComplexProto::Log::kNameFieldNumber, name2));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_BYTES | ComplexProto::Log::kDataFieldNumber, data2, 4));
+ proto.end(token2);
+
+ ComplexProto complex;
+ ASSERT_TRUE(complex.ParseFromString(iterateToString(&proto)));
+ EXPECT_EQ(complex.ints_size(), 3);
+ EXPECT_EQ(complex.ints(0), 23);
+ EXPECT_EQ(complex.ints(1), 101);
+ EXPECT_EQ(complex.ints(2), -72);
+ EXPECT_EQ(complex.logs_size(), 2);
+ ComplexProto::Log log1 = complex.logs(0);
+ EXPECT_EQ(log1.id(), 12);
+ EXPECT_THAT(log1.name(), StrEq(name1.c_str()));
+ EXPECT_THAT(log1.data(), StrEq("funny")); // should not contain '!'
+ ComplexProto::Log log2 = complex.logs(1);
+ EXPECT_EQ(log2.id(), 98);
+ EXPECT_THAT(log2.name(), StrEq(name2.c_str()));
+ EXPECT_THAT(log2.data(), StrEq("food"));
+}
+
+TEST(ProtoOutputStreamTest, Reusability) {
+ ProtoOutputStream proto;
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 32));
+ EXPECT_TRUE(proto.write(FIELD_TYPE_INT32 | ComplexProto::kIntsFieldNumber, 15));
+ EXPECT_EQ(proto.bytesWritten(), 4);
+ EXPECT_EQ(proto.size(), 4);
+ proto.clear();
+ EXPECT_EQ(proto.bytesWritten(), 0);
+ EXPECT_EQ(proto.size(), 0);
+}
+
+TEST(ProtoOutputStreamTest, AdvancedEncoding) {
+ ProtoOutputStream proto;
+ proto.writeRawVarint(ComplexProto::kIntsFieldNumber << FIELD_ID_SHIFT);
+ proto.writeRawVarint(UINT64_C(-123809234));
+ proto.writeLengthDelimitedHeader(ComplexProto::kLogsFieldNumber, 8);
+ proto.writeRawByte((ComplexProto::Log::kDataFieldNumber << FIELD_ID_SHIFT) + 2);
+ proto.writeRawByte(6);
+ proto.writeRawByte('b');
+ proto.writeRawByte('a');
+ proto.writeRawByte('n');
+ proto.writeRawByte('a');
+ proto.writeRawByte('n');
+ proto.writeRawByte('a');
+ uint64_t token = proto.start(FIELD_TYPE_MESSAGE | ComplexProto::kLogsFieldNumber);
+ proto.write(FIELD_TYPE_INT32 | ComplexProto::Log::kIdFieldNumber, 14);
+ proto.end(token);
+
+ ComplexProto complex;
+ ASSERT_TRUE(complex.ParseFromString(flushToString(&proto)));
+ EXPECT_EQ(complex.ints_size(), 1);
+ EXPECT_EQ(complex.ints(0), UINT64_C(-123809234));
+ EXPECT_EQ(complex.logs_size(), 2);
+ ComplexProto::Log log1 = complex.logs(0);
+ EXPECT_FALSE(log1.has_id());
+ EXPECT_FALSE(log1.has_name());
+ EXPECT_THAT(log1.data(), StrEq("banana"));
+ ComplexProto::Log log2 = complex.logs(1);
+ EXPECT_EQ(log2.id(), 14);
+ EXPECT_FALSE(log2.has_name());
+ EXPECT_FALSE(log2.has_data());
+}
diff --git a/libs/protoutil/tests/protobuf_test.cpp b/libs/protoutil/tests/protobuf_test.cpp
new file mode 100644
index 0000000..5ca3e64
--- /dev/null
+++ b/libs/protoutil/tests/protobuf_test.cpp
@@ -0,0 +1,51 @@
+// 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.
+#include <android/util/protobuf.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace android::util;
+
+TEST(ProtobufTest, All) {
+ EXPECT_EQ(read_wire_type(UINT32_C(17)), 1);
+ EXPECT_EQ(read_field_id(UINT32_C(17)), 2);
+ EXPECT_EQ(get_varint_size(UINT64_C(234134)), 3);
+ EXPECT_EQ(get_varint_size(UINT64_C(-1)), 10);
+
+ constexpr uint8_t UNSET_BYTE = 0xAB;
+
+ uint8_t buf[11];
+ memset(buf, UNSET_BYTE, sizeof(buf));
+ EXPECT_EQ(write_raw_varint(buf, UINT64_C(150)) - buf, 2);
+ EXPECT_EQ(buf[0], 0x96);
+ EXPECT_EQ(buf[1], 0x01);
+ EXPECT_EQ(buf[2], UNSET_BYTE);
+
+ memset(buf, UNSET_BYTE, sizeof(buf));
+ EXPECT_EQ(write_raw_varint(buf, UINT64_C(-2)) - buf, 10);
+ EXPECT_EQ(buf[0], 0xfe);
+ for (int i = 1; i < 9; i++) {
+ EXPECT_EQ(buf[i], 0xff);
+ }
+ EXPECT_EQ(buf[9], 0x01);
+ EXPECT_EQ(buf[10], UNSET_BYTE);
+
+ uint8_t header[20];
+ memset(header, UNSET_BYTE, sizeof(header));
+ EXPECT_EQ(write_length_delimited_tag_header(header, 3, 150) - header, 3);
+ EXPECT_EQ(header[0], 26);
+ EXPECT_EQ(header[1], 0x96);
+ EXPECT_EQ(header[2], 0x01);
+ EXPECT_EQ(header[3], UNSET_BYTE);
+}
\ No newline at end of file
diff --git a/libs/protoutil/tests/test.proto b/libs/protoutil/tests/test.proto
new file mode 100644
index 0000000..52c55f3
--- /dev/null
+++ b/libs/protoutil/tests/test.proto
@@ -0,0 +1,42 @@
+// This proto file is only used for testing purpose.
+syntax = "proto2";
+
+package android.util;
+
+message PrimitiveProto {
+
+ optional int32 val_int32 = 1;
+ optional int64 val_int64 = 2;
+ optional float val_float = 3;
+ optional double val_double = 4;
+ optional uint32 val_uint32 = 5;
+ optional uint64 val_uint64 = 6;
+ optional fixed32 val_fixed32 = 7;
+ optional fixed64 val_fixed64 = 8;
+ optional bool val_bool = 9;
+ optional string val_string = 10;
+ optional bytes val_bytes = 11;
+ optional sfixed32 val_sfixed32 = 12;
+ optional sfixed64 val_sfixed64 = 13;
+ optional sint32 val_sint32 = 14;
+ optional sint64 val_sint64 = 15;
+
+ enum Count {
+ ZERO = 0;
+ ONE = 1;
+ TWO = 2;
+ };
+ optional Count val_enum = 16;
+}
+
+message ComplexProto {
+
+ repeated int32 ints = 1;
+
+ message Log {
+ optional int32 id = 1;
+ optional string name = 2;
+ optional bytes data = 3;
+ }
+ repeated Log logs = 2;
+}