| // 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/statusor.h" | |
| #include <memory> | |
| #include <utility> | |
| #include <base/functional/bind.h> | |
| #include <base/functional/callback_forward.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 <base/types/expected.h> | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| #include "missive/util/status.h" | |
| #include "missive/util/test_support_callbacks.h" | |
| using ::testing::AllOf; | |
| using ::testing::Eq; | |
| using ::testing::Property; | |
| using ::testing::StrEq; | |
| namespace reporting { | |
| namespace { | |
| TEST(StatusOr, MoveConstructFromAndExtractToStatusImplicitly) { | |
| Status status(error::INTERNAL, "internal error"); | |
| base::unexpected<Status> unexpected_status(status); | |
| StatusOr<int> status_or(std::move(unexpected_status)); | |
| Status extracted_status{std::move(status_or).error()}; | |
| EXPECT_EQ(status, extracted_status); | |
| } | |
| TEST(StatusOr, CopyConstructFromAndExtractToStatusImplicitly) { | |
| Status status(error::INTERNAL, "internal error"); | |
| base::unexpected<Status> unexpected_status(status); | |
| StatusOr<int> status_or(unexpected_status); | |
| Status extracted_status{status_or.error()}; | |
| EXPECT_EQ(status, extracted_status); | |
| } | |
| TEST(StatusOr, CallbackAfterDeletion) { | |
| base::test::TaskEnvironment test_env{}; | |
| class Handler { | |
| public: | |
| using Result = StatusOr<int64_t>; | |
| Handler() = default; | |
| Handler(const Handler&) = delete; | |
| Handler& operator=(const Handler&) = delete; | |
| void Handle(base::OnceCallback<void(Result)> done_cb) { | |
| std::move(done_cb).Run(12345L); | |
| } | |
| 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<Handler::Result> cb_dead; | |
| base::OnceClosure async_cb_dead; | |
| { | |
| std::unique_ptr<Handler, base::OnTaskRunnerDeleter> handler{ | |
| new Handler, base::OnTaskRunnerDeleter(task_runner)}; | |
| test::TestEvent<Handler::Result> cb_alive; | |
| task_runner->PostTask( | |
| FROM_HERE, | |
| base::BindOnce(&Handler::Handle, handler->GetWeakPtr(), cb_alive.cb())); | |
| const auto result = cb_alive.result(); | |
| ASSERT_TRUE(result.has_value()) << result.error(); | |
| EXPECT_THAT(result.value(), Eq(12345L)); | |
| async_cb_dead = base::BindOnce( | |
| &Handler::Handle, handler->GetWeakPtr(), | |
| Scoped<Handler::Result>( | |
| cb_dead.cb(), base::unexpected(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. | |
| const auto result = cb_dead.result(); | |
| EXPECT_FALSE(result.has_value()); | |
| EXPECT_THAT( | |
| result.error(), | |
| AllOf(Property(&Status::error_code, Eq(error::UNAVAILABLE)), | |
| Property(&Status::error_message, StrEq("Handler destructed")))); | |
| } | |
| } // namespace | |
| } // namespace reporting |