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