File: test_completion_callback_unittest.cc

package info (click to toggle)
chromium 120.0.6099.224-1~deb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,112,112 kB
  • sloc: cpp: 32,907,025; ansic: 8,148,123; javascript: 3,679,536; python: 2,031,248; asm: 959,718; java: 804,675; xml: 617,256; sh: 111,417; objc: 100,835; perl: 88,443; cs: 53,032; makefile: 29,579; fortran: 24,137; php: 21,162; tcl: 21,147; sql: 20,809; ruby: 17,735; pascal: 12,864; yacc: 8,045; lisp: 3,388; lex: 1,323; ada: 727; awk: 329; jsp: 267; csh: 117; exp: 43; sed: 37
file content (143 lines) | stat: -rw-r--r-- 4,456 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright 2011 The Chromium Authors
// 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/check_op.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/notreached.h"
#include "base/task/single_thread_task_runner.h"
#include "net/base/completion_once_callback.h"
#include "net/test/test_with_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"

namespace net {

namespace {

const int kMagicResult = 8888;

void CallClosureAfterCheckingResult(base::OnceClosure closure,
                                    bool* did_check_result,
                                    int result) {
  DCHECK_EQ(result, kMagicResult);
  *did_check_result = true;
  std::move(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(const ExampleEmployer&) = delete;
  ExampleEmployer& operator=(const ExampleEmployer&) = delete;
  ~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_;
};

// 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).
  raw_ptr<ExampleEmployer> employer_;
  CompletionOnceCallback callback_;
  // Used to post ourselves onto the origin thread.
  const scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_ =
      base::SingleThreadTaskRunner::GetCurrentDefault();
};

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::BindOnce(&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_ = nullptr;

  std::move(callback_).Run(kMagicResult);
}

ExampleEmployer::ExampleEmployer() = default;

ExampleEmployer::~ExampleEmployer() = default;

bool ExampleEmployer::DoSomething(CompletionOnceCallback callback) {
  DCHECK(!request_.get()) << "already in use";

  request_ = base::MakeRefCounted<ExampleWorker>(this, std::move(callback));

  if (!base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
          FROM_HERE, base::BindOnce(&ExampleWorker::DoWork, request_))) {
    NOTREACHED();
    request_ = nullptr;
    return false;
  }

  return true;
}

}  // namespace

class TestCompletionCallbackTest : public PlatformTest,
                                   public WithTaskEnvironment {};

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