| // Copyright 2026 Google Inc. All Rights Reserved. |
| // |
| // 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 "proto.h" |
| #include "proto_reader.h" |
| #include "input_stream.h" |
| #include "output_stream.h" |
| #include "test.h" |
| |
| #include <string> |
| #include <vector> |
| #include <limits> |
| |
| TEST(ProtoTest, Varint32RoundTrip) { |
| std::vector<uint32_t> values = {0, 1, 127, 128, 300, 0x7FFFFFFF, 0xFFFFFFFF}; |
| for (uint32_t val : values) { |
| StringOutputStream out; |
| WriteVarint32NoTag(&out, val); |
| |
| uint32_t read_val; |
| const std::string& buf = out.str(); |
| MemoryInputStream in(buf.data(), buf.size()); |
| EXPECT_TRUE(ReadVarint32(in, &read_val)); |
| EXPECT_EQ(val, read_val); |
| } |
| } |
| |
| TEST(ProtoTest, Varint64RoundTrip) { |
| std::vector<uint64_t> values = {0, 1, 127, 128, 300, 0x7FFFFFFF, 0xFFFFFFFF, |
| 0x100000000ULL, 0x7FFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL}; |
| for (uint64_t val : values) { |
| StringOutputStream out; |
| WriteVarint64(&out, 1, val); |
| |
| uint32_t tag; |
| int field_number, wire_type; |
| uint64_t read_val; |
| const std::string& buf = out.str(); |
| MemoryInputStream in(buf.data(), buf.size()); |
| EXPECT_TRUE(ReadTag(in, &tag, &field_number, &wire_type)); |
| EXPECT_EQ(1, field_number); |
| EXPECT_EQ(0, wire_type); |
| EXPECT_TRUE(ReadVarint64(in, &read_val)); |
| EXPECT_EQ(val, read_val); |
| } |
| } |
| |
| TEST(ProtoTest, Fixed32RoundTrip) { |
| std::vector<uint32_t> values = {0, 1, 0x12345678, 0xFFFFFFFF}; |
| for (uint32_t val : values) { |
| StringOutputStream out; |
| WriteFixed32NoTag(&out, val); |
| |
| uint32_t read_val; |
| const std::string& buf = out.str(); |
| MemoryInputStream in(buf.data(), buf.size()); |
| EXPECT_TRUE(ReadFixed32(in, &read_val)); |
| EXPECT_EQ(val, read_val); |
| } |
| } |
| |
| TEST(ProtoTest, Fixed64RoundTrip) { |
| std::vector<uint64_t> values = {0, 1, 0x123456789ABCDEF0ULL, 0xFFFFFFFFFFFFFFFFULL}; |
| for (uint64_t val : values) { |
| StringOutputStream out; |
| WriteFixed64NoTag(&out, val); |
| |
| uint64_t read_val; |
| const std::string& buf = out.str(); |
| MemoryInputStream in(buf.data(), buf.size()); |
| EXPECT_TRUE(ReadFixed64(in, &read_val)); |
| EXPECT_EQ(val, read_val); |
| } |
| } |
| |
| TEST(ProtoTest, StringRoundTrip) { |
| std::vector<std::string> values = {"", "a", "hello", std::string(300, 'x')}; |
| for (const auto& val : values) { |
| StringOutputStream out; |
| WriteString(&out, 1, val); |
| |
| uint32_t tag; |
| int field_number, wire_type; |
| std::string read_val; |
| const std::string& buf = out.str(); |
| MemoryInputStream in(buf.data(), buf.size()); |
| EXPECT_TRUE(ReadTag(in, &tag, &field_number, &wire_type)); |
| EXPECT_EQ(1, field_number); |
| EXPECT_EQ(2, wire_type); |
| EXPECT_TRUE(ReadString(in, &read_val)); |
| EXPECT_EQ(val, read_val); |
| } |
| } |
| |
| TEST(ProtoTest, ZigZag32RoundTrip) { |
| std::vector<int32_t> values = {0, 1, -1, 127, -128, 0x7FFFFFFF, (int32_t)0x80000000}; |
| for (int32_t val : values) { |
| uint32_t encoded = ZigZagEncode32(val); |
| int32_t decoded = ZigZagDecode32(encoded); |
| EXPECT_EQ(val, decoded); |
| } |
| } |
| |
| TEST(ProtoTest, ZigZag64RoundTrip) { |
| std::vector<int64_t> values = {0, 1, -1, 127, -128, 0x7FFFFFFFFFFFFFFFULL, (int64_t)0x8000000000000000ULL}; |
| for (int64_t val : values) { |
| uint64_t encoded = ZigZagEncode64(val); |
| int64_t decoded = ZigZagDecode64(encoded); |
| EXPECT_EQ(val, decoded); |
| } |
| } |
| |
| TEST(ProtoTest, LargeFieldNumbers) { |
| std::vector<int> field_numbers = {1, 15, 16, 31, 32, 1000, 0x1FFFFFFF}; |
| for (int fn : field_numbers) { |
| StringOutputStream out; |
| WriteVarint32(&out, fn, 42); |
| |
| uint32_t tag; |
| int read_fn, wire_type; |
| uint32_t read_val; |
| const std::string& buf = out.str(); |
| MemoryInputStream in(buf.data(), buf.size()); |
| EXPECT_TRUE(ReadTag(in, &tag, &read_fn, &wire_type)); |
| EXPECT_EQ(fn, read_fn); |
| EXPECT_EQ(0, wire_type); |
| EXPECT_TRUE(ReadVarint32(in, &read_val)); |
| EXPECT_EQ(42, read_val); |
| } |
| } |
| |
| TEST(ProtoTest, LargeVarints) { |
| // Specifically test values with bit 31 set to catch signedness/truncation issues. |
| std::vector<uint64_t> values = { |
| 0x80000000ULL, // bit 31 set |
| 0xFFFFFFFFULL, // all 32 bits set |
| 0x100000000ULL, // bit 32 set |
| 0x7FFFFFFFFFFFFFFFULL, // max int64 |
| 0xFFFFFFFFFFFFFFFFULL // max uint64 |
| }; |
| |
| for (uint64_t val : values) { |
| StringOutputStream out; |
| WriteVarint64(&out, 1, val); |
| |
| uint64_t read_val; |
| const std::string& buf = out.str(); |
| MemoryInputStream in(buf.data(), buf.size()); |
| uint32_t tag; |
| int fn, wt; |
| ASSERT_TRUE(ReadTag(in, &tag, &fn, &wt)); |
| EXPECT_TRUE(ReadVarint64(in, &read_val)); |
| EXPECT_EQ(val, read_val); |
| } |
| |
| // Test 32-bit varint with bit 31 set |
| uint32_t val32 = 0x80000000U; |
| StringOutputStream out; |
| WriteVarint32NoTag(&out, val32); |
| uint32_t read_val32; |
| MemoryInputStream in(out.str().data(), out.str().size()); |
| EXPECT_TRUE(ReadVarint32(in, &read_val32)); |
| EXPECT_EQ(val32, read_val32); |
| } |