1 /*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "api/sequence_checker.h"
12
13 #include <memory>
14 #include <utility>
15
16 #include "api/function_view.h"
17 #include "api/units/time_delta.h"
18 #include "rtc_base/event.h"
19 #include "rtc_base/platform_thread.h"
20 #include "rtc_base/task_queue_for_test.h"
21 #include "test/gmock.h"
22 #include "test/gtest.h"
23
24 using testing::HasSubstr;
25
26 namespace webrtc {
27 namespace {
28
29 // This class is dead code, but its purpose is to make sure that
30 // SequenceChecker is compatible with the RTC_GUARDED_BY and RTC_RUN_ON
31 // attributes that are checked at compile-time.
32 class CompileTimeTestForGuardedBy {
33 public:
CalledOnSequence()34 int CalledOnSequence() RTC_RUN_ON(sequence_checker_) { return guarded_; }
35
CallMeFromSequence()36 void CallMeFromSequence() {
37 RTC_DCHECK_RUN_ON(&sequence_checker_);
38 guarded_ = 41;
39 }
40
41 private:
42 int guarded_ RTC_GUARDED_BY(sequence_checker_);
43 ::webrtc::SequenceChecker sequence_checker_;
44 };
45
RunOnDifferentThread(rtc::FunctionView<void ()> run)46 void RunOnDifferentThread(rtc::FunctionView<void()> run) {
47 rtc::Event thread_has_run_event;
48 rtc::PlatformThread::SpawnJoinable(
49 [&] {
50 run();
51 thread_has_run_event.Set();
52 },
53 "thread");
54 EXPECT_TRUE(thread_has_run_event.Wait(TimeDelta::Seconds(1)));
55 }
56
57 } // namespace
58
TEST(SequenceCheckerTest,CallsAllowedOnSameThread)59 TEST(SequenceCheckerTest, CallsAllowedOnSameThread) {
60 SequenceChecker sequence_checker;
61 EXPECT_TRUE(sequence_checker.IsCurrent());
62 }
63
TEST(SequenceCheckerTest,DestructorAllowedOnDifferentThread)64 TEST(SequenceCheckerTest, DestructorAllowedOnDifferentThread) {
65 auto sequence_checker = std::make_unique<SequenceChecker>();
66 RunOnDifferentThread([&] {
67 // Verify that the destructor doesn't assert when called on a different
68 // thread.
69 sequence_checker.reset();
70 });
71 }
72
TEST(SequenceCheckerTest,Detach)73 TEST(SequenceCheckerTest, Detach) {
74 SequenceChecker sequence_checker;
75 sequence_checker.Detach();
76 RunOnDifferentThread([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); });
77 }
78
TEST(SequenceCheckerTest,DetachFromThreadAndUseOnTaskQueue)79 TEST(SequenceCheckerTest, DetachFromThreadAndUseOnTaskQueue) {
80 SequenceChecker sequence_checker;
81 sequence_checker.Detach();
82 TaskQueueForTest queue;
83 queue.SendTask([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); });
84 }
85
TEST(SequenceCheckerTest,DetachFromTaskQueueAndUseOnThread)86 TEST(SequenceCheckerTest, DetachFromTaskQueueAndUseOnThread) {
87 TaskQueueForTest queue;
88 queue.SendTask([] {
89 SequenceChecker sequence_checker;
90 sequence_checker.Detach();
91 RunOnDifferentThread([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); });
92 });
93 }
94
TEST(SequenceCheckerTest,MethodNotAllowedOnDifferentThreadInDebug)95 TEST(SequenceCheckerTest, MethodNotAllowedOnDifferentThreadInDebug) {
96 SequenceChecker sequence_checker;
97 RunOnDifferentThread(
98 [&] { EXPECT_EQ(sequence_checker.IsCurrent(), !RTC_DCHECK_IS_ON); });
99 }
100
TEST(SequenceCheckerTest,MethodNotAllowedOnDifferentTaskQueueInDebug)101 TEST(SequenceCheckerTest, MethodNotAllowedOnDifferentTaskQueueInDebug) {
102 SequenceChecker sequence_checker;
103 TaskQueueForTest queue;
104 queue.SendTask(
105 [&] { EXPECT_EQ(sequence_checker.IsCurrent(), !RTC_DCHECK_IS_ON); });
106 }
107
TEST(SequenceCheckerTest,DetachFromTaskQueueInDebug)108 TEST(SequenceCheckerTest, DetachFromTaskQueueInDebug) {
109 SequenceChecker sequence_checker;
110 sequence_checker.Detach();
111
112 TaskQueueForTest queue1;
113 queue1.SendTask([&] { EXPECT_TRUE(sequence_checker.IsCurrent()); });
114
115 // IsCurrent should return false in debug builds after moving to
116 // another task queue.
117 TaskQueueForTest queue2;
118 queue2.SendTask(
119 [&] { EXPECT_EQ(sequence_checker.IsCurrent(), !RTC_DCHECK_IS_ON); });
120 }
121
TEST(SequenceCheckerTest,ExpectationToString)122 TEST(SequenceCheckerTest, ExpectationToString) {
123 TaskQueueForTest queue1;
124
125 SequenceChecker sequence_checker;
126 sequence_checker.Detach();
127
128 rtc::Event blocker;
129 queue1.PostTask([&blocker, &sequence_checker]() {
130 (void)sequence_checker.IsCurrent();
131 blocker.Set();
132 });
133
134 blocker.Wait(rtc::Event::kForever);
135
136 #if RTC_DCHECK_IS_ON
137
138 EXPECT_THAT(ExpectationToString(&sequence_checker),
139 HasSubstr("# Expected: TQ:"));
140
141 // Test for the base class
142 webrtc_sequence_checker_internal::SequenceCheckerImpl* sequence_checker_base =
143 &sequence_checker;
144 EXPECT_THAT(ExpectationToString(sequence_checker_base),
145 HasSubstr("# Expected: TQ:"));
146
147 #else
148 GTEST_ASSERT_EQ(ExpectationToString(&sequence_checker), "");
149 #endif
150 }
151
152 class TestAnnotations {
153 public:
TestAnnotations()154 TestAnnotations() : test_var_(false) {}
155
ModifyTestVar()156 void ModifyTestVar() {
157 RTC_DCHECK_RUN_ON(&checker_);
158 test_var_ = true;
159 }
160
161 private:
162 bool test_var_ RTC_GUARDED_BY(&checker_);
163 SequenceChecker checker_;
164 };
165
TEST(SequenceCheckerTest,TestAnnotations)166 TEST(SequenceCheckerTest, TestAnnotations) {
167 TestAnnotations annotations;
168 annotations.ModifyTestVar();
169 }
170
171 #if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
172
TestAnnotationsOnWrongQueue()173 void TestAnnotationsOnWrongQueue() {
174 TestAnnotations annotations;
175 TaskQueueForTest queue;
176 queue.SendTask([&] { annotations.ModifyTestVar(); });
177 }
178
179 #if RTC_DCHECK_IS_ON
180 // Note: Ending the test suite name with 'DeathTest' is important as it causes
181 // gtest to order this test before any other non-death-tests, to avoid potential
182 // global process state pollution such as shared worker threads being started
183 // (e.g. a side effect of calling InitCocoaMultiThreading() on Mac causes one or
184 // two additional threads to be created).
TEST(SequenceCheckerDeathTest,TestAnnotationsOnWrongQueueDebug)185 TEST(SequenceCheckerDeathTest, TestAnnotationsOnWrongQueueDebug) {
186 ASSERT_DEATH({ TestAnnotationsOnWrongQueue(); }, "");
187 }
188 #else
TEST(SequenceCheckerTest,TestAnnotationsOnWrongQueueRelease)189 TEST(SequenceCheckerTest, TestAnnotationsOnWrongQueueRelease) {
190 TestAnnotationsOnWrongQueue();
191 }
192 #endif
193 #endif // GTEST_HAS_DEATH_TEST
194 } // namespace webrtc
195