blob: f94bfc8dd59eb7352e635bcd76871523aa2be71f [file] [log] [blame]
Kyle Qiana01e9302018-08-25 00:07:421// Copyright (c) 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chromeos/components/nearby/condition_variable_impl.h"
6
7#include <memory>
8
9#include "base/single_thread_task_runner.h"
10#include "base/task/post_task.h"
11#include "base/test/gtest_util.h"
12#include "base/test/scoped_task_environment.h"
13#include "base/test/test_timeouts.h"
14#include "base/threading/platform_thread.h"
15#include "base/threading/thread_restrictions.h"
16#include "chromeos/components/nearby/lock_base.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19namespace chromeos {
20
21namespace nearby {
22
23namespace {
24
25class FakeLock : public LockBase {
26 public:
27 FakeLock() : condition_variable_(&lock_) {}
28 ~FakeLock() override = default;
29
30 // location::nearby::Lock:
31 void lock() override {
32 base::AutoLock al(lock_);
33 ++num_locks_;
34 condition_variable_.Signal();
35 }
36
37 void unlock() override {
38 base::AutoLock al(lock_);
39 ++num_unlocks_;
40 condition_variable_.Signal();
41 }
42
43 // chromeos::nearby::LockBase:
44 bool IsHeldByCurrentThread() override { return is_held_by_current_thread_; }
45
46 void set_is_held_by_current_thread(bool value) {
47 is_held_by_current_thread_ = value;
48 }
49
50 int num_locks() {
51 base::AutoLock al(lock_);
52 return num_locks_;
53 }
54
55 int num_unlocks() {
56 base::AutoLock al(lock_);
57 return num_unlocks_;
58 }
59
60 void WaitForLocksAndUnlocks(int expected_num_locks,
61 int expected_num_unlocks) {
62 base::AutoLock al(lock_);
63 while (expected_num_locks != num_locks_ ||
64 expected_num_unlocks != num_unlocks_) {
65 condition_variable_.Wait();
66 }
67 }
68
69 private:
70 base::Lock lock_;
71 base::ConditionVariable condition_variable_;
72 bool is_held_by_current_thread_ = false;
73 int num_locks_ = 0;
74 int num_unlocks_ = 0;
75
76 DISALLOW_COPY_AND_ASSIGN(FakeLock);
77};
78
79} // namespace
80
81class ConditionVariableImplTest : public testing::Test {
82 protected:
83 ConditionVariableImplTest()
84 : fake_lock_(std::make_unique<FakeLock>()),
85 condition_variable_(
86 std::make_unique<ConditionVariableImpl>(fake_lock_.get())) {}
87
88 // testing::Test
89 void TearDown() override {
90 scoped_task_environment_.RunUntilIdle();
91 EXPECT_EQ(fake_lock_->num_locks(), fake_lock_->num_unlocks());
92 }
93
94 location::nearby::ConditionVariable* condition_variable() {
95 return condition_variable_.get();
96 }
97
98 FakeLock* fake_lock() { return fake_lock_.get(); }
99
100 void WaitOnConditionVariableFromParallelSequence(bool should_succeed) {
101 base::PostTask(
102 FROM_HERE,
103 base::BindOnce(&ConditionVariableImplTest::WaitOnConditionVariable,
104 base::Unretained(this), should_succeed));
105 }
106
107 // Invoked whenever attempting to verify that a parallel task has indeed
108 // blocked, since there's no way to deterministically find out if that task
109 // will ever unblock.
110 void TinyTimeout() {
111 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
112 }
113
114 void VerifyNumLocksAndUnlocks(int expected_num_locks,
115 int expected_num_unlocks) {
116 EXPECT_EQ(expected_num_locks, fake_lock_->num_locks());
117 EXPECT_EQ(expected_num_unlocks, fake_lock_->num_unlocks());
118 }
119
120 // To ensure that |condition_variable_| is blocking as expected, wait until
121 // its |fake_lock_| has been locked and unlocked once per blocked sequence.
122 // Then manually wait, and verify afterwards that the number of locks and
123 // unlocks remain unchanged.
124 void VerifyBlockedConditionVariable(int expected_num_blocked_sequences) {
125 fake_lock()->WaitForLocksAndUnlocks(
126 expected_num_blocked_sequences /* expected_num_locks */,
127 expected_num_blocked_sequences /* expected_num_unlocks */);
128 TinyTimeout();
129 VerifyNumLocksAndUnlocks(
130 expected_num_blocked_sequences /* expected_num_locks */,
131 expected_num_blocked_sequences /* expected_num_unlocks */);
132 }
133
134 base::test::ScopedTaskEnvironment scoped_task_environment_;
135
136 private:
137 void WaitOnConditionVariable(bool should_succeed) {
138 const base::ScopedAllowBaseSyncPrimitivesForTesting allow_sync_primitives;
139
140 // ConditionVariable::wait() should only be called by a thread that already
141 // owns the associated Lock. This is not tested as the expected behavior is
142 // either undefined or specified by the particular Lock implementation.
143 fake_lock_->lock();
144
145 if (should_succeed)
146 condition_variable_->wait();
147 else
148 EXPECT_DCHECK_DEATH(condition_variable_->wait());
149
150 // ConditionVariable::wait() will call Lock::lock() after unblocking, so
151 // this undoes that.
152 fake_lock_->unlock();
153 }
154
155 std::unique_ptr<FakeLock> fake_lock_;
156 std::unique_ptr<location::nearby::ConditionVariable> condition_variable_;
157
158 DISALLOW_COPY_AND_ASSIGN(ConditionVariableImplTest);
159};
160
161TEST_F(ConditionVariableImplTest,
162 SingleSequence_BlocksOnWaitAndUnblocksOnNotify) {
163 WaitOnConditionVariableFromParallelSequence(true /* should_succeed */);
164 VerifyBlockedConditionVariable(1 /* expected_num_blocked_sequences */);
165
166 // Should unblock after notify().
167 condition_variable()->notify();
168 scoped_task_environment_.RunUntilIdle();
169 VerifyNumLocksAndUnlocks(2 /* expected_num_locks */,
170 2 /* expected_num_unlocks */);
171}
172
173TEST_F(ConditionVariableImplTest,
174 MultipleSequences_BlocksOnWaitAndUnblocksOnNotify) {
175 WaitOnConditionVariableFromParallelSequence(true /* should_succeed */);
176 WaitOnConditionVariableFromParallelSequence(true /* should_succeed */);
177 WaitOnConditionVariableFromParallelSequence(true /* should_succeed */);
178 VerifyBlockedConditionVariable(3 /* expected_num_blocked_sequences */);
179
180 // All should unblock after notify().
181 condition_variable()->notify();
182 scoped_task_environment_.RunUntilIdle();
183 VerifyNumLocksAndUnlocks(6 /* expected_num_locks */,
184 6 /* expected_num_unlocks */);
185}
186
187TEST_F(ConditionVariableImplTest, ThreadCannotWaitIfStillOwnsLock) {
188 fake_lock()->set_is_held_by_current_thread(true);
189 WaitOnConditionVariableFromParallelSequence(false /* should_succeed */);
190}
191
192} // namespace nearby
193
194} // namespace chromeos