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 }
106
107 return true;
108 }
109
110 } // namespace
111
112 class TestCompletionCallbackTest : public PlatformTest,
113 public WithTaskEnvironment {};
114
TEST_F(TestCompletionCallbackTest,Simple)115 TEST_F(TestCompletionCallbackTest, Simple) {
116 ExampleEmployer boss;
117 TestCompletionCallback callback;
118 bool queued = boss.DoSomething(callback.callback());
119 EXPECT_TRUE(queued);
120 int result = callback.WaitForResult();
121 EXPECT_EQ(result, kMagicResult);
122 }
123
TEST_F(TestCompletionCallbackTest,Closure)124 TEST_F(TestCompletionCallbackTest, Closure) {
125 ExampleEmployer boss;
126 TestClosure closure;
127 bool did_check_result = false;
128 CompletionOnceCallback completion_callback =
129 base::BindOnce(&CallClosureAfterCheckingResult, closure.closure(),
130 base::Unretained(&did_check_result));
131 bool queued = boss.DoSomething(std::move(completion_callback));
132 EXPECT_TRUE(queued);
133
134 EXPECT_FALSE(did_check_result);
135 closure.WaitForResult();
136 EXPECT_TRUE(did_check_result);
137 }
138
139 // TODO: test deleting ExampleEmployer while work outstanding
140
141 } // namespace net
142