| // Copyright 2021 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "missive/util/status.h" | |
| #include <memory> | |
| #include <utility> | |
| #include <base/functional/bind.h> | |
| #include <base/functional/callback_forward.h> | |
| #include <base/logging.h> | |
| #include <base/memory/weak_ptr.h> | |
| #include <base/task/sequenced_task_runner.h> | |
| #include <base/task/thread_pool.h> | |
| #include <base/test/task_environment.h> | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| #include "missive/proto/status.pb.h" | |
| #include "missive/util/status_macros.h" | |
| #include "missive/util/test_support_callbacks.h" | |
| using ::testing::AllOf; | |
| using ::testing::Eq; | |
| using ::testing::Property; | |
| using ::testing::StrEq; | |
| namespace reporting { | |
| namespace { | |
| TEST(Status, Empty) { | |
| Status status; | |
| EXPECT_EQ(error::OK, status.error_code()); | |
| EXPECT_EQ(error::OK, status.code()); | |
| EXPECT_EQ("OK", status.ToString()); | |
| } | |
| TEST(Status, GenericCodes) { | |
| EXPECT_EQ(error::OK, Status::StatusOK().error_code()); | |
| EXPECT_EQ(error::OK, Status::StatusOK().code()); | |
| EXPECT_EQ("OK", Status::StatusOK().ToString()); | |
| } | |
| TEST(Status, OkConstructorIgnoresMessage) { | |
| Status status(error::OK, "msg"); | |
| EXPECT_TRUE(status.ok()); | |
| EXPECT_EQ("OK", status.ToString()); | |
| } | |
| TEST(Status, CheckOK) { | |
| Status status; | |
| CHECK_OK(status); | |
| CHECK_OK(status) << "Failed"; | |
| // DCHECK is ok to use here since we're just verifying functionality of Status | |
| // to work with all types of checks. | |
| DCHECK_OK(status) << "Failed"; | |
| } | |
| TEST(Status, ErrorMessage) { | |
| Status status(error::INVALID_ARGUMENT, ""); | |
| EXPECT_FALSE(status.ok()); | |
| EXPECT_EQ("", status.error_message()); | |
| EXPECT_EQ("", status.message()); | |
| EXPECT_EQ("INVALID_ARGUMENT", status.ToString()); | |
| status = Status(error::INVALID_ARGUMENT, "msg"); | |
| EXPECT_FALSE(status.ok()); | |
| EXPECT_EQ("msg", status.error_message()); | |
| EXPECT_EQ("msg", status.message()); | |
| EXPECT_EQ("INVALID_ARGUMENT:msg", status.ToString()); | |
| status = Status(error::OK, "msg"); | |
| EXPECT_TRUE(status.ok()); | |
| EXPECT_EQ("", status.error_message()); | |
| EXPECT_EQ("", status.message()); | |
| EXPECT_EQ("OK", status.ToString()); | |
| } | |
| TEST(Status, Copy) { | |
| Status a(error::UNKNOWN, "message"); | |
| Status b(a); | |
| EXPECT_EQ(a.ToString(), b.ToString()); | |
| } | |
| TEST(Status, Assign) { | |
| Status a(error::UNKNOWN, "message"); | |
| Status b; | |
| b = a; | |
| EXPECT_EQ(a.ToString(), b.ToString()); | |
| } | |
| TEST(Status, AssignEmpty) { | |
| Status a(error::UNKNOWN, "message"); | |
| Status b; | |
| a = b; | |
| EXPECT_EQ(std::string("OK"), a.ToString()); | |
| EXPECT_TRUE(b.ok()); | |
| EXPECT_TRUE(a.ok()); | |
| } | |
| TEST(Status, EqualsOK) { | |
| EXPECT_EQ(Status::StatusOK(), Status()); | |
| } | |
| TEST(Status, EqualsSame) { | |
| const Status a = Status(error::CANCELLED, "message"); | |
| const Status b = Status(error::CANCELLED, "message"); | |
| EXPECT_EQ(a, b); | |
| } | |
| TEST(Status, EqualsCopy) { | |
| const Status a = Status(error::CANCELLED, "message"); | |
| const Status b = a; | |
| EXPECT_EQ(a, b); | |
| } | |
| TEST(Status, EqualsDifferentCode) { | |
| const Status a = Status(error::CANCELLED, "message"); | |
| const Status b = Status(error::UNKNOWN, "message"); | |
| EXPECT_NE(a, b); | |
| } | |
| TEST(Status, EqualsDifferentMessage) { | |
| const Status a = Status(error::CANCELLED, "message"); | |
| const Status b = Status(error::CANCELLED, "another"); | |
| EXPECT_NE(a, b); | |
| } | |
| TEST(Status, SaveOkTo) { | |
| StatusProto status_proto; | |
| Status::StatusOK().SaveTo(&status_proto); | |
| EXPECT_EQ(status_proto.code(), error::OK); | |
| EXPECT_TRUE(status_proto.error_message().empty()) | |
| << status_proto.error_message(); | |
| } | |
| TEST(Status, SaveTo) { | |
| Status status(error::INVALID_ARGUMENT, "argument error"); | |
| StatusProto status_proto; | |
| status.SaveTo(&status_proto); | |
| EXPECT_EQ(status_proto.code(), status.error_code()); | |
| EXPECT_EQ(status_proto.error_message(), status.error_message()); | |
| } | |
| TEST(Status, RestoreFromOk) { | |
| StatusProto status_proto; | |
| status_proto.set_code(error::OK); | |
| status_proto.set_error_message("invalid error"); | |
| Status status; | |
| status.RestoreFrom(status_proto); | |
| EXPECT_EQ(status.error_code(), status_proto.code()); | |
| // Error messages are ignored for OK status objects. | |
| EXPECT_TRUE(status.error_message().empty()) << status.error_message(); | |
| EXPECT_TRUE(status.ok()); | |
| } | |
| TEST(Status, RestoreFromNonOk) { | |
| StatusProto status_proto; | |
| status_proto.set_code(error::INVALID_ARGUMENT); | |
| status_proto.set_error_message("argument error"); | |
| Status status; | |
| status.RestoreFrom(status_proto); | |
| EXPECT_EQ(status.error_code(), status_proto.code()); | |
| EXPECT_EQ(status.error_message(), status_proto.error_message()); | |
| } | |
| TEST(Status, ConvertStatusToString) { | |
| const std::pair<Status, const char*> status_pairs[] = { | |
| {Status::StatusOK(), "OK"}, | |
| {Status(error::CANCELLED, "Cancelled"), "CANCELLED:Cancelled"}, | |
| {Status(error::UNKNOWN, "Unknown"), "UNKNOWN:Unknown"}, | |
| {Status(error::INVALID_ARGUMENT, "Invalid argument"), | |
| "INVALID_ARGUMENT:Invalid argument"}, | |
| {Status(error::DEADLINE_EXCEEDED, "Deadline exceeded"), | |
| "DEADLINE_EXCEEDED:Deadline exceeded"}, | |
| {Status(error::NOT_FOUND, "Not found"), "NOT_FOUND:Not found"}, | |
| {Status(error::ALREADY_EXISTS, "Already exists"), | |
| "ALREADY_EXISTS:Already exists"}, | |
| {Status(error::PERMISSION_DENIED, "Permission denied"), | |
| "PERMISSION_DENIED:Permission denied"}, | |
| {Status(error::UNAUTHENTICATED, "Unathenticated"), | |
| "UNAUTHENTICATED:Unathenticated"}, | |
| {Status(error::RESOURCE_EXHAUSTED, "Resourse exhausted"), | |
| "RESOURCE_EXHAUSTED:Resourse exhausted"}, | |
| {Status(error::FAILED_PRECONDITION, "Failed precondition"), | |
| "FAILED_PRECONDITION:Failed precondition"}, | |
| {Status(error::ABORTED, "Aborted"), "ABORTED:Aborted"}, | |
| {Status(error::OUT_OF_RANGE, "Out of range"), | |
| "OUT_OF_RANGE:Out of range"}, | |
| {Status(error::UNIMPLEMENTED, "Unimplemented"), | |
| "UNIMPLEMENTED:Unimplemented"}, | |
| {Status(error::INTERNAL, "Internal"), "INTERNAL:Internal"}, | |
| {Status(error::UNAVAILABLE, "Unavailable"), "UNAVAILABLE:Unavailable"}, | |
| {Status(error::DATA_LOSS, "Data loss"), "DATA_LOSS:Data loss"}, | |
| }; | |
| for (const auto& p : status_pairs) { | |
| LOG(INFO) << p.first; | |
| EXPECT_THAT(p.first.ToString(), StrEq(p.second)); | |
| } | |
| } | |
| TEST(Status, CallbackAfterDeletion) { | |
| base::test::TaskEnvironment test_env{}; | |
| class Handler { | |
| public: | |
| Handler() = default; | |
| Handler(const Handler&) = delete; | |
| Handler& operator=(const Handler&) = delete; | |
| void Handle(base::OnceCallback<void(Status)> done_cb) { | |
| std::move(done_cb).Run(Status::StatusOK()); | |
| } | |
| base::WeakPtr<Handler> GetWeakPtr() { | |
| return weak_ptr_factory_.GetWeakPtr(); | |
| } | |
| private: | |
| base::WeakPtrFactory<Handler> weak_ptr_factory_{this}; | |
| }; | |
| // Create seq task runner as required by weak pointers. | |
| auto task_runner = base::ThreadPool::CreateSequencedTaskRunner({}); | |
| test::TestEvent<Status> cb_dead; | |
| base::OnceClosure async_cb_dead; | |
| { | |
| std::unique_ptr<Handler, base::OnTaskRunnerDeleter> handler{ | |
| new Handler, base::OnTaskRunnerDeleter(task_runner)}; | |
| test::TestEvent<Status> cb_alive; | |
| task_runner->PostTask( | |
| FROM_HERE, | |
| base::BindOnce(&Handler::Handle, handler->GetWeakPtr(), cb_alive.cb())); | |
| EXPECT_OK(cb_alive.result()); | |
| async_cb_dead = base::BindOnce( | |
| &Handler::Handle, handler->GetWeakPtr(), | |
| Scoped<Status>(cb_dead.cb(), | |
| Status(error::UNAVAILABLE, "Handler destructed"))); | |
| } | |
| // Out of scope: run after Handler has been deleted (on sequence!). | |
| task_runner->PostTask(FROM_HERE, std::move(async_cb_dead)); | |
| // Make sure callback is still invoked, but with error. | |
| EXPECT_THAT( | |
| cb_dead.result(), | |
| AllOf(Property(&Status::error_code, Eq(error::UNAVAILABLE)), | |
| Property(&Status::error_message, StrEq("Handler destructed")))); | |
| } | |
| } // namespace | |
| } // namespace reporting |