// 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 #include #include #include #include #include #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. template absl::StatusOr ToStatusOr(const StatusBuilder& s) { return s; } void CheckSourceLocation( const absl::Status& status, std::vector 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("")))); EXPECT_THAT(builder.source_location().line(), AnyOf(Eq(1), Eq(loc.line() - 1))); } testing::Matcher 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("")), 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 value; std::unique_ptr 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(100)}), Pointee(100)); EXPECT_THAT(StatusBuilder(absl::AbortedError("zomg"), absl::SourceLocation()) .With(MoveOnlyAdaptor{std::make_unique(100)}), Pointee(100)); } struct StringifiableType { absl::string_view message; template 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); } 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 value = StatusBuilder(absl::CancelledError()); ASSERT_FALSE(value.ok()); EXPECT_THAT(value.status(), StatusIs(absl::StatusCode::kCancelled)); } } // namespace ABSL_NAMESPACE_END } // namespace absl