| // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Illustrates how to use net::TestCompletionCallback. | |
| #include "net/base/test_completion_callback.h" | |
| #include "base/bind.h" | |
| #include "base/location.h" | |
| #include "base/logging.h" | |
| #include "base/macros.h" | |
| #include "base/single_thread_task_runner.h" | |
| #include "base/threading/thread_task_runner_handle.h" | |
| #include "net/base/completion_once_callback.h" | |
| #include "net/test/test_with_scoped_task_environment.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| #include "testing/platform_test.h" | |
| namespace net { | |
| namespace { | |
| const int kMagicResult = 8888; | |
| void CallClosureAfterCheckingResult(const base::Closure& closure, | |
| bool* did_check_result, | |
| int result) { | |
| DCHECK_EQ(result, kMagicResult); | |
| *did_check_result = true; | |
| closure.Run(); | |
| } | |
| // ExampleEmployer is a toy version of HostResolver | |
| // TODO: restore damage done in extracting example from real code | |
| // (e.g. bring back real destructor, bring back comments) | |
| class ExampleEmployer { | |
| public: | |
| ExampleEmployer(); | |
| ~ExampleEmployer(); | |
| // Posts to the current thread a task which itself posts |callback| to the | |
| // current thread. Returns true on success | |
| bool DoSomething(CompletionOnceCallback callback); | |
| private: | |
| class ExampleWorker; | |
| friend class ExampleWorker; | |
| scoped_refptr<ExampleWorker> request_; | |
| DISALLOW_COPY_AND_ASSIGN(ExampleEmployer); | |
| }; | |
| // Helper class; this is how ExampleEmployer schedules work. | |
| class ExampleEmployer::ExampleWorker | |
| : public base::RefCountedThreadSafe<ExampleWorker> { | |
| public: | |
| ExampleWorker(ExampleEmployer* employer, CompletionOnceCallback callback) | |
| : employer_(employer), callback_(std::move(callback)) {} | |
| void DoWork(); | |
| void DoCallback(); | |
| private: | |
| friend class base::RefCountedThreadSafe<ExampleWorker>; | |
| ~ExampleWorker() = default; | |
| // Only used on the origin thread (where DoSomething was called). | |
| ExampleEmployer* employer_; | |
| CompletionOnceCallback callback_; | |
| // Used to post ourselves onto the origin thread. | |
| const scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_ = | |
| base::ThreadTaskRunnerHandle::Get(); | |
| }; | |
| void ExampleEmployer::ExampleWorker::DoWork() { | |
| // In a real worker thread, some work would be done here. | |
| // Pretend it is, and send the completion callback. | |
| origin_task_runner_->PostTask(FROM_HERE, | |
| base::Bind(&ExampleWorker::DoCallback, this)); | |
| } | |
| void ExampleEmployer::ExampleWorker::DoCallback() { | |
| // Running on the origin thread. | |
| // Drop the employer_'s reference to us. Do this before running the | |
| // callback since the callback might result in the employer being | |
| // destroyed. | |
| employer_->request_ = NULL; | |
| std::move(callback_).Run(kMagicResult); | |
| } | |
| ExampleEmployer::ExampleEmployer() = default; | |
| ExampleEmployer::~ExampleEmployer() = default; | |
| bool ExampleEmployer::DoSomething(CompletionOnceCallback callback) { | |
| DCHECK(!request_.get()) << "already in use"; | |
| request_ = new ExampleWorker(this, std::move(callback)); | |
| if (!base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| FROM_HERE, base::Bind(&ExampleWorker::DoWork, request_))) { | |
| NOTREACHED(); | |
| request_ = NULL; | |
| return false; | |
| } | |
| return true; | |
| } | |
| } // namespace | |
| class TestCompletionCallbackTest : public PlatformTest, | |
| public WithScopedTaskEnvironment {}; | |
| TEST_F(TestCompletionCallbackTest, Simple) { | |
| ExampleEmployer boss; | |
| TestCompletionCallback callback; | |
| bool queued = boss.DoSomething(callback.callback()); | |
| EXPECT_TRUE(queued); | |
| int result = callback.WaitForResult(); | |
| EXPECT_EQ(result, kMagicResult); | |
| } | |
| TEST_F(TestCompletionCallbackTest, Closure) { | |
| ExampleEmployer boss; | |
| TestClosure closure; | |
| bool did_check_result = false; | |
| CompletionOnceCallback completion_callback = | |
| base::BindOnce(&CallClosureAfterCheckingResult, closure.closure(), | |
| base::Unretained(&did_check_result)); | |
| bool queued = boss.DoSomething(std::move(completion_callback)); | |
| EXPECT_TRUE(queued); | |
| EXPECT_FALSE(did_check_result); | |
| closure.WaitForResult(); | |
| EXPECT_TRUE(did_check_result); | |
| } | |
| // TODO: test deleting ExampleEmployer while work outstanding | |
| } // namespace net |