-- bffb14058bb46137d42c7a113a36b6b582997cda by Xiaoyi Zhang <zhangxy@google.com>: Add ABSL_MUST_USE_RESULT to Status. PiperOrigin-RevId: 296272498 -- b426fdd3b3f687d7a8aeb644925923bbab503778 by CJ Johnson <johnsoncj@google.com>: Optimizes absl::InlinedVector::clear() by not deallocating the data, if allocated. This allows allocations to be reused. This matches the behavior of std::vector::clear() PiperOrigin-RevId: 296197235 -- 8cb9fbfe20e749816065c1a042e84f72dac9bfc0 by CJ Johnson <johnsoncj@google.com>: Optimizes absl::InlinedVector::clear() by not deallocating the data, if allocated. This allows allocations to be reused. This matches the behavior of std::vector::clear() PiperOrigin-RevId: 296058092 -- 2558d3369a482879919155b6f46317ccafe0ca13 by Matthew Brown <matthewbr@google.com>: Internal cleanup PiperOrigin-RevId: 296025806 -- cf7ee57228534021c15ed7421df92acf6c27c9c7 by Gennadiy Rozental <rogeeff@google.com>: Make FlagOps enum class. We also add comments to all the functions used to invoke flag ops. PiperOrigin-RevId: 295975809 -- 74bbdbd12fbc54e9c4ebcb3005e727becf0e509d by Xiaoyi Zhang <zhangxy@google.com>: Release `absl::Status`. PiperOrigin-RevId: 295777662 -- 3dbc622b4e2227863525da2f7de7ecbeb3ede21f by Xiaoyi Zhang <zhangxy@google.com>: Internal change. PiperOrigin-RevId: 295733658 -- 48d74aa0ab01d611da6012b377f038d8b26c712e by Abseil Team <absl-team@google.com>: Fix typo in container/CMakeLists.txt for container_common PiperOrigin-RevId: 295491438 GitOrigin-RevId: bffb14058bb46137d42c7a113a36b6b582997cda Change-Id: Ia966857b07fa7412cd6489ac37b5fa26640e4141pull/629/head
parent
3c81410510
commit
914ff44510
13 changed files with 1556 additions and 43 deletions
@ -0,0 +1,65 @@ |
||||
# |
||||
# Copyright 2017 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. |
||||
|
||||
# This package contains `absl::Status`. |
||||
# It will expand later to have utilities around `Status` like `StatusOr`, |
||||
# `StatusBuilder` and macros. |
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") |
||||
load( |
||||
"//absl:copts/configure_copts.bzl", |
||||
"ABSL_DEFAULT_COPTS", |
||||
"ABSL_TEST_COPTS", |
||||
) |
||||
|
||||
package(default_visibility = ["//visibility:public"]) |
||||
|
||||
licenses(["notice"]) # Apache 2.0 |
||||
|
||||
cc_library( |
||||
name = "status", |
||||
srcs = [ |
||||
"status.cc", |
||||
"status_payload_printer.cc", |
||||
], |
||||
hdrs = [ |
||||
"status.h", |
||||
"status_payload_printer.h", |
||||
], |
||||
copts = ABSL_DEFAULT_COPTS, |
||||
deps = [ |
||||
"//absl/base:config", |
||||
"//absl/base:core_headers", |
||||
"//absl/base:raw_logging_internal", |
||||
"//absl/container:inlined_vector", |
||||
"//absl/debugging:stacktrace", |
||||
"//absl/debugging:symbolize", |
||||
"//absl/strings", |
||||
"//absl/strings:cord", |
||||
"//absl/strings:str_format", |
||||
"//absl/types:optional", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "status_test", |
||||
srcs = ["status_test.cc"], |
||||
copts = ABSL_TEST_COPTS, |
||||
deps = [ |
||||
":status", |
||||
"//absl/strings", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
@ -0,0 +1,52 @@ |
||||
# |
||||
# Copyright 2020 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. |
||||
# |
||||
absl_cc_library( |
||||
NAME |
||||
status |
||||
HDRS |
||||
"status.h" |
||||
SRCS |
||||
"status.cc" |
||||
"status_payload_printer.h" |
||||
"status_payload_printer.cc" |
||||
COPTS |
||||
${ABSL_DEFAULT_COPTS} |
||||
DEPS |
||||
absl::config |
||||
absl::core_headers |
||||
absl::raw_logging_internal |
||||
absl::inlined_vector |
||||
absl::stacktrace |
||||
absl::symbolize |
||||
absl::strings |
||||
absl::cord |
||||
absl::str_format |
||||
absl::optional |
||||
PUBLIC |
||||
) |
||||
|
||||
absl_cc_test( |
||||
NAME |
||||
status_test |
||||
HDRS |
||||
"status_test.cc" |
||||
COPTS |
||||
${ABSL_TEST_COPTS} |
||||
DEPS |
||||
absl::status |
||||
absl::strings |
||||
gmock_main |
||||
) |
@ -0,0 +1,439 @@ |
||||
// Copyright 2019 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.h" |
||||
|
||||
#include <cassert> |
||||
|
||||
#include "absl/base/internal/raw_logging.h" |
||||
#include "absl/debugging/stacktrace.h" |
||||
#include "absl/debugging/symbolize.h" |
||||
#include "absl/status/status_payload_printer.h" |
||||
#include "absl/strings/escaping.h" |
||||
#include "absl/strings/str_cat.h" |
||||
#include "absl/strings/str_format.h" |
||||
#include "absl/strings/str_split.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
// The implementation was intentionally kept same as util::error::Code_Name()
|
||||
// to ease the migration.
|
||||
std::string StatusCodeToString(StatusCode code) { |
||||
switch (code) { |
||||
case StatusCode::kOk: |
||||
return "OK"; |
||||
case StatusCode::kCancelled: |
||||
return "CANCELLED"; |
||||
case StatusCode::kUnknown: |
||||
return "UNKNOWN"; |
||||
case StatusCode::kInvalidArgument: |
||||
return "INVALID_ARGUMENT"; |
||||
case StatusCode::kDeadlineExceeded: |
||||
return "DEADLINE_EXCEEDED"; |
||||
case StatusCode::kNotFound: |
||||
return "NOT_FOUND"; |
||||
case StatusCode::kAlreadyExists: |
||||
return "ALREADY_EXISTS"; |
||||
case StatusCode::kPermissionDenied: |
||||
return "PERMISSION_DENIED"; |
||||
case StatusCode::kUnauthenticated: |
||||
return "UNAUTHENTICATED"; |
||||
case StatusCode::kResourceExhausted: |
||||
return "RESOURCE_EXHAUSTED"; |
||||
case StatusCode::kFailedPrecondition: |
||||
return "FAILED_PRECONDITION"; |
||||
case StatusCode::kAborted: |
||||
return "ABORTED"; |
||||
case StatusCode::kOutOfRange: |
||||
return "OUT_OF_RANGE"; |
||||
case StatusCode::kUnimplemented: |
||||
return "UNIMPLEMENTED"; |
||||
case StatusCode::kInternal: |
||||
return "INTERNAL"; |
||||
case StatusCode::kUnavailable: |
||||
return "UNAVAILABLE"; |
||||
case StatusCode::kDataLoss: |
||||
return "DATA_LOSS"; |
||||
default: |
||||
return ""; |
||||
} |
||||
} |
||||
|
||||
std::ostream& operator<<(std::ostream& os, StatusCode code) { |
||||
return os << StatusCodeToString(code); |
||||
} |
||||
|
||||
namespace status_internal { |
||||
|
||||
static int FindPayloadIndexByUrl(const Payloads* payloads, |
||||
absl::string_view type_url) { |
||||
if (payloads == nullptr) return -1; |
||||
|
||||
for (int i = 0; i < payloads->size(); ++i) { |
||||
if ((*payloads)[i].type_url == type_url) return i; |
||||
} |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
// Convert canonical code to a value known to this binary.
|
||||
absl::StatusCode MapToLocalCode(int value) { |
||||
absl::StatusCode code = static_cast<absl::StatusCode>(value); |
||||
switch (code) { |
||||
case absl::StatusCode::kOk: |
||||
case absl::StatusCode::kCancelled: |
||||
case absl::StatusCode::kUnknown: |
||||
case absl::StatusCode::kInvalidArgument: |
||||
case absl::StatusCode::kDeadlineExceeded: |
||||
case absl::StatusCode::kNotFound: |
||||
case absl::StatusCode::kAlreadyExists: |
||||
case absl::StatusCode::kPermissionDenied: |
||||
case absl::StatusCode::kResourceExhausted: |
||||
case absl::StatusCode::kFailedPrecondition: |
||||
case absl::StatusCode::kAborted: |
||||
case absl::StatusCode::kOutOfRange: |
||||
case absl::StatusCode::kUnimplemented: |
||||
case absl::StatusCode::kInternal: |
||||
case absl::StatusCode::kUnavailable: |
||||
case absl::StatusCode::kDataLoss: |
||||
case absl::StatusCode::kUnauthenticated: |
||||
return code; |
||||
default: |
||||
return absl::StatusCode::kUnknown; |
||||
} |
||||
} |
||||
} // namespace status_internal
|
||||
|
||||
absl::optional<absl::Cord> Status::GetPayload( |
||||
absl::string_view type_url) const { |
||||
const auto* payloads = GetPayloads(); |
||||
int index = status_internal::FindPayloadIndexByUrl(payloads, type_url); |
||||
if (index != -1) return (*payloads)[index].payload; |
||||
|
||||
return absl::nullopt; |
||||
} |
||||
|
||||
void Status::SetPayload(absl::string_view type_url, absl::Cord payload) { |
||||
if (ok()) return; |
||||
|
||||
PrepareToModify(); |
||||
|
||||
status_internal::StatusRep* rep = RepToPointer(rep_); |
||||
if (!rep->payloads) { |
||||
rep->payloads = absl::make_unique<status_internal::Payloads>(); |
||||
} |
||||
|
||||
int index = |
||||
status_internal::FindPayloadIndexByUrl(rep->payloads.get(), type_url); |
||||
if (index != -1) { |
||||
(*rep->payloads)[index].payload = std::move(payload); |
||||
return; |
||||
} |
||||
|
||||
rep->payloads->push_back({std::string(type_url), std::move(payload)}); |
||||
} |
||||
|
||||
bool Status::ErasePayload(absl::string_view type_url) { |
||||
int index = status_internal::FindPayloadIndexByUrl(GetPayloads(), type_url); |
||||
if (index != -1) { |
||||
GetPayloads()->erase(GetPayloads()->begin() + index); |
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
void Status::ForEachPayload( |
||||
const std::function<void(absl::string_view, const absl::Cord&)>& visitor) |
||||
const { |
||||
if (auto* payloads = GetPayloads()) { |
||||
bool in_reverse = |
||||
payloads->size() > 1 && reinterpret_cast<uintptr_t>(payloads) % 13 > 6; |
||||
|
||||
for (int index = 0; index < payloads->size(); ++index) { |
||||
const auto& elem = |
||||
(*payloads)[in_reverse ? payloads->size() - 1 - index : index]; |
||||
|
||||
#ifdef NDEBUG |
||||
visitor(elem.type_url, elem.payload); |
||||
#else |
||||
// In debug mode invaldiate the type url to prevent users from relying on
|
||||
// this std::string lifetime.
|
||||
|
||||
// NOLINTNEXTLINE intentional extra conversion to force temporary.
|
||||
visitor(std::string(elem.type_url), elem.payload); |
||||
#endif // NDEBUG
|
||||
} |
||||
} |
||||
} |
||||
|
||||
const std::string* Status::EmptyString() { |
||||
static std::string* empty_string = new std::string(); |
||||
return empty_string; |
||||
} |
||||
|
||||
constexpr const char Status::kMovedFromString[]; |
||||
|
||||
const std::string* Status::MovedFromString() { |
||||
static std::string* moved_from_string = new std::string(kMovedFromString); |
||||
return moved_from_string; |
||||
} |
||||
|
||||
void Status::UnrefNonInlined(uintptr_t rep) { |
||||
status_internal::StatusRep* r = RepToPointer(rep); |
||||
// Fast path: if ref==1, there is no need for a RefCountDec (since
|
||||
// this is the only reference and therefore no other thread is
|
||||
// allowed to be mucking with r).
|
||||
if (r->ref.load(std::memory_order_acquire) == 1 || |
||||
r->ref.fetch_sub(1, std::memory_order_acq_rel) - 1 == 0) { |
||||
delete r; |
||||
} |
||||
} |
||||
|
||||
uintptr_t Status::NewRep(absl::StatusCode code, absl::string_view msg, |
||||
std::unique_ptr<status_internal::Payloads> payloads) { |
||||
status_internal::StatusRep* rep = new status_internal::StatusRep; |
||||
rep->ref.store(1, std::memory_order_relaxed); |
||||
rep->code = code; |
||||
rep->message.assign(msg.data(), msg.size()); |
||||
rep->payloads = std::move(payloads); |
||||
return PointerToRep(rep); |
||||
} |
||||
|
||||
Status::Status(absl::StatusCode code, absl::string_view msg) |
||||
: rep_(CodeToInlinedRep(code)) { |
||||
if (code != absl::StatusCode::kOk && !msg.empty()) { |
||||
rep_ = NewRep(code, msg, nullptr); |
||||
} |
||||
} |
||||
|
||||
int Status::raw_code() const { |
||||
if (IsInlined(rep_)) { |
||||
return static_cast<int>(InlinedRepToCode(rep_)); |
||||
} |
||||
status_internal::StatusRep* rep = RepToPointer(rep_); |
||||
return static_cast<int>(rep->code); |
||||
} |
||||
|
||||
absl::StatusCode Status::code() const { |
||||
return status_internal::MapToLocalCode(raw_code()); |
||||
} |
||||
|
||||
void Status::PrepareToModify() { |
||||
ABSL_RAW_CHECK(!ok(), "PrepareToModify shouldn't be called on OK status."); |
||||
if (IsInlined(rep_)) { |
||||
rep_ = NewRep(static_cast<absl::StatusCode>(raw_code()), |
||||
absl::string_view(), nullptr); |
||||
return; |
||||
} |
||||
|
||||
uintptr_t rep_i = rep_; |
||||
status_internal::StatusRep* rep = RepToPointer(rep_); |
||||
if (rep->ref.load(std::memory_order_acquire) != 1) { |
||||
std::unique_ptr<status_internal::Payloads> payloads; |
||||
if (rep->payloads) { |
||||
payloads = absl::make_unique<status_internal::Payloads>(*rep->payloads); |
||||
} |
||||
rep_ = NewRep(rep->code, message(), std::move(payloads)); |
||||
UnrefNonInlined(rep_i); |
||||
} |
||||
} |
||||
|
||||
bool Status::EqualsSlow(const absl::Status& a, const absl::Status& b) { |
||||
if (IsInlined(a.rep_) != IsInlined(b.rep_)) return false; |
||||
if (a.message() != b.message()) return false; |
||||
if (a.raw_code() != b.raw_code()) return false; |
||||
if (a.GetPayloads() == b.GetPayloads()) return true; |
||||
|
||||
const status_internal::Payloads no_payloads; |
||||
const status_internal::Payloads* larger_payloads = |
||||
a.GetPayloads() ? a.GetPayloads() : &no_payloads; |
||||
const status_internal::Payloads* smaller_payloads = |
||||
b.GetPayloads() ? b.GetPayloads() : &no_payloads; |
||||
if (larger_payloads->size() < smaller_payloads->size()) { |
||||
std::swap(larger_payloads, smaller_payloads); |
||||
} |
||||
if ((larger_payloads->size() - smaller_payloads->size()) > 1) return false; |
||||
// Payloads can be ordered differently, so we can't just compare payload
|
||||
// vectors.
|
||||
for (const auto& payload : *larger_payloads) { |
||||
|
||||
bool found = false; |
||||
for (const auto& other_payload : *smaller_payloads) { |
||||
if (payload.type_url == other_payload.type_url) { |
||||
if (payload.payload != other_payload.payload) { |
||||
return false; |
||||
} |
||||
found = true; |
||||
break; |
||||
} |
||||
} |
||||
if (!found) return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
std::string Status::ToStringSlow() const { |
||||
std::string text; |
||||
absl::StrAppend(&text, absl::StatusCodeToString(code()), ": ", message()); |
||||
status_internal::StatusPayloadPrinter printer = |
||||
status_internal::GetStatusPayloadPrinter(); |
||||
this->ForEachPayload([&](absl::string_view type_url, |
||||
const absl::Cord& payload) { |
||||
absl::optional<std::string> result; |
||||
if (printer) result = printer(type_url, payload); |
||||
absl::StrAppend( |
||||
&text, " [", type_url, "='", |
||||
result.has_value() ? *result : absl::CHexEscape(std::string(payload)), |
||||
"']"); |
||||
}); |
||||
|
||||
return text; |
||||
} |
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Status& x) { |
||||
os << x.ToString(); |
||||
return os; |
||||
} |
||||
|
||||
Status AbortedError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kAborted, message); |
||||
} |
||||
|
||||
Status AlreadyExistsError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kAlreadyExists, message); |
||||
} |
||||
|
||||
Status CancelledError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kCancelled, message); |
||||
} |
||||
|
||||
Status DataLossError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kDataLoss, message); |
||||
} |
||||
|
||||
Status DeadlineExceededError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kDeadlineExceeded, message); |
||||
} |
||||
|
||||
Status FailedPreconditionError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kFailedPrecondition, message); |
||||
} |
||||
|
||||
Status InternalError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kInternal, message); |
||||
} |
||||
|
||||
Status InvalidArgumentError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kInvalidArgument, message); |
||||
} |
||||
|
||||
Status NotFoundError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kNotFound, message); |
||||
} |
||||
|
||||
Status OutOfRangeError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kOutOfRange, message); |
||||
} |
||||
|
||||
Status PermissionDeniedError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kPermissionDenied, message); |
||||
} |
||||
|
||||
Status ResourceExhaustedError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kResourceExhausted, message); |
||||
} |
||||
|
||||
Status UnauthenticatedError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kUnauthenticated, message); |
||||
} |
||||
|
||||
Status UnavailableError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kUnavailable, message); |
||||
} |
||||
|
||||
Status UnimplementedError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kUnimplemented, message); |
||||
} |
||||
|
||||
Status UnknownError(absl::string_view message) { |
||||
return Status(absl::StatusCode::kUnknown, message); |
||||
} |
||||
|
||||
bool IsAborted(const Status& status) { |
||||
return status.code() == absl::StatusCode::kAborted; |
||||
} |
||||
|
||||
bool IsAlreadyExists(const Status& status) { |
||||
return status.code() == absl::StatusCode::kAlreadyExists; |
||||
} |
||||
|
||||
bool IsCancelled(const Status& status) { |
||||
return status.code() == absl::StatusCode::kCancelled; |
||||
} |
||||
|
||||
bool IsDataLoss(const Status& status) { |
||||
return status.code() == absl::StatusCode::kDataLoss; |
||||
} |
||||
|
||||
bool IsDeadlineExceeded(const Status& status) { |
||||
return status.code() == absl::StatusCode::kDeadlineExceeded; |
||||
} |
||||
|
||||
bool IsFailedPrecondition(const Status& status) { |
||||
return status.code() == absl::StatusCode::kFailedPrecondition; |
||||
} |
||||
|
||||
bool IsInternal(const Status& status) { |
||||
return status.code() == absl::StatusCode::kInternal; |
||||
} |
||||
|
||||
bool IsInvalidArgument(const Status& status) { |
||||
return status.code() == absl::StatusCode::kInvalidArgument; |
||||
} |
||||
|
||||
bool IsNotFound(const Status& status) { |
||||
return status.code() == absl::StatusCode::kNotFound; |
||||
} |
||||
|
||||
bool IsOutOfRange(const Status& status) { |
||||
return status.code() == absl::StatusCode::kOutOfRange; |
||||
} |
||||
|
||||
bool IsPermissionDenied(const Status& status) { |
||||
return status.code() == absl::StatusCode::kPermissionDenied; |
||||
} |
||||
|
||||
bool IsResourceExhausted(const Status& status) { |
||||
return status.code() == absl::StatusCode::kResourceExhausted; |
||||
} |
||||
|
||||
bool IsUnauthenticated(const Status& status) { |
||||
return status.code() == absl::StatusCode::kUnauthenticated; |
||||
} |
||||
|
||||
bool IsUnavailable(const Status& status) { |
||||
return status.code() == absl::StatusCode::kUnavailable; |
||||
} |
||||
|
||||
bool IsUnimplemented(const Status& status) { |
||||
return status.code() == absl::StatusCode::kUnimplemented; |
||||
} |
||||
|
||||
bool IsUnknown(const Status& status) { |
||||
return status.code() == absl::StatusCode::kUnknown; |
||||
} |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,428 @@ |
||||
// Copyright 2019 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.
|
||||
#ifndef ABSL_STATUS_STATUS_H_ |
||||
#define ABSL_STATUS_STATUS_H_ |
||||
|
||||
#include <iostream> |
||||
#include <string> |
||||
|
||||
#include "absl/container/inlined_vector.h" |
||||
#include "absl/strings/cord.h" |
||||
#include "absl/types/optional.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
|
||||
enum class StatusCode : int { |
||||
kOk = 0, |
||||
kCancelled = 1, |
||||
kUnknown = 2, |
||||
kInvalidArgument = 3, |
||||
kDeadlineExceeded = 4, |
||||
kNotFound = 5, |
||||
kAlreadyExists = 6, |
||||
kPermissionDenied = 7, |
||||
kResourceExhausted = 8, |
||||
kFailedPrecondition = 9, |
||||
kAborted = 10, |
||||
kOutOfRange = 11, |
||||
kUnimplemented = 12, |
||||
kInternal = 13, |
||||
kUnavailable = 14, |
||||
kDataLoss = 15, |
||||
kUnauthenticated = 16, |
||||
kDoNotUseReservedForFutureExpansionUseDefaultInSwitchInstead_ = 20 |
||||
}; |
||||
|
||||
// Returns the name for the status code, or "" if it is an unknown value.
|
||||
std::string StatusCodeToString(StatusCode code); |
||||
|
||||
// Streams StatusCodeToString(code) to `os`.
|
||||
std::ostream& operator<<(std::ostream& os, StatusCode code); |
||||
|
||||
namespace status_internal { |
||||
|
||||
// Container for status payloads.
|
||||
struct Payload { |
||||
std::string type_url; |
||||
absl::Cord payload; |
||||
}; |
||||
|
||||
using Payloads = absl::InlinedVector<Payload, 1>; |
||||
|
||||
// Reference-counted representation of Status data.
|
||||
struct StatusRep { |
||||
std::atomic<int32_t> ref; |
||||
absl::StatusCode code; |
||||
std::string message; |
||||
std::unique_ptr<status_internal::Payloads> payloads; |
||||
}; |
||||
|
||||
absl::StatusCode MapToLocalCode(int value); |
||||
} // namespace status_internal
|
||||
|
||||
class ABSL_MUST_USE_RESULT Status final { |
||||
public: |
||||
// Creates an OK status with no message or payload.
|
||||
Status(); |
||||
|
||||
// Create a status in the canonical error space with the specified code and
|
||||
// error message. If `code == util::error::OK`, `msg` is ignored and an
|
||||
// object identical to an OK status is constructed.
|
||||
//
|
||||
// `msg` must be in UTF-8. The implementation may complain (e.g.,
|
||||
// by printing a warning) if it is not.
|
||||
Status(absl::StatusCode code, absl::string_view msg); |
||||
|
||||
Status(const Status&); |
||||
Status& operator=(const Status& x); |
||||
|
||||
// Move operations.
|
||||
// The moved-from state is valid but unspecified.
|
||||
Status(Status&&) noexcept; |
||||
Status& operator=(Status&&); |
||||
|
||||
~Status(); |
||||
|
||||
// If `this->ok()`, stores `new_status` into *this. If `!this->ok()`,
|
||||
// preserves the current data. May, in the future, augment the current status
|
||||
// with additional information about `new_status`.
|
||||
//
|
||||
// Convenient way of keeping track of the first error encountered.
|
||||
// Instead of:
|
||||
// if (overall_status.ok()) overall_status = new_status
|
||||
// Use:
|
||||
// overall_status.Update(new_status);
|
||||
//
|
||||
// Style guide exception for rvalue reference granted in CL 153567220.
|
||||
void Update(const Status& new_status); |
||||
void Update(Status&& new_status); |
||||
|
||||
// Returns true if the Status is OK.
|
||||
ABSL_MUST_USE_RESULT bool ok() const; |
||||
|
||||
// Returns the (canonical) error code.
|
||||
absl::StatusCode code() const; |
||||
|
||||
// Returns the raw (canonical) error code which could be out of the range of
|
||||
// the local `absl::StatusCode` enum. NOTE: This should only be called when
|
||||
// converting to wire format. Use `code` for error handling.
|
||||
int raw_code() const; |
||||
|
||||
// Returns the error message. Note: prefer ToString() for debug logging.
|
||||
// This message rarely describes the error code. It is not unusual for the
|
||||
// error message to be the empty std::string.
|
||||
absl::string_view message() const; |
||||
|
||||
friend bool operator==(const Status&, const Status&); |
||||
friend bool operator!=(const Status&, const Status&); |
||||
|
||||
// Returns a combination of the error code name, the message and the payloads.
|
||||
// You can expect the code name and the message to be substrings of the
|
||||
// result, and the payloads to be printed by the registered printer extensions
|
||||
// if they are recognized.
|
||||
// WARNING: Do not depend on the exact format of the result of `ToString()`
|
||||
// which is subject to change.
|
||||
std::string ToString() const; |
||||
|
||||
// Ignores any errors. This method does nothing except potentially suppress
|
||||
// complaints from any tools that are checking that errors are not dropped on
|
||||
// the floor.
|
||||
void IgnoreError() const; |
||||
|
||||
// Swap the contents of `a` with `b`
|
||||
friend void swap(Status& a, Status& b); |
||||
|
||||
// Payload management APIs
|
||||
|
||||
// Type URL should be unique and follow the naming convention below:
|
||||
// The idea of type URL comes from `google.protobuf.Any`
|
||||
// (https://developers.google.com/protocol-buffers/docs/proto3#any). The
|
||||
// type URL should be globally unique and follow the format of URL
|
||||
// (https://en.wikipedia.org/wiki/URL). The default type URL for a given
|
||||
// protobuf message type is "type.googleapis.com/packagename.messagename". For
|
||||
// other custom wire formats, users should define the format of type URL in a
|
||||
// similar practice so as to minimize the chance of conflict between type
|
||||
// URLs. Users should make sure that the type URL can be mapped to a concrete
|
||||
// C++ type if they want to deserialize the payload and read it effectively.
|
||||
|
||||
// Gets the payload based for `type_url` key, if it is present.
|
||||
absl::optional<absl::Cord> GetPayload(absl::string_view type_url) const; |
||||
|
||||
// Sets the payload for `type_url` key for a non-ok status, overwriting any
|
||||
// existing payload for `type_url`.
|
||||
//
|
||||
// NOTE: Does nothing if the Status is ok.
|
||||
void SetPayload(absl::string_view type_url, absl::Cord payload); |
||||
|
||||
// Erases the payload corresponding to the `type_url` key. Returns true if
|
||||
// the payload was present.
|
||||
bool ErasePayload(absl::string_view type_url); |
||||
|
||||
// Iterates over the stored payloads and calls `visitor(type_key, payload)`
|
||||
// for each one.
|
||||
//
|
||||
// NOTE: The order of calls to `visitor` is not specified and may change at
|
||||
// any time.
|
||||
//
|
||||
// NOTE: Any mutation on the same 'Status' object during visitation is
|
||||
// forbidden and could result in undefined behavior.
|
||||
void ForEachPayload( |
||||
const std::function<void(absl::string_view, const absl::Cord&)>& visitor) |
||||
const; |
||||
|
||||
private: |
||||
friend Status CancelledError(); |
||||
|
||||
// Creates a status in the canonical error space with the specified
|
||||
// code, and an empty error message.
|
||||
explicit Status(absl::StatusCode code); |
||||
|
||||
static void UnrefNonInlined(uintptr_t rep); |
||||
static void Ref(uintptr_t rep); |
||||
static void Unref(uintptr_t rep); |
||||
|
||||
// REQUIRES: !ok()
|
||||
// Ensures rep_ is not shared with any other Status.
|
||||
void PrepareToModify(); |
||||
|
||||
const status_internal::Payloads* GetPayloads() const; |
||||
status_internal::Payloads* GetPayloads(); |
||||
|
||||
// Takes ownership of payload.
|
||||
static uintptr_t NewRep(absl::StatusCode code, absl::string_view msg, |
||||
std::unique_ptr<status_internal::Payloads> payload); |
||||
static bool EqualsSlow(const absl::Status& a, const absl::Status& b); |
||||
|
||||
// MSVC 14.0 limitation requires the const.
|
||||
static constexpr const char kMovedFromString[] = |
||||
"Status accessed after move."; |
||||
|
||||
static const std::string* EmptyString(); |
||||
static const std::string* MovedFromString(); |
||||
|
||||
// Returns whether rep contains an inlined representation.
|
||||
// See rep_ for details.
|
||||
static bool IsInlined(uintptr_t rep); |
||||
|
||||
// Indicates whether this Status was the rhs of a move operation. See rep_
|
||||
// for details.
|
||||
static bool IsMovedFrom(uintptr_t rep); |
||||
static uintptr_t MovedFromRep(); |
||||
|
||||
// Convert between error::Code and the inlined uintptr_t representation used
|
||||
// by rep_. See rep_ for details.
|
||||
static uintptr_t CodeToInlinedRep(absl::StatusCode code); |
||||
static absl::StatusCode InlinedRepToCode(uintptr_t rep); |
||||
|
||||
// Converts between StatusRep* and the external uintptr_t representation used
|
||||
// by rep_. See rep_ for details.
|
||||
static uintptr_t PointerToRep(status_internal::StatusRep* r); |
||||
static status_internal::StatusRep* RepToPointer(uintptr_t r); |
||||
|
||||
// Returns std::string for non-ok Status.
|
||||
std::string ToStringSlow() const; |
||||
|
||||
// Status supports two different representations.
|
||||
// - When the low bit is off it is an inlined representation.
|
||||
// It uses the canonical error space, no message or payload.
|
||||
// The error code is (rep_ >> 2).
|
||||
// The (rep_ & 2) bit is the "moved from" indicator, used in IsMovedFrom().
|
||||
// - When the low bit is on it is an external representation.
|
||||
// In this case all the data comes from a heap allocated Rep object.
|
||||
// (rep_ - 1) is a status_internal::StatusRep* pointer to that structure.
|
||||
uintptr_t rep_; |
||||
}; |
||||
|
||||
// Returns an OK status, equivalent to a default constructed instance.
|
||||
Status OkStatus(); |
||||
|
||||
// Prints a human-readable representation of `x` to `os`.
|
||||
std::ostream& operator<<(std::ostream& os, const Status& x); |
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Implementation details follow
|
||||
|
||||
inline Status::Status() : rep_(CodeToInlinedRep(absl::StatusCode::kOk)) {} |
||||
|
||||
inline Status::Status(absl::StatusCode code) : rep_(CodeToInlinedRep(code)) {} |
||||
|
||||
inline Status::Status(const Status& x) : rep_(x.rep_) { Ref(rep_); } |
||||
|
||||
inline Status& Status::operator=(const Status& x) { |
||||
uintptr_t old_rep = rep_; |
||||
if (x.rep_ != old_rep) { |
||||
Ref(x.rep_); |
||||
rep_ = x.rep_; |
||||
Unref(old_rep); |
||||
} |
||||
return *this; |
||||
} |
||||
|
||||
inline Status::Status(Status&& x) noexcept : rep_(x.rep_) { |
||||
x.rep_ = MovedFromRep(); |
||||
} |
||||
|
||||
inline Status& Status::operator=(Status&& x) { |
||||
uintptr_t old_rep = rep_; |
||||
rep_ = x.rep_; |
||||
x.rep_ = MovedFromRep(); |
||||
Unref(old_rep); |
||||
return *this; |
||||
} |
||||
|
||||
inline void Status::Update(const Status& new_status) { |
||||
if (ok()) { |
||||
*this = new_status; |
||||
} |
||||
} |
||||
|
||||
inline void Status::Update(Status&& new_status) { |
||||
if (ok()) { |
||||
*this = std::move(new_status); |
||||
} |
||||
} |
||||
|
||||
inline Status::~Status() { Unref(rep_); } |
||||
|
||||
inline bool Status::ok() const { |
||||
return rep_ == CodeToInlinedRep(absl::StatusCode::kOk); |
||||
} |
||||
|
||||
inline absl::string_view Status::message() const { |
||||
return !IsInlined(rep_) |
||||
? RepToPointer(rep_)->message |
||||
: (IsMovedFrom(rep_) ? absl::string_view(kMovedFromString) |
||||
: absl::string_view()); |
||||
} |
||||
|
||||
inline bool operator==(const Status& lhs, const Status& rhs) { |
||||
return lhs.rep_ == rhs.rep_ || Status::EqualsSlow(lhs, rhs); |
||||
} |
||||
|
||||
inline bool operator!=(const Status& lhs, const Status& rhs) { |
||||
return !(lhs == rhs); |
||||
} |
||||
|
||||
inline std::string Status::ToString() const { |
||||
return ok() ? "OK" : ToStringSlow(); |
||||
} |
||||
|
||||
inline void Status::IgnoreError() const { |
||||
// no-op
|
||||
} |
||||
|
||||
inline void swap(absl::Status& a, absl::Status& b) { |
||||
using std::swap; |
||||
swap(a.rep_, b.rep_); |
||||
} |
||||
|
||||
inline const status_internal::Payloads* Status::GetPayloads() const { |
||||
return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get(); |
||||
} |
||||
|
||||
inline status_internal::Payloads* Status::GetPayloads() { |
||||
return IsInlined(rep_) ? nullptr : RepToPointer(rep_)->payloads.get(); |
||||
} |
||||
|
||||
inline bool Status::IsInlined(uintptr_t rep) { return (rep & 1) == 0; } |
||||
|
||||
inline bool Status::IsMovedFrom(uintptr_t rep) { |
||||
return IsInlined(rep) && (rep & 2) != 0; |
||||
} |
||||
|
||||
inline uintptr_t Status::MovedFromRep() { |
||||
return CodeToInlinedRep(absl::StatusCode::kInternal) | 2; |
||||
} |
||||
|
||||
inline uintptr_t Status::CodeToInlinedRep(absl::StatusCode code) { |
||||
return static_cast<uintptr_t>(code) << 2; |
||||
} |
||||
|
||||
inline absl::StatusCode Status::InlinedRepToCode(uintptr_t rep) { |
||||
assert(IsInlined(rep)); |
||||
return static_cast<absl::StatusCode>(rep >> 2); |
||||
} |
||||
|
||||
inline status_internal::StatusRep* Status::RepToPointer(uintptr_t rep) { |
||||
assert(!IsInlined(rep)); |
||||
return reinterpret_cast<status_internal::StatusRep*>(rep - 1); |
||||
} |
||||
|
||||
inline uintptr_t Status::PointerToRep(status_internal::StatusRep* rep) { |
||||
return reinterpret_cast<uintptr_t>(rep) + 1; |
||||
} |
||||
|
||||
inline void Status::Ref(uintptr_t rep) { |
||||
if (!IsInlined(rep)) { |
||||
RepToPointer(rep)->ref.fetch_add(1, std::memory_order_relaxed); |
||||
} |
||||
} |
||||
|
||||
inline void Status::Unref(uintptr_t rep) { |
||||
if (!IsInlined(rep)) { |
||||
UnrefNonInlined(rep); |
||||
} |
||||
} |
||||
|
||||
inline Status OkStatus() { return Status(); } |
||||
|
||||
// Each of the functions below creates a Status object with a particular error
|
||||
// code and the given message. The error code of the returned status object
|
||||
// matches the name of the function.
|
||||
Status AbortedError(absl::string_view message); |
||||
Status AlreadyExistsError(absl::string_view message); |
||||
Status CancelledError(absl::string_view message); |
||||
Status DataLossError(absl::string_view message); |
||||
Status DeadlineExceededError(absl::string_view message); |
||||
Status FailedPreconditionError(absl::string_view message); |
||||
Status InternalError(absl::string_view message); |
||||
Status InvalidArgumentError(absl::string_view message); |
||||
Status NotFoundError(absl::string_view message); |
||||
Status OutOfRangeError(absl::string_view message); |
||||
Status PermissionDeniedError(absl::string_view message); |
||||
Status ResourceExhaustedError(absl::string_view message); |
||||
Status UnauthenticatedError(absl::string_view message); |
||||
Status UnavailableError(absl::string_view message); |
||||
Status UnimplementedError(absl::string_view message); |
||||
Status UnknownError(absl::string_view message); |
||||
|
||||
// Creates a `Status` object with the `absl::StatusCode::kCancelled` error code
|
||||
// and an empty message. It is provided only for efficiency, given that
|
||||
// message-less kCancelled errors are common in the infrastructure.
|
||||
inline Status CancelledError() { return Status(absl::StatusCode::kCancelled); } |
||||
|
||||
// Each of the functions below returns true if the given status matches the
|
||||
// error code implied by the function's name.
|
||||
ABSL_MUST_USE_RESULT bool IsAborted(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsAlreadyExists(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsCancelled(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsDataLoss(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsDeadlineExceeded(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsFailedPrecondition(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsInternal(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsInvalidArgument(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsNotFound(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsOutOfRange(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsPermissionDenied(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsResourceExhausted(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsUnauthenticated(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsUnavailable(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsUnimplemented(const Status& status); |
||||
ABSL_MUST_USE_RESULT bool IsUnknown(const Status& status); |
||||
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_STATUS_STATUS_H_
|
@ -0,0 +1,43 @@ |
||||
// Copyright 2019 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_payload_printer.h" |
||||
|
||||
#include <atomic> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace status_internal { |
||||
|
||||
namespace { |
||||
// Tried constant initialized global variable but it doesn't work with Lexan
|
||||
// (MSVC's `std::atomic` has trouble constant initializing).
|
||||
std::atomic<StatusPayloadPrinter>& GetStatusPayloadPrinterStorage() { |
||||
ABSL_CONST_INIT static std::atomic<StatusPayloadPrinter> instance{nullptr}; |
||||
return instance; |
||||
} |
||||
} // namespace
|
||||
|
||||
void SetStatusPayloadPrinter(StatusPayloadPrinter printer) { |
||||
GetStatusPayloadPrinterStorage().store(printer, std::memory_order_relaxed); |
||||
} |
||||
|
||||
StatusPayloadPrinter GetStatusPayloadPrinter() { |
||||
return GetStatusPayloadPrinterStorage().load(std::memory_order_relaxed); |
||||
} |
||||
|
||||
} // namespace status_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
@ -0,0 +1,51 @@ |
||||
// Copyright 2019 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.
|
||||
#ifndef ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_ |
||||
#define ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_ |
||||
|
||||
#include <string> |
||||
|
||||
#include "absl/strings/cord.h" |
||||
#include "absl/strings/string_view.h" |
||||
#include "absl/types/optional.h" |
||||
|
||||
namespace absl { |
||||
ABSL_NAMESPACE_BEGIN |
||||
namespace status_internal { |
||||
|
||||
// By default, `Status::ToString` and `operator<<(Status)` print a payload by
|
||||
// dumping the type URL and the raw bytes. To help debugging, we provide an
|
||||
// extension point, which is a global printer function that can be set by users
|
||||
// to specify how to print payloads. The function takes the type URL and the
|
||||
// payload as input, and should return a valid human-readable string on success
|
||||
// or `absl::nullopt` on failure (in which case it falls back to the default
|
||||
// approach of printing the raw bytes).
|
||||
// NOTE: This is an internal API and the design is subject to change in the
|
||||
// future in a non-backward-compatible way. Since it's only meant for debugging
|
||||
// purpose, you should not rely on it in any critical logic.
|
||||
using StatusPayloadPrinter = absl::optional<std::string> (*)(absl::string_view, |
||||
const absl::Cord&); |
||||
|
||||
// Sets the global payload printer. Only one printer should be set per process.
|
||||
// If multiple printers are set, it's undefined which one will be used.
|
||||
void SetStatusPayloadPrinter(StatusPayloadPrinter); |
||||
|
||||
// Returns the global payload printer if previously set, otherwise `nullptr`.
|
||||
StatusPayloadPrinter GetStatusPayloadPrinter(); |
||||
|
||||
} // namespace status_internal
|
||||
ABSL_NAMESPACE_END |
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_STATUS_STATUS_PAYLOAD_PRINTER_H_
|
@ -0,0 +1,401 @@ |
||||
// Copyright 2019 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.h" |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/strings/str_cat.h" |
||||
|
||||
namespace { |
||||
|
||||
using ::testing::Eq; |
||||
using ::testing::HasSubstr; |
||||
using ::testing::Optional; |
||||
using ::testing::UnorderedElementsAreArray; |
||||
|
||||
TEST(StatusCode, InsertionOperator) { |
||||
const absl::StatusCode code = absl::StatusCode::kUnknown; |
||||
std::ostringstream oss; |
||||
oss << code; |
||||
EXPECT_EQ(oss.str(), absl::StatusCodeToString(code)); |
||||
} |
||||
|
||||
// This structure holds the details for testing a single error code,
|
||||
// its creator, and its classifier.
|
||||
struct ErrorTest { |
||||
absl::StatusCode code; |
||||
using Creator = absl::Status (*)(absl::string_view); |
||||
using Classifier = bool (*)(const absl::Status&); |
||||
Creator creator; |
||||
Classifier classifier; |
||||
}; |
||||
|
||||
constexpr ErrorTest kErrorTests[]{ |
||||
{absl::StatusCode::kCancelled, absl::CancelledError, absl::IsCancelled}, |
||||
{absl::StatusCode::kUnknown, absl::UnknownError, absl::IsUnknown}, |
||||
{absl::StatusCode::kInvalidArgument, absl::InvalidArgumentError, |
||||
absl::IsInvalidArgument}, |
||||
{absl::StatusCode::kDeadlineExceeded, absl::DeadlineExceededError, |
||||
absl::IsDeadlineExceeded}, |
||||
{absl::StatusCode::kNotFound, absl::NotFoundError, absl::IsNotFound}, |
||||
{absl::StatusCode::kAlreadyExists, absl::AlreadyExistsError, |
||||
absl::IsAlreadyExists}, |
||||
{absl::StatusCode::kPermissionDenied, absl::PermissionDeniedError, |
||||
absl::IsPermissionDenied}, |
||||
{absl::StatusCode::kResourceExhausted, absl::ResourceExhaustedError, |
||||
absl::IsResourceExhausted}, |
||||
{absl::StatusCode::kFailedPrecondition, absl::FailedPreconditionError, |
||||
absl::IsFailedPrecondition}, |
||||
{absl::StatusCode::kAborted, absl::AbortedError, absl::IsAborted}, |
||||
{absl::StatusCode::kOutOfRange, absl::OutOfRangeError, absl::IsOutOfRange}, |
||||
{absl::StatusCode::kUnimplemented, absl::UnimplementedError, |
||||
absl::IsUnimplemented}, |
||||
{absl::StatusCode::kInternal, absl::InternalError, absl::IsInternal}, |
||||
{absl::StatusCode::kUnavailable, absl::UnavailableError, |
||||
absl::IsUnavailable}, |
||||
{absl::StatusCode::kDataLoss, absl::DataLossError, absl::IsDataLoss}, |
||||
{absl::StatusCode::kUnauthenticated, absl::UnauthenticatedError, |
||||
absl::IsUnauthenticated}, |
||||
}; |
||||
|
||||
TEST(Status, CreateAndClassify) { |
||||
for (const auto& test : kErrorTests) { |
||||
SCOPED_TRACE(absl::StatusCodeToString(test.code)); |
||||
|
||||
// Ensure that the creator does, in fact, create status objects with the
|
||||
// expected error code and message.
|
||||
std::string message = |
||||
absl::StrCat("error code ", test.code, " test message"); |
||||
absl::Status status = test.creator(message); |
||||
EXPECT_EQ(test.code, status.code()); |
||||
EXPECT_EQ(message, status.message()); |
||||
|
||||
// Ensure that the classifier returns true for a status produced by the
|
||||
// creator.
|
||||
EXPECT_TRUE(test.classifier(status)); |
||||
|
||||
// Ensure that the classifier returns false for status with a different
|
||||
// code.
|
||||
for (const auto& other : kErrorTests) { |
||||
if (other.code != test.code) { |
||||
EXPECT_FALSE(test.classifier(absl::Status(other.code, ""))) |
||||
<< " other.code = " << other.code; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
TEST(Status, DefaultConstructor) { |
||||
absl::Status status; |
||||
EXPECT_TRUE(status.ok()); |
||||
EXPECT_EQ(absl::StatusCode::kOk, status.code()); |
||||
EXPECT_EQ("", status.message()); |
||||
} |
||||
|
||||
TEST(Status, OkStatus) { |
||||
absl::Status status = absl::OkStatus(); |
||||
EXPECT_TRUE(status.ok()); |
||||
EXPECT_EQ(absl::StatusCode::kOk, status.code()); |
||||
EXPECT_EQ("", status.message()); |
||||
} |
||||
|
||||
TEST(Status, ConstructorWithCodeMessage) { |
||||
{ |
||||
absl::Status status(absl::StatusCode::kCancelled, ""); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(absl::StatusCode::kCancelled, status.code()); |
||||
EXPECT_EQ("", status.message()); |
||||
} |
||||
{ |
||||
absl::Status status(absl::StatusCode::kInternal, "message"); |
||||
EXPECT_FALSE(status.ok()); |
||||
EXPECT_EQ(absl::StatusCode::kInternal, status.code()); |
||||
EXPECT_EQ("message", status.message()); |
||||
} |
||||
} |
||||
|
||||
TEST(Status, ConstructOutOfRangeCode) { |
||||
const int kRawCode = 9999; |
||||
absl::Status status(static_cast<absl::StatusCode>(kRawCode), ""); |
||||
EXPECT_EQ(absl::StatusCode::kUnknown, status.code()); |
||||
EXPECT_EQ(kRawCode, status.raw_code()); |
||||
} |
||||
|
||||
constexpr char kUrl1[] = "url.payload.1"; |
||||
constexpr char kUrl2[] = "url.payload.2"; |
||||
constexpr char kUrl3[] = "url.payload.3"; |
||||
constexpr char kUrl4[] = "url.payload.xx"; |
||||
|
||||
constexpr char kPayload1[] = "aaaaa"; |
||||
constexpr char kPayload2[] = "bbbbb"; |
||||
constexpr char kPayload3[] = "ccccc"; |
||||
|
||||
using PayloadsVec = std::vector<std::pair<std::string, absl::Cord>>; |
||||
|
||||
TEST(Status, TestGetSetPayload) { |
||||
absl::Status ok_status = absl::OkStatus(); |
||||
ok_status.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
ok_status.SetPayload(kUrl2, absl::Cord(kPayload2)); |
||||
|
||||
EXPECT_FALSE(ok_status.GetPayload(kUrl1)); |
||||
EXPECT_FALSE(ok_status.GetPayload(kUrl2)); |
||||
|
||||
absl::Status bad_status(absl::StatusCode::kInternal, "fail"); |
||||
bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
bad_status.SetPayload(kUrl2, absl::Cord(kPayload2)); |
||||
|
||||
EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload1))); |
||||
EXPECT_THAT(bad_status.GetPayload(kUrl2), Optional(Eq(kPayload2))); |
||||
|
||||
EXPECT_FALSE(bad_status.GetPayload(kUrl3)); |
||||
|
||||
bad_status.SetPayload(kUrl1, absl::Cord(kPayload3)); |
||||
EXPECT_THAT(bad_status.GetPayload(kUrl1), Optional(Eq(kPayload3))); |
||||
|
||||
// Testing dynamically generated type_url
|
||||
bad_status.SetPayload(absl::StrCat(kUrl1, ".1"), absl::Cord(kPayload1)); |
||||
EXPECT_THAT(bad_status.GetPayload(absl::StrCat(kUrl1, ".1")), |
||||
Optional(Eq(kPayload1))); |
||||
} |
||||
|
||||
TEST(Status, TestErasePayload) { |
||||
absl::Status bad_status(absl::StatusCode::kInternal, "fail"); |
||||
bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
bad_status.SetPayload(kUrl2, absl::Cord(kPayload2)); |
||||
bad_status.SetPayload(kUrl3, absl::Cord(kPayload3)); |
||||
|
||||
EXPECT_FALSE(bad_status.ErasePayload(kUrl4)); |
||||
|
||||
EXPECT_TRUE(bad_status.GetPayload(kUrl2)); |
||||
EXPECT_TRUE(bad_status.ErasePayload(kUrl2)); |
||||
EXPECT_FALSE(bad_status.GetPayload(kUrl2)); |
||||
EXPECT_FALSE(bad_status.ErasePayload(kUrl2)); |
||||
|
||||
EXPECT_TRUE(bad_status.ErasePayload(kUrl1)); |
||||
EXPECT_TRUE(bad_status.ErasePayload(kUrl3)); |
||||
|
||||
bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
EXPECT_TRUE(bad_status.ErasePayload(kUrl1)); |
||||
} |
||||
|
||||
TEST(Status, TestComparePayloads) { |
||||
absl::Status bad_status1(absl::StatusCode::kInternal, "fail"); |
||||
bad_status1.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
bad_status1.SetPayload(kUrl2, absl::Cord(kPayload2)); |
||||
bad_status1.SetPayload(kUrl3, absl::Cord(kPayload3)); |
||||
|
||||
absl::Status bad_status2(absl::StatusCode::kInternal, "fail"); |
||||
bad_status2.SetPayload(kUrl2, absl::Cord(kPayload2)); |
||||
bad_status2.SetPayload(kUrl3, absl::Cord(kPayload3)); |
||||
bad_status2.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
|
||||
EXPECT_EQ(bad_status1, bad_status2); |
||||
} |
||||
|
||||
PayloadsVec AllVisitedPayloads(const absl::Status& s) { |
||||
PayloadsVec result; |
||||
|
||||
s.ForEachPayload([&](absl::string_view type_url, const absl::Cord& payload) { |
||||
result.push_back(std::make_pair(std::string(type_url), payload)); |
||||
}); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
TEST(Status, TestForEachPayload) { |
||||
absl::Status bad_status(absl::StatusCode::kInternal, "fail"); |
||||
bad_status.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
bad_status.SetPayload(kUrl2, absl::Cord(kPayload2)); |
||||
bad_status.SetPayload(kUrl3, absl::Cord(kPayload3)); |
||||
|
||||
int count = 0; |
||||
|
||||
bad_status.ForEachPayload( |
||||
[&count](absl::string_view, const absl::Cord&) { ++count; }); |
||||
|
||||
EXPECT_EQ(count, 3); |
||||
|
||||
PayloadsVec expected_payloads = {{kUrl1, absl::Cord(kPayload1)}, |
||||
{kUrl2, absl::Cord(kPayload2)}, |
||||
{kUrl3, absl::Cord(kPayload3)}}; |
||||
|
||||
// Test that we visit all the payloads in the status.
|
||||
PayloadsVec visited_payloads = AllVisitedPayloads(bad_status); |
||||
EXPECT_THAT(visited_payloads, UnorderedElementsAreArray(expected_payloads)); |
||||
|
||||
// Test that visitation order is not consistent between run.
|
||||
std::vector<absl::Status> scratch; |
||||
while (true) { |
||||
scratch.emplace_back(absl::StatusCode::kInternal, "fail"); |
||||
|
||||
scratch.back().SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
scratch.back().SetPayload(kUrl2, absl::Cord(kPayload2)); |
||||
scratch.back().SetPayload(kUrl3, absl::Cord(kPayload3)); |
||||
|
||||
if (AllVisitedPayloads(scratch.back()) != visited_payloads) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
TEST(Status, ToString) { |
||||
absl::Status s(absl::StatusCode::kInternal, "fail"); |
||||
EXPECT_EQ("INTERNAL: fail", s.ToString()); |
||||
s.SetPayload("foo", absl::Cord("bar")); |
||||
EXPECT_EQ("INTERNAL: fail [foo='bar']", s.ToString()); |
||||
s.SetPayload("bar", absl::Cord("\377")); |
||||
EXPECT_THAT(s.ToString(), |
||||
AllOf(HasSubstr("INTERNAL: fail"), HasSubstr("[foo='bar']"), |
||||
HasSubstr("[bar='\\xff']"))); |
||||
} |
||||
|
||||
TEST(Status, CopyConstructor) { |
||||
{ |
||||
absl::Status status; |
||||
absl::Status copy(status); |
||||
EXPECT_EQ(copy, status); |
||||
} |
||||
{ |
||||
absl::Status status(absl::StatusCode::kInvalidArgument, "message"); |
||||
absl::Status copy(status); |
||||
EXPECT_EQ(copy, status); |
||||
} |
||||
{ |
||||
absl::Status status(absl::StatusCode::kInvalidArgument, "message"); |
||||
status.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
absl::Status copy(status); |
||||
EXPECT_EQ(copy, status); |
||||
} |
||||
} |
||||
|
||||
TEST(Status, CopyAssignment) { |
||||
absl::Status assignee; |
||||
{ |
||||
absl::Status status; |
||||
assignee = status; |
||||
EXPECT_EQ(assignee, status); |
||||
} |
||||
{ |
||||
absl::Status status(absl::StatusCode::kInvalidArgument, "message"); |
||||
assignee = status; |
||||
EXPECT_EQ(assignee, status); |
||||
} |
||||
{ |
||||
absl::Status status(absl::StatusCode::kInvalidArgument, "message"); |
||||
status.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
assignee = status; |
||||
EXPECT_EQ(assignee, status); |
||||
} |
||||
} |
||||
|
||||
TEST(Status, MoveConstructor) { |
||||
{ |
||||
absl::Status status; |
||||
absl::Status copy(absl::Status{}); |
||||
EXPECT_EQ(copy, status); |
||||
} |
||||
{ |
||||
absl::Status status(absl::StatusCode::kInvalidArgument, "message"); |
||||
absl::Status copy( |
||||
absl::Status(absl::StatusCode::kInvalidArgument, "message")); |
||||
EXPECT_EQ(copy, status); |
||||
} |
||||
{ |
||||
absl::Status status(absl::StatusCode::kInvalidArgument, "message"); |
||||
status.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
absl::Status copy1(status); |
||||
absl::Status copy2(std::move(status)); |
||||
EXPECT_EQ(copy1, copy2); |
||||
} |
||||
} |
||||
|
||||
TEST(Status, MoveAssignment) { |
||||
absl::Status assignee; |
||||
{ |
||||
absl::Status status; |
||||
assignee = absl::Status(); |
||||
EXPECT_EQ(assignee, status); |
||||
} |
||||
{ |
||||
absl::Status status(absl::StatusCode::kInvalidArgument, "message"); |
||||
assignee = absl::Status(absl::StatusCode::kInvalidArgument, "message"); |
||||
EXPECT_EQ(assignee, status); |
||||
} |
||||
{ |
||||
absl::Status status(absl::StatusCode::kInvalidArgument, "message"); |
||||
status.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
absl::Status copy(status); |
||||
assignee = std::move(status); |
||||
EXPECT_EQ(assignee, copy); |
||||
} |
||||
} |
||||
|
||||
TEST(Status, Update) { |
||||
absl::Status s; |
||||
s.Update(absl::OkStatus()); |
||||
EXPECT_TRUE(s.ok()); |
||||
const absl::Status a(absl::StatusCode::kCancelled, "message"); |
||||
s.Update(a); |
||||
EXPECT_EQ(s, a); |
||||
const absl::Status b(absl::StatusCode::kInternal, "other message"); |
||||
s.Update(b); |
||||
EXPECT_EQ(s, a); |
||||
s.Update(absl::OkStatus()); |
||||
EXPECT_EQ(s, a); |
||||
EXPECT_FALSE(s.ok()); |
||||
} |
||||
|
||||
TEST(Status, Equality) { |
||||
absl::Status ok; |
||||
absl::Status no_payload = absl::CancelledError("no payload"); |
||||
absl::Status one_payload = absl::InvalidArgumentError("one payload"); |
||||
one_payload.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
absl::Status two_payloads = one_payload; |
||||
two_payloads.SetPayload(kUrl2, absl::Cord(kPayload2)); |
||||
const std::array<absl::Status, 4> status_arr = {ok, no_payload, one_payload, |
||||
two_payloads}; |
||||
for (int i = 0; i < status_arr.size(); i++) { |
||||
for (int j = 0; j < status_arr.size(); j++) { |
||||
if (i == j) { |
||||
EXPECT_TRUE(status_arr[i] == status_arr[j]); |
||||
EXPECT_FALSE(status_arr[i] != status_arr[j]); |
||||
} else { |
||||
EXPECT_TRUE(status_arr[i] != status_arr[j]); |
||||
EXPECT_FALSE(status_arr[i] == status_arr[j]); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
TEST(Status, Swap) { |
||||
auto test_swap = [](const absl::Status& s1, const absl::Status& s2) { |
||||
absl::Status copy1 = s1, copy2 = s2; |
||||
swap(copy1, copy2); |
||||
EXPECT_EQ(copy1, s2); |
||||
EXPECT_EQ(copy2, s1); |
||||
}; |
||||
const absl::Status ok; |
||||
const absl::Status no_payload(absl::StatusCode::kAlreadyExists, "no payload"); |
||||
absl::Status with_payload(absl::StatusCode::kInternal, "with payload"); |
||||
with_payload.SetPayload(kUrl1, absl::Cord(kPayload1)); |
||||
test_swap(ok, no_payload); |
||||
test_swap(no_payload, ok); |
||||
test_swap(ok, with_payload); |
||||
test_swap(with_payload, ok); |
||||
test_swap(no_payload, with_payload); |
||||
test_swap(with_payload, no_payload); |
||||
} |
||||
|
||||
} // namespace
|
Loading…
Reference in new issue