blob: 640d730f6ecffe29a15a14aabe6d878cf15d0681 [file]
// Copyright 2026 Google LLC
//
// 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
//
// https://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 "utils/riegeli.h"
#include <cstdint>
#include <limits>
#include <string>
#include <type_traits>
#include "absl/functional/function_ref.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/status_matchers.h"
#include "absl/status/statusor.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "riegeli/bytes/string_reader.h"
#include "riegeli/bytes/string_writer.h"
#include "testing/test_string_utils.h"
namespace credentio {
namespace {
using ::absl_testing::IsOkAndHolds;
using ::absl_testing::StatusIs;
using ::testing::HasSubstr;
template <typename T>
using CreateStringFunction = absl::FunctionRef<std::string(T)>;
template <typename T>
absl::StatusOr<CreateStringFunction<T>> GetCreateStringFunction() {
if (!std::is_unsigned_v<T>) {
return absl::InvalidArgumentError("Type must be unsigned");
}
switch (sizeof(T)) {
case 1:
return credentio_testing::Uint8Str;
case 2:
return credentio_testing::Uint16Str;
case 4:
return credentio_testing::Uint32Str;
case 8:
return credentio_testing::Uint64Str;
}
return absl::InvalidArgumentError("Unsupported type size");
}
template <typename T>
using CopyBigEndianTests = testing::Test;
TYPED_TEST_SUITE_P(CopyBigEndianTests);
TYPED_TEST_P(CopyBigEndianTests, FailsWhenSourceClosed) {
std::string source_str = "";
riegeli::StringReader<> source_reader(source_str);
std::string result;
riegeli::StringWriter<> destination(&result);
source_reader.Close();
EXPECT_THAT(CopyBigEndian<TypeParam>(source_reader, destination, 0),
StatusIs(absl::StatusCode::kFailedPrecondition,
HasSubstr("Object closed")));
destination.Close();
EXPECT_EQ(result, "");
}
TYPED_TEST_P(CopyBigEndianTests, FailsWhenDestinationClosed) {
auto create_string = GetCreateStringFunction<TypeParam>();
ASSERT_TRUE(create_string.ok());
std::string source_str = (*create_string)(10);
riegeli::StringReader<> source_reader(source_str);
std::string result;
riegeli::StringWriter<> destination(&result);
destination.Close();
EXPECT_THAT(CopyBigEndian<TypeParam>(source_reader, destination, 0),
StatusIs(absl::StatusCode::kFailedPrecondition,
HasSubstr("Object closed")));
}
TYPED_TEST_P(CopyBigEndianTests, FailsWhenNoSource) {
std::string source_str = "";
riegeli::StringReader<> source_reader(source_str);
std::string result;
riegeli::StringWriter<> destination(&result);
EXPECT_THAT(
CopyBigEndian<TypeParam>(source_reader, destination, 0),
StatusIs(absl::StatusCode::kDataLoss, HasSubstr("Failed to read")));
destination.Close();
EXPECT_EQ(result, "");
}
TYPED_TEST_P(CopyBigEndianTests, FailsWhenTooLarge) {
auto create_string = GetCreateStringFunction<TypeParam>();
ASSERT_TRUE(create_string.ok());
std::string source_str =
(*create_string)(std::numeric_limits<TypeParam>().max() - 10);
riegeli::StringReader<> source_reader(source_str);
std::string result;
riegeli::StringWriter<> destination(&result);
EXPECT_THAT(CopyBigEndian<TypeParam>(source_reader, destination,
std::numeric_limits<int64_t>().max()),
StatusIs(absl::StatusCode::kDataLoss,
HasSubstr("adjusted offset is too large")));
destination.Close();
EXPECT_EQ(result, "");
}
TYPED_TEST_P(CopyBigEndianTests, Succeeds) {
auto create_string = GetCreateStringFunction<TypeParam>();
ASSERT_TRUE(create_string.ok());
TypeParam source = 10;
std::string source_str = (*create_string)(source);
riegeli::StringReader<> source_reader(source_str);
int64_t adjustment = 10;
TypeParam expected = source + adjustment;
std::string expected_str = (*create_string)(expected);
std::string result;
riegeli::StringWriter<> destination(&result);
EXPECT_THAT(CopyBigEndian<TypeParam>(source_reader, destination, adjustment),
IsOkAndHolds(expected));
EXPECT_TRUE(destination.Close());
EXPECT_EQ(result, expected_str);
}
TYPED_TEST_P(CopyBigEndianTests, SucceedsWithZeroAdjustment) {
auto create_string = GetCreateStringFunction<TypeParam>();
ASSERT_TRUE(create_string.ok());
TypeParam source = 42;
std::string source_str = (*create_string)(source);
riegeli::StringReader<> source_reader(source_str);
std::string result;
riegeli::StringWriter<> destination(&result);
EXPECT_THAT(
credentio::CopyBigEndian<TypeParam>(source_reader, destination, 0),
IsOkAndHolds(source));
EXPECT_TRUE(destination.Close());
EXPECT_EQ(result, source_str);
}
TEST(ReadNullTerminatedStringTests, SucceedsWithNullTerminator) {
std::string source_str("hello\0world", 11);
riegeli::StringReader<> reader(source_str);
std::string output;
EXPECT_TRUE(ReadNullTerminatedString(reader, 10, output));
EXPECT_EQ(output, "hello");
EXPECT_EQ(reader.pos(), 6);
}
TEST(ReadNullTerminatedStringTests, SucceedsWithNullTerminatorAtStart) {
std::string source_str("\0helloworld", 11);
riegeli::StringReader<> reader(source_str);
std::string output;
EXPECT_TRUE(ReadNullTerminatedString(reader, 6, output));
EXPECT_EQ(output, "");
EXPECT_EQ(reader.pos(), 1);
}
TEST(ReadNullTerminatedStringTests, SucceedsWithMultipleNulls) {
std::string source_str("abc\0def\0ghi", 11);
riegeli::StringReader<> reader(source_str);
std::string output;
EXPECT_TRUE(ReadNullTerminatedString(reader, 8, output));
EXPECT_EQ(output, "abc");
EXPECT_EQ(reader.pos(), 4);
}
TEST(ReadNullTerminatedStringTests, FailsWithoutNullTerminatorWithinBounds) {
std::string source_str = "abcdefghij";
riegeli::StringReader<> reader(source_str);
std::string output;
EXPECT_FALSE(ReadNullTerminatedString(reader, 5, output));
EXPECT_EQ(output, "");
EXPECT_EQ(reader.pos(), 0);
}
TEST(ReadNullTerminatedStringTests, FailsIfInsufficientBytesToRead) {
std::string source_str = "abc";
riegeli::StringReader<> reader(source_str);
std::string output;
EXPECT_FALSE(ReadNullTerminatedString(reader, 5, output));
}
REGISTER_TYPED_TEST_SUITE_P(CopyBigEndianTests, FailsWhenSourceClosed,
FailsWhenDestinationClosed, FailsWhenNoSource,
FailsWhenTooLarge, Succeeds,
SucceedsWithZeroAdjustment);
using CopyBigEndianTypes =
::testing::Types<uint8_t, uint16_t, uint32_t, uint64_t>;
INSTANTIATE_TYPED_TEST_SUITE_P(_, CopyBigEndianTests, CopyBigEndianTypes);
} // namespace
} // namespace credentio