| Flutter Impeller |

base_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter 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 "flutter/testing/testing.h"
6 #include "impeller/base/mask.h"
7 #include "impeller/base/promise.h"
8 #include "impeller/base/strings.h"
9 #include "impeller/base/thread.h"
10
11 namespace impeller {
12
13 enum class MyMaskBits : uint32_t {
14 kFoo = 0,
15 kBar = 1 << 0,
16 kBaz = 1 << 1,
17 kBang = 1 << 2,
18 };
19
20 using MyMask = Mask<MyMaskBits>;
21
22 IMPELLER_ENUM_IS_MASK(MyMaskBits);
23
24 namespace testing {
25
26 struct Foo {
27 Mutex mtx;
28 int a IPLR_GUARDED_BY(mtx);
29 };
30
31 struct RWFoo {
32 RWMutex mtx;
33 int a IPLR_GUARDED_BY(mtx);
34 };
35
36 TEST(ThreadTest, CanCreateMutex) {
37 Foo f = {};
38
39 // f.a = 100; <--- Static analysis error.
40 f.mtx.Lock();
41 f.a = 100;
42 f.mtx.Unlock();
43 }
44
45 TEST(ThreadTest, CanCreateMutexLock) {
46 Foo f = {};
47
48 // f.a = 100; <--- Static analysis error.
49 auto a = Lock(f.mtx);
50 f.a = 100;
51 }
52
53 TEST(ThreadTest, CanCreateRWMutex) {
54 RWFoo f = {};
55
56 // f.a = 100; <--- Static analysis error.
57 f.mtx.LockWriter();
58 f.a = 100;
59 f.mtx.UnlockWriter();
60 // int b = f.a; <--- Static analysis error.
61 f.mtx.LockReader();
62 int b = f.a; // NOLINT(clang-analyzer-deadcode.DeadStores)
63 FML_ALLOW_UNUSED_LOCAL(b);
64 f.mtx.UnlockReader();
65 }
66
67 TEST(ThreadTest, CanCreateRWMutexLock) {
68 RWFoo f = {};
69
70 // f.a = 100; <--- Static analysis error.
71 {
72 auto write_lock = WriterLock{f.mtx};
73 f.a = 100;
74 }
75
76 // int b = f.a; <--- Static analysis error.
77 {
78 auto read_lock = ReaderLock(f.mtx);
79 int b = f.a; // NOLINT(clang-analyzer-deadcode.DeadStores)
80 FML_ALLOW_UNUSED_LOCAL(b);
81 }
82
83 // f.mtx.UnlockReader(); <--- Static analysis error.
84 }
85
86 TEST(StringsTest, CanSPrintF) {
87 ASSERT_EQ(SPrintF("%sx%d", "Hello", 12), "Hellox12");
88 ASSERT_EQ(SPrintF(""), "");
89 ASSERT_EQ(SPrintF("Hello"), "Hello");
90 ASSERT_EQ(SPrintF("%sx%.2f", "Hello", 12.122222), "Hellox12.12");
91 }
92
93 struct CVTest {
94 Mutex mutex;
95 ConditionVariable cv;
96 uint32_t rando_ivar IPLR_GUARDED_BY(mutex) = 0;
97 };
98
99 TEST(ConditionVariableTest, WaitUntil) {
100 CVTest test;
101 // test.rando_ivar = 12; // <--- Static analysis error
102 for (size_t i = 0; i < 2; ++i) {
103 test.mutex.Lock(); // <--- Static analysis error without this.
104 auto result = test.cv.WaitUntil(
105 test.mutex,
106 std::chrono::high_resolution_clock::now() +
107 std::chrono::milliseconds{10},
108 [&]() IPLR_REQUIRES(test.mutex) {
109 test.rando_ivar = 12; // <-- Static analysics error without the
110 // IPLR_REQUIRES on the pred.
111 return false;
112 });
113 test.mutex.Unlock();
114 ASSERT_FALSE(result);
115 }
116 Lock lock(test.mutex); // <--- Static analysis error without this.
117 // The predicate never returns true. So return has to be due to a non-spurious
118 // wake.
119 ASSERT_EQ(test.rando_ivar, 12u);
120 }
121
122 TEST(ConditionVariableTest, WaitFor) {
123 CVTest test;
124 // test.rando_ivar = 12; // <--- Static analysis error
125 for (size_t i = 0; i < 2; ++i) {
126 test.mutex.Lock(); // <--- Static analysis error without this.
127 auto result = test.cv.WaitFor(
128 test.mutex, std::chrono::milliseconds{10},
129 [&]() IPLR_REQUIRES(test.mutex) {
130 test.rando_ivar = 12; // <-- Static analysics error without the
131 // IPLR_REQUIRES on the pred.
132 return false;
133 });
134 test.mutex.Unlock();
135 ASSERT_FALSE(result);
136 }
137 Lock lock(test.mutex); // <--- Static analysis error without this.
138 // The predicate never returns true. So return has to be due to a non-spurious
139 // wake.
140 ASSERT_EQ(test.rando_ivar, 12u);
141 }
142
143 TEST(ConditionVariableTest, WaitForever) {
144 CVTest test;
145 // test.rando_ivar = 12; // <--- Static analysis error
146 for (size_t i = 0; i < 2; ++i) {
147 test.mutex.Lock(); // <--- Static analysis error without this.
148 test.cv.Wait(test.mutex, [&]() IPLR_REQUIRES(test.mutex) {
149 test.rando_ivar = 12; // <-- Static analysics error without
150 // the IPLR_REQUIRES on the pred.
151 return true;
152 });
153 test.mutex.Unlock();
154 }
155 Lock lock(test.mutex); // <--- Static analysis error without this.
156 // The wake only happens when the predicate returns true.
157 ASSERT_EQ(test.rando_ivar, 12u);
158 }
159
160 TEST(ConditionVariableTest, TestsCriticalSectionAfterWaitForUntil) {
161 std::vector<std::thread> threads;
162 const auto kThreadCount = 10u;
163
164 Mutex mtx;
165 ConditionVariable cv;
166 size_t sum = 0u;
167
168 std::condition_variable start_cv;
169 std::mutex start_mtx;
170 bool start = false;
171 auto start_predicate = [&start]() { return start; };
172 auto thread_main = [&]() {
173 {
174 std::unique_lock start_lock(start_mtx);
175 start_cv.wait(start_lock, start_predicate);
176 }
177
178 mtx.Lock();
179 cv.WaitFor(mtx, std::chrono::milliseconds{0u}, []() { return true; });
180 auto old_val = sum;
181 std::this_thread::sleep_for(std::chrono::milliseconds{100u});
182 sum = old_val + 1u;
183 mtx.Unlock();
184 };
185 // Launch all threads. They will wait for the start CV to be signaled.
186 for (size_t i = 0; i < kThreadCount; i++) {
187 threads.emplace_back(thread_main);
188 }
189 // Notify all threads that the test may start.
190 {
191 {
192 std::scoped_lock start_lock(start_mtx);
193 start = true;
194 }
195 start_cv.notify_all();
196 }
197 // Join all threads.
198 ASSERT_EQ(threads.size(), kThreadCount);
199 for (size_t i = 0; i < kThreadCount; i++) {
200 threads[i].join();
201 }
202 ASSERT_EQ(sum, kThreadCount);
203 }
204
205 TEST(ConditionVariableTest, TestsCriticalSectionAfterWait) {
206 std::vector<std::thread> threads;
207 const auto kThreadCount = 10u;
208
209 Mutex mtx;
210 ConditionVariable cv;
211 size_t sum = 0u;
212
213 std::condition_variable start_cv;
214 std::mutex start_mtx;
215 bool start = false;
216 auto start_predicate = [&start]() { return start; };
217 auto thread_main = [&]() {
218 {
219 std::unique_lock start_lock(start_mtx);
220 start_cv.wait(start_lock, start_predicate);
221 }
222
223 mtx.Lock();
224 cv.Wait(mtx, []() { return true; });
225 auto old_val = sum;
226 std::this_thread::sleep_for(std::chrono::milliseconds{100u});
227 sum = old_val + 1u;
228 mtx.Unlock();
229 };
230 // Launch all threads. They will wait for the start CV to be signaled.
231 for (size_t i = 0; i < kThreadCount; i++) {
232 threads.emplace_back(thread_main);
233 }
234 // Notify all threads that the test may start.
235 {
236 {
237 std::scoped_lock start_lock(start_mtx);
238 start = true;
239 }
240 start_cv.notify_all();
241 }
242 // Join all threads.
243 ASSERT_EQ(threads.size(), kThreadCount);
244 for (size_t i = 0; i < kThreadCount; i++) {
245 threads[i].join();
246 }
247 ASSERT_EQ(sum, kThreadCount);
248 }
249
250 TEST(BaseTest, NoExceptionPromiseValue) {
251 NoExceptionPromise<int> wrapper;
252 std::future future = wrapper.get_future();
253 wrapper.set_value(123);
254 ASSERT_EQ(future.get(), 123);
255 }
256
257 TEST(BaseTest, NoExceptionPromiseEmpty) {
258 auto wrapper = std::make_shared<NoExceptionPromise<int>>();
259 std::future future = wrapper->get_future();
260
261 // Destroy the empty promise with the future still pending. Verify that the
262 // process does not abort while destructing the promise.
263 wrapper.reset();
264 }
265
266 TEST(BaseTest, CanUseTypedMasks) {
267 {
268 MyMask mask;
269 ASSERT_EQ(static_cast<uint32_t>(mask), 0u);
270 ASSERT_FALSE(mask);
271 }
272
273 {
274 MyMask mask(MyMaskBits::kBar);
275 ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
276 ASSERT_TRUE(mask);
277 }
278
279 {
280 MyMask mask2(MyMaskBits::kBar);
281 MyMask mask(mask2);
282 ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
283 ASSERT_TRUE(mask);
284 }
285
286 {
287 MyMask mask2(MyMaskBits::kBar);
288 MyMask mask(std::move(mask2)); // NOLINT
289 ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
290 ASSERT_TRUE(mask);
291 }
292
293 ASSERT_LT(MyMaskBits::kBar, MyMaskBits::kBaz);
294 ASSERT_LE(MyMaskBits::kBar, MyMaskBits::kBaz);
295 ASSERT_GT(MyMaskBits::kBaz, MyMaskBits::kBar);
296 ASSERT_GE(MyMaskBits::kBaz, MyMaskBits::kBar);
297 ASSERT_EQ(MyMaskBits::kBaz, MyMaskBits::kBaz);
298 ASSERT_NE(MyMaskBits::kBaz, MyMaskBits::kBang);
299
300 {
301 MyMask m1(MyMaskBits::kBar);
302 MyMask m2(MyMaskBits::kBaz);
303 ASSERT_EQ(static_cast<uint32_t>(m1 & m2), 0u);
304 ASSERT_FALSE(m1 & m2);
305 }
306
307 {
308 MyMask m1(MyMaskBits::kBar);
309 MyMask m2(MyMaskBits::kBaz);
310 ASSERT_EQ(static_cast<uint32_t>(m1 | m2), ((1u << 0u) | (1u << 1u)));
311 ASSERT_TRUE(m1 | m2);
312 }
313
314 {
315 MyMask m1(MyMaskBits::kBar);
316 MyMask m2(MyMaskBits::kBaz);
317 ASSERT_EQ(static_cast<uint32_t>(m1 ^ m2), ((1u << 0u) ^ (1u << 1u)));
318 ASSERT_TRUE(m1 ^ m2);
319 }
320
321 {
322 MyMask m1(MyMaskBits::kBar);
323 ASSERT_EQ(static_cast<uint32_t>(~m1), (~(1u << 0u)));
324 ASSERT_TRUE(m1);
325 }
326
327 {
328 MyMask m1 = MyMaskBits::kBar;
329 MyMask m2 = MyMaskBits::kBaz;
330 m2 = m1;
331 ASSERT_EQ(m2, MyMaskBits::kBar);
332 }
333
334 {
335 MyMask m = MyMaskBits::kBar | MyMaskBits::kBaz;
336 ASSERT_TRUE(m);
337 }
338
339 {
340 MyMask m = MyMaskBits::kBar & MyMaskBits::kBaz;
341 ASSERT_FALSE(m);
342 }
343
344 {
345 MyMask m = MyMaskBits::kBar ^ MyMaskBits::kBaz;
346 ASSERT_TRUE(m);
347 }
348
349 {
350 MyMask m = ~MyMaskBits::kBar;
351 ASSERT_TRUE(m);
352 }
353
354 {
355 MyMask m1 = MyMaskBits::kBar;
356 MyMask m2 = MyMaskBits::kBaz;
357 m2 |= m1;
358 ASSERT_EQ(m1, MyMaskBits::kBar);
359 MyMask pred = MyMaskBits::kBar | MyMaskBits::kBaz;
360 ASSERT_EQ(m2, pred);
361 }
362
363 {
364 MyMask m1 = MyMaskBits::kBar;
365 MyMask m2 = MyMaskBits::kBaz;
366 m2 &= m1;
367 ASSERT_EQ(m1, MyMaskBits::kBar);
368 MyMask pred = MyMaskBits::kBar & MyMaskBits::kBaz;
369 ASSERT_EQ(m2, pred);
370 }
371
372 {
373 MyMask m1 = MyMaskBits::kBar;
374 MyMask m2 = MyMaskBits::kBaz;
375 m2 ^= m1;
376 ASSERT_EQ(m1, MyMaskBits::kBar);
377 MyMask pred = MyMaskBits::kBar ^ MyMaskBits::kBaz;
378 ASSERT_EQ(m2, pred);
379 }
380
381 {
382 MyMask x = MyMaskBits::kBar;
383 MyMask m = x | MyMaskBits::kBaz;
384 ASSERT_TRUE(m);
385 }
386
387 {
388 MyMask x = MyMaskBits::kBar;
389 MyMask m = MyMaskBits::kBaz | x;
390 ASSERT_TRUE(m);
391 }
392
393 {
394 MyMask x = MyMaskBits::kBar;
395 MyMask m = x & MyMaskBits::kBaz;
396 ASSERT_FALSE(m);
397 }
398
399 {
400 MyMask x = MyMaskBits::kBar;
401 MyMask m = MyMaskBits::kBaz & x;
402 ASSERT_FALSE(m);
403 }
404
405 {
406 MyMask x = MyMaskBits::kBar;
407 MyMask m = x ^ MyMaskBits::kBaz;
408 ASSERT_TRUE(m);
409 }
410
411 {
412 MyMask x = MyMaskBits::kBar;
413 MyMask m = MyMaskBits::kBaz ^ x;
414 ASSERT_TRUE(m);
415 }
416
417 {
418 MyMask x = MyMaskBits::kBar;
419 MyMask m = ~x;
420 ASSERT_TRUE(m);
421 }
422
423 {
424 MyMaskBits x = MyMaskBits::kBar;
425 MyMask m = MyMaskBits::kBaz;
426 ASSERT_TRUE(x < m);
427 ASSERT_TRUE(x <= m);
428 }
429
430 {
431 MyMaskBits x = MyMaskBits::kBar;
432 MyMask m = MyMaskBits::kBaz;
433 ASSERT_FALSE(x == m);
434 }
435
436 {
437 MyMaskBits x = MyMaskBits::kBar;
438 MyMask m = MyMaskBits::kBaz;
439 ASSERT_TRUE(x != m);
440 }
441
442 {
443 MyMaskBits x = MyMaskBits::kBar;
444 MyMask m = MyMaskBits::kBaz;
445 ASSERT_FALSE(x > m);
446 ASSERT_FALSE(x >= m);
447 }
448 }
449
450 } // namespace testing
451 } // namespace impeller
impeller::MyMaskBits::kBaz
@ kBaz
impeller::WriterLock
Definition: thread.h:116
impeller::NoExceptionPromise::set_value
void set_value(const T &value)
Definition: promise.h:38
impeller::testing::RWFoo
Definition: base_unittests.cc:31
impeller::Lock
Definition: thread.h:75
impeller::NoExceptionPromise::get_future
std::future< T > get_future()
Definition: promise.h:36
impeller::testing::RWFoo::mtx
RWMutex mtx
Definition: base_unittests.cc:32
impeller::MyMaskBits::kFoo
@ kFoo
impeller::MyMaskBits::kBar
@ kBar
impeller::ConditionVariable::Wait
void Wait(Mutex &mutex, const Predicate &should_stop_waiting) IPLR_REQUIRES(mutex)
Atomically unlocks the mutex and waits on the condition variable indefinitely till the predicate dete...
Definition: thread.h:260
impeller::Mask
A mask of typed enums.
Definition: mask.h:33
impeller::testing::Foo::IPLR_GUARDED_BY
int a IPLR_GUARDED_BY(mtx)
impeller::NoExceptionPromise
Definition: promise.h:26
impeller::ReaderLock
Definition: thread.h:95
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
impeller::testing::TEST
TEST(CanvasRecorder, Save)
Definition: canvas_recorder_unittests.cc:65
mask.h
strings.h
impeller::testing::CVTest::cv
ConditionVariable cv
Definition: base_unittests.cc:95
impeller::testing::Foo
Definition: base_unittests.cc:26
impeller::MyMaskBits::kBang
@ kBang
impeller::testing::CVTest
Definition: base_unittests.cc:93
promise.h
impeller::MyMaskBits
MyMaskBits
Definition: base_unittests.cc:13
impeller::ConditionVariable
A condition variable exactly similar to the one in libcxx with two major differences:
Definition: thread.h:146
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::testing::CVTest::IPLR_GUARDED_BY
uint32_t rando_ivar IPLR_GUARDED_BY(mutex)=0
impeller::ConditionVariable::WaitUntil
bool WaitUntil(Mutex &mutex, const std::chrono::time_point< Clock, Duration > &time_point, const Predicate &should_stop_waiting) IPLR_REQUIRES(mutex)
Atomically unlocks the mutex and waits on the condition variable up to a specified time point....
Definition: thread.h:192
thread.h
impeller::ConditionVariable::WaitFor
bool WaitFor(Mutex &mutex, const std::chrono::duration< Representation, Period > &duration, const Predicate &should_stop_waiting) IPLR_REQUIRES(mutex)
Atomically unlocks the mutex and waits on the condition variable for a designated duration....
Definition: thread.h:231
impeller
Definition: aiks_blur_unittests.cc:20
impeller::testing::RWFoo::IPLR_GUARDED_BY
int a IPLR_GUARDED_BY(mtx)
impeller::testing::Foo::mtx
Mutex mtx
Definition: base_unittests.cc:27
impeller::testing::CVTest::mutex
Mutex mutex
Definition: base_unittests.cc:94
impeller::IMPELLER_ENUM_IS_MASK
IMPELLER_ENUM_IS_MASK(MyMaskBits)
IPLR_REQUIRES
#define IPLR_REQUIRES(...)
Definition: thread_safety.h:30