1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Illustrates how to use net::TestCompletionCallback.
6
7 #include "net/base/test_completion_callback.h"
8
9 #include "base/check_op.h"
10 #include "base/functional/bind.h"
11 #include "base/location.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/notreached.h"
14 #include "base/task/single_thread_task_runner.h"
15 #include "net/base/completion_once_callback.h"
16 #include "net/test/test_with_task_environment.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "testing/platform_test.h"
19
20 namespace net {
21
22 namespace {
23
24 const int kMagicResult = 8888;
25
CallClosureAfterCheckingResult(base::OnceClosure closure,bool * did_check_result,int result)26 void CallClosureAfterCheckingResult(base::OnceClosure closure,
27 bool* did_check_result,
28 int result) {
29 DCHECK_EQ(result, kMagicResult);
30 *did_check_result = true;
31 std::move(closure).Run();
32 }
33
34 // ExampleEmployer is a toy version of HostResolver
35 // TODO: restore damage done in extracting example from real code
36 // (e.g. bring back real destructor, bring back comments)
37 class ExampleEmployer {
38 public:
39 ExampleEmployer();
40 ExampleEmployer(const ExampleEmployer&) = delete;
41 ExampleEmployer& operator=(const ExampleEmployer&) = delete;
42 ~ExampleEmployer();
43
44 // Posts to the current thread a task which itself posts |callback| to the
45 // current thread. Returns true on success
46 bool DoSomething(CompletionOnceCallback callback);
47
48 private:
49 class ExampleWorker;
50 friend class ExampleWorker;
51 scoped_refptr<ExampleWorker> request_;
52 };
53
54 // Helper class; this is how ExampleEmployer schedules work.
55 class ExampleEmployer::ExampleWorker
56 : public base::RefCountedThreadSafe<ExampleWorker> {
57 public:
ExampleWorker(ExampleEmployer * employer,CompletionOnceCallback callback)58 ExampleWorker(ExampleEmployer* employer, CompletionOnceCallback callback)
59 : employer_(employer), callback_(std::move(callback)) {}
60 void DoWork();
61 void DoCallback();
62 private:
63 friend class base::RefCountedThreadSafe<ExampleWorker>;
64
65 ~ExampleWorker() = default;
66
67 // Only used on the origin thread (where DoSomething was called).
68 raw_ptr<ExampleEmployer> employer_;
69 CompletionOnceCallback callback_;
70 // Used to post ourselves onto the origin thread.
71 const scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_ =
72 base::SingleThreadTaskRunner::GetCurrentDefault();
73 };
74
DoWork()75 void ExampleEmployer::ExampleWorker::DoWork() {
76 // In a real worker thread, some work would be done here.
77 // Pretend it is, and send the completion callback.
78 origin_task_runner_->PostTask(
79 FROM_HERE, base::BindOnce(&ExampleWorker::DoCallback, this));
80 }
81
DoCallback()82 void ExampleEmployer::ExampleWorker::DoCallback() {
83 // Running on the origin thread.
84
85 // Drop the employer_'s reference to us. Do this before running the
86 // callback since the callback might result in the employer being
87 // destroyed.
88 employer_->request_ = nullptr;
89
90 std::move(callback_).Run(kMagicResult);
91 }
92
93 ExampleEmployer::ExampleEmployer() = default;
94
95 ExampleEmployer::~ExampleEmployer() = default;
96
DoSomething(CompletionOnceCallback callback)97 bool ExampleEmployer::DoSomething(CompletionOnceCallback callback) {
98 DCHECK(!request_.get()) << "already in use";
99
100 request_ = base::MakeRefCounted<ExampleWorker>(this, std::move(callback));
101
102 if (!base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
103 FROM_HERE, base::BindOnce(&ExampleWorker::DoWork, request_))) {
104 NOTREACHED();
105 request_ = nullptr;
106 return false;
107 }
108
109 return true;
110 }
111
112 } // namespace
113
114 class TestCompletionCallbackTest : public PlatformTest,
115 public WithTaskEnvironment {};
116
TEST_F(TestCompletionCallbackTest,Simple)117 TEST_F(TestCompletionCallbackTest, Simple) {
118 ExampleEmployer boss;
119 TestCompletionCallback callback;
120 bool queued = boss.DoSomething(callback.callback());
121 EXPECT_TRUE(queued);
122 int result = callback.WaitForResult();
123 EXPECT_EQ(result, kMagicResult);
124 }
125
TEST_F(TestCompletionCallbackTest,Closure)126 TEST_F(TestCompletionCallbackTest, Closure) {
127 ExampleEmployer boss;
128 TestClosure closure;
129 bool did_check_result = false;
130 CompletionOnceCallback completion_callback =
131 base::BindOnce(&CallClosureAfterCheckingResult, closure.closure(),
132 base::Unretained(&did_check_result));
133 bool queued = boss.DoSomething(std::move(completion_callback));
134 EXPECT_TRUE(queued);
135
136 EXPECT_FALSE(did_check_result);
137 closure.WaitForResult();
138 EXPECT_TRUE(did_check_result);
139 }
140
141 // TODO: test deleting ExampleEmployer while work outstanding
142
143 } // namespace net
144