mirror of
https://github.com/abseil/abseil-cpp.git
synced 2026-06-04 12:07:05 +08:00
381 lines
14 KiB
C++
381 lines
14 KiB
C++
// Copyright 2026 The Abseil Authors
|
|
//
|
|
// 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 "absl/status/status_builder.h"
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include "absl/base/config.h"
|
|
#include "absl/log/log.h"
|
|
#include "absl/log/log_entry.h"
|
|
#include "absl/log/log_sink.h"
|
|
#include "absl/status/status.h"
|
|
#include "absl/status/status_matchers.h"
|
|
#include "absl/status/statusor.h"
|
|
#include "absl/strings/string_view.h"
|
|
#include "absl/types/source_location.h"
|
|
|
|
namespace absl {
|
|
ABSL_NAMESPACE_BEGIN
|
|
namespace {
|
|
|
|
using ::absl_testing::StatusIs;
|
|
using ::testing::AllOf;
|
|
using ::testing::AnyOf;
|
|
using ::testing::ElementsAre;
|
|
using ::testing::Eq;
|
|
using ::testing::IsEmpty;
|
|
using ::testing::Pointee;
|
|
using ::testing::Property;
|
|
|
|
// Converts a StatusBuilder to a Status.
|
|
absl::Status ToStatus(const StatusBuilder& s) { return s; }
|
|
|
|
// Converts a StatusBuilder to a StatusOr<T>.
|
|
template <typename T>
|
|
absl::StatusOr<T> ToStatusOr(const StatusBuilder& s) {
|
|
return s;
|
|
}
|
|
|
|
void CheckSourceLocation(
|
|
const absl::Status& status, std::vector<int> lines = {},
|
|
absl::SourceLocation loc = absl::SourceLocation::current()) {
|
|
ASSERT_EQ(status.GetSourceLocations().size(), lines.size())
|
|
<< "Size check failed at " << loc.line();
|
|
for (size_t i = 0; i < lines.size(); ++i) {
|
|
EXPECT_EQ(absl::string_view(status.GetSourceLocations()[i].file_name()),
|
|
absl::string_view(loc.file_name()))
|
|
<< "File name check failed at " << loc.line();
|
|
EXPECT_EQ(status.GetSourceLocations()[i].line(), lines[i])
|
|
<< "Line check failed at " << loc.line();
|
|
}
|
|
}
|
|
|
|
class StatusBuilderTest : public ::testing::Test {};
|
|
|
|
TEST_F(StatusBuilderTest, Size) {
|
|
EXPECT_LE(sizeof(StatusBuilder), 40)
|
|
<< "Relax this test with caution and thorough testing. If StatusBuilder "
|
|
"is too large it can potentially blow stacks, especially in debug "
|
|
"builds. See the comments for StatusBuilder::Rep.";
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, ExplicitSourceLocation) {
|
|
const absl::SourceLocation kLocation = absl::SourceLocation::current();
|
|
|
|
{
|
|
const StatusBuilder builder(absl::OkStatus(), kLocation);
|
|
EXPECT_THAT(builder.source_location().file_name(),
|
|
Eq(kLocation.file_name()));
|
|
EXPECT_THAT(builder.source_location().line(), Eq(kLocation.line()));
|
|
}
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, ImplicitSourceLocation) {
|
|
const StatusBuilder builder(absl::OkStatus());
|
|
auto loc = absl::SourceLocation::current();
|
|
EXPECT_THAT(builder.source_location().file_name(),
|
|
AnyOf(Eq(absl::string_view(loc.file_name())),
|
|
Eq(absl::string_view("<source_location>"))));
|
|
EXPECT_THAT(builder.source_location().line(),
|
|
AnyOf(Eq(1), Eq(loc.line() - 1)));
|
|
}
|
|
|
|
testing::Matcher<absl::SourceLocation> SourceLocationIs(
|
|
absl::SourceLocation loc) {
|
|
return AnyOf(
|
|
AllOf(Property(&absl::SourceLocation::file_name, Eq(loc.file_name())),
|
|
Property(&absl::SourceLocation::line, Eq(loc.line()))),
|
|
// Fallback for platforms that don't support source locations.
|
|
AllOf(Property(&absl::SourceLocation::file_name, Eq("<source_location>")),
|
|
Property(&absl::SourceLocation::line, Eq(1))));
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, GetPreviousSourceLocations) {
|
|
const absl::SourceLocation loc0 = absl::SourceLocation::current();
|
|
absl::Status status = absl::InvalidArgumentError("hi", loc0);
|
|
const absl::SourceLocation loc1 = absl::SourceLocation::current();
|
|
status.AddSourceLocation(loc1);
|
|
const absl::SourceLocation loc2 = absl::SourceLocation::current();
|
|
status.AddSourceLocation(loc2);
|
|
|
|
// The builder's location is not included.
|
|
const StatusBuilder builder(status);
|
|
EXPECT_THAT(builder.GetPreviousSourceLocations(),
|
|
ElementsAre(SourceLocationIs(loc0), SourceLocationIs(loc1),
|
|
SourceLocationIs(loc2)));
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, EmptyGetPreviousSourceLocationsForNewFromStatusCode) {
|
|
const StatusBuilder builder(absl::StatusCode::kInvalidArgument);
|
|
EXPECT_THAT(builder.GetPreviousSourceLocations(), IsEmpty());
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, StatusCode) {
|
|
// OK
|
|
{
|
|
const StatusBuilder builder(absl::StatusCode::kOk);
|
|
EXPECT_TRUE(builder.ok());
|
|
EXPECT_THAT(builder.code(), Eq(absl::StatusCode::kOk));
|
|
}
|
|
// Non-OK code
|
|
{
|
|
const StatusBuilder builder(absl::StatusCode::kInvalidArgument);
|
|
EXPECT_FALSE(builder.ok());
|
|
EXPECT_THAT(builder.code(), Eq(absl::StatusCode::kInvalidArgument));
|
|
}
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, OkIgnoresStuff) {
|
|
EXPECT_THAT(ToStatus(StatusBuilder(absl::OkStatus(), absl::SourceLocation())
|
|
<< "booyah"),
|
|
Eq(absl::OkStatus()));
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, Streaming) {
|
|
EXPECT_THAT(
|
|
ToStatus(StatusBuilder(absl::CancelledError(), absl::SourceLocation())
|
|
<< "booyah"),
|
|
Eq(absl::CancelledError("booyah")));
|
|
EXPECT_THAT(
|
|
ToStatus(
|
|
StatusBuilder(absl::AbortedError("hello"), absl::SourceLocation())
|
|
<< "world"),
|
|
Eq(absl::AbortedError("hello; world")));
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, PrependLvalue) {
|
|
{
|
|
StatusBuilder builder(absl::CancelledError(), absl::SourceLocation());
|
|
EXPECT_THAT(ToStatus(builder.SetPrepend() << "booyah"),
|
|
Eq(absl::CancelledError("booyah")));
|
|
}
|
|
{
|
|
StatusBuilder builder(absl::AbortedError(" hello"), absl::SourceLocation());
|
|
EXPECT_THAT(ToStatus(builder.SetPrepend() << "world"),
|
|
Eq(absl::AbortedError("world hello")));
|
|
}
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, PrependRvalue) {
|
|
EXPECT_THAT(
|
|
ToStatus(StatusBuilder(absl::CancelledError(), absl::SourceLocation())
|
|
.SetPrepend()
|
|
<< "booyah"),
|
|
Eq(absl::CancelledError("booyah")));
|
|
EXPECT_THAT(ToStatus(StatusBuilder(absl::AbortedError(" hello"),
|
|
absl::SourceLocation())
|
|
.SetPrepend()
|
|
<< "world"),
|
|
Eq(absl::AbortedError("world hello")));
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, AppendLvalue) {
|
|
{
|
|
StatusBuilder builder(absl::CancelledError(), absl::SourceLocation());
|
|
EXPECT_THAT(ToStatus(builder.SetAppend() << "booyah"),
|
|
Eq(absl::CancelledError("booyah")));
|
|
}
|
|
{
|
|
StatusBuilder builder(absl::AbortedError("hello"), absl::SourceLocation());
|
|
EXPECT_THAT(ToStatus(builder.SetAppend() << " world"),
|
|
Eq(absl::AbortedError("hello world")));
|
|
}
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, AppendRvalue) {
|
|
EXPECT_THAT(
|
|
ToStatus(StatusBuilder(absl::CancelledError(), absl::SourceLocation())
|
|
.SetAppend()
|
|
<< "booyah"),
|
|
Eq(absl::CancelledError("booyah")));
|
|
EXPECT_THAT(ToStatus(StatusBuilder(absl::AbortedError("hello"),
|
|
absl::SourceLocation())
|
|
.SetAppend()
|
|
<< " world"),
|
|
Eq(absl::AbortedError("hello world")));
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, WithRvalueRef) {
|
|
auto policy = [](StatusBuilder sb) { return sb << "policy"; };
|
|
EXPECT_THAT(ToStatus(StatusBuilder(absl::AbortedError("hello"),
|
|
absl::SourceLocation())
|
|
.With(policy)),
|
|
Eq(absl::AbortedError("hello; policy")));
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, WithRef) {
|
|
auto policy = [](StatusBuilder sb) { return sb << "policy"; };
|
|
StatusBuilder sb(absl::AbortedError("zomg"), absl::SourceLocation());
|
|
EXPECT_THAT(ToStatus(sb.With(policy)),
|
|
Eq(absl::AbortedError("zomg; policy")));
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, WithTypeChange) {
|
|
auto policy = [](StatusBuilder sb) -> std::string {
|
|
return sb.ok() ? "true" : "false";
|
|
};
|
|
EXPECT_EQ(StatusBuilder(absl::CancelledError(), absl::SourceLocation())
|
|
.With(policy),
|
|
"false");
|
|
EXPECT_EQ(
|
|
StatusBuilder(absl::OkStatus(), absl::SourceLocation()).With(policy),
|
|
"true");
|
|
}
|
|
|
|
struct MoveOnlyAdaptor {
|
|
std::unique_ptr<int> value;
|
|
std::unique_ptr<int> operator()(const absl::Status&) && {
|
|
return std::move(value);
|
|
}
|
|
};
|
|
|
|
TEST_F(StatusBuilderTest, WithMoveOnlyAdaptor) {
|
|
StatusBuilder sb(absl::AbortedError("zomg"), absl::SourceLocation());
|
|
EXPECT_THAT(sb.With(MoveOnlyAdaptor{std::make_unique<int>(100)}),
|
|
Pointee(100));
|
|
EXPECT_THAT(StatusBuilder(absl::AbortedError("zomg"), absl::SourceLocation())
|
|
.With(MoveOnlyAdaptor{std::make_unique<int>(100)}),
|
|
Pointee(100));
|
|
}
|
|
|
|
struct StringifiableType {
|
|
absl::string_view message;
|
|
|
|
template <typename Sink>
|
|
friend void AbslStringify(Sink& sink, const StringifiableType& o) {
|
|
sink.Append(o.message);
|
|
}
|
|
};
|
|
|
|
class MockLogSink : public absl::LogSink {
|
|
public:
|
|
MOCK_METHOD(void, Send, (const absl::LogEntry&), (override));
|
|
};
|
|
|
|
TEST(WithExtraMessagePolicyTest, AppendsToExtraMessage) {
|
|
// The policy simply calls operator<< on the builder; the following examples
|
|
// demonstrate that, without duplicating all of the above tests.
|
|
EXPECT_THAT(ToStatus(StatusBuilder(absl::AbortedError("hello"),
|
|
absl::SourceLocation())
|
|
.With(ExtraMessage("world"))),
|
|
Eq(absl::AbortedError("hello; world")));
|
|
EXPECT_THAT(ToStatus(StatusBuilder(absl::AbortedError("hello"),
|
|
absl::SourceLocation())
|
|
.With(ExtraMessage() << "world")),
|
|
Eq(absl::AbortedError("hello; world")));
|
|
EXPECT_THAT(ToStatus(StatusBuilder(absl::AbortedError("hello"),
|
|
absl::SourceLocation())
|
|
.With(ExtraMessage("world"))
|
|
.With(ExtraMessage("!"))),
|
|
Eq(absl::AbortedError("hello; world!")));
|
|
EXPECT_THAT(ToStatus(StatusBuilder(absl::AbortedError("hello"),
|
|
absl::SourceLocation())
|
|
.With(ExtraMessage("world, "))
|
|
.SetPrepend()),
|
|
Eq(absl::AbortedError("world, hello")));
|
|
EXPECT_THAT(ToStatus(StatusBuilder(absl::AbortedError("hello"),
|
|
absl::SourceLocation())
|
|
.With(ExtraMessage() << StringifiableType{"world"})),
|
|
Eq(absl::AbortedError("hello; world")));
|
|
|
|
// The above examples use temporary StatusBuilder rvalues; verify things also
|
|
// work fine when StatusBuilder is an lvalue.
|
|
StatusBuilder builder(absl::AbortedError("hello"), absl::SourceLocation());
|
|
EXPECT_THAT(
|
|
ToStatus(builder.With(ExtraMessage("world")).With(ExtraMessage("!"))),
|
|
Eq(absl::AbortedError("hello; world!")));
|
|
}
|
|
|
|
TEST(WithExtraMessagePolicyTest,
|
|
ExtraMessageStreamOperatorPreservesRvalueness) {
|
|
static_assert(
|
|
std::is_same_v<ExtraMessage&&, decltype(ExtraMessage() << "foo")>);
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, StatusSourceLocationChaining) {
|
|
{
|
|
absl::Status src = absl::OkStatus();
|
|
CheckSourceLocation(src);
|
|
CheckSourceLocation(ToStatus(StatusBuilder(src, absl::SourceLocation())));
|
|
CheckSourceLocation(
|
|
ToStatus(StatusBuilder(src, absl::SourceLocation::current())));
|
|
CheckSourceLocation(
|
|
ToStatus(StatusBuilder(src, absl::SourceLocation::current()) << "hmm"));
|
|
}
|
|
{
|
|
absl::Status src = absl::Status(absl::StatusCode::kCancelled, "");
|
|
CheckSourceLocation(src);
|
|
CheckSourceLocation(ToStatus(StatusBuilder(src, absl::SourceLocation())));
|
|
CheckSourceLocation(
|
|
ToStatus(StatusBuilder(src, absl::SourceLocation::current())));
|
|
CheckSourceLocation(
|
|
ToStatus(StatusBuilder(src, absl::SourceLocation::current()) << ""));
|
|
CheckSourceLocation(
|
|
ToStatus(StatusBuilder(src, absl::SourceLocation::current()) << "hmm"),
|
|
{__builtin_LINE() - 1});
|
|
}
|
|
{
|
|
absl::Status src = absl::Status(absl::StatusCode::kCancelled, "msg",
|
|
absl::SourceLocation());
|
|
CheckSourceLocation(src);
|
|
CheckSourceLocation(ToStatus(StatusBuilder(src, absl::SourceLocation())));
|
|
CheckSourceLocation(
|
|
ToStatus(StatusBuilder(src, absl::SourceLocation::current())),
|
|
{__builtin_LINE() - 1});
|
|
CheckSourceLocation(
|
|
ToStatus(StatusBuilder(src, absl::SourceLocation::current()) << "hmm"),
|
|
{__builtin_LINE() - 1});
|
|
}
|
|
{
|
|
absl::Status src = absl::Status(absl::StatusCode::kCancelled, "msg");
|
|
int src_line = __builtin_LINE() - 1;
|
|
CheckSourceLocation(src, {src_line});
|
|
CheckSourceLocation(ToStatus(StatusBuilder(src, absl::SourceLocation())),
|
|
{src_line});
|
|
CheckSourceLocation(
|
|
ToStatus(StatusBuilder(src, absl::SourceLocation::current())),
|
|
{src_line, __builtin_LINE() - 1});
|
|
CheckSourceLocation(
|
|
ToStatus(StatusBuilder(src, absl::SourceLocation::current()) << "hmm"),
|
|
{src_line, __builtin_LINE() - 1});
|
|
}
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, SetErrorCode) {
|
|
StatusBuilder builder;
|
|
builder.SetCode(absl::StatusCode::kResourceExhausted);
|
|
LOG(INFO) << "Builder code: " << builder;
|
|
EXPECT_FALSE(builder.ok());
|
|
EXPECT_EQ(builder.code(), absl::StatusCode::kResourceExhausted);
|
|
}
|
|
|
|
TEST_F(StatusBuilderTest, BuilderToStatusOrStatusShouldGiveErrorStatusOr) {
|
|
absl::StatusOr<absl::Status> value = StatusBuilder(absl::CancelledError());
|
|
ASSERT_FALSE(value.ok());
|
|
EXPECT_THAT(value.status(), StatusIs(absl::StatusCode::kCancelled));
|
|
}
|
|
|
|
} // namespace
|
|
ABSL_NAMESPACE_END
|
|
} // namespace absl
|