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"
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 
21 
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;
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;
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;
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 
299 
300  {
303  ASSERT_EQ(static_cast<uint32_t>(m1 & m2), 0u);
304  ASSERT_FALSE(m1 & m2);
305  }
306 
307  {
310  ASSERT_EQ(static_cast<uint32_t>(m1 | m2), ((1u << 0u) | (1u << 1u)));
311  ASSERT_TRUE(m1 | m2);
312  }
313 
314  {
317  ASSERT_EQ(static_cast<uint32_t>(m1 ^ m2), ((1u << 0u) ^ (1u << 1u)));
318  ASSERT_TRUE(m1 ^ m2);
319  }
320 
321  {
323  ASSERT_EQ(static_cast<uint32_t>(~m1), (~(1u << 0u)));
324  ASSERT_TRUE(m1);
325  }
326 
327  {
330  m2 = m1;
331  ASSERT_EQ(m2, MyMaskBits::kBar);
332  }
333 
334  {
336  ASSERT_TRUE(m);
337  }
338 
339  {
341  ASSERT_FALSE(m);
342  }
343 
344  {
346  ASSERT_TRUE(m);
347  }
348 
349  {
350  MyMask m = ~MyMaskBits::kBar;
351  ASSERT_TRUE(m);
352  }
353 
354  {
357  m2 |= m1;
358  ASSERT_EQ(m1, MyMaskBits::kBar);
360  ASSERT_EQ(m2, pred);
361  }
362 
363  {
366  m2 &= m1;
367  ASSERT_EQ(m1, MyMaskBits::kBar);
369  ASSERT_EQ(m2, pred);
370  }
371 
372  {
375  m2 ^= m1;
376  ASSERT_EQ(m1, MyMaskBits::kBar);
378  ASSERT_EQ(m2, pred);
379  }
380 
381  {
383  MyMask m = x | MyMaskBits::kBaz;
384  ASSERT_TRUE(m);
385  }
386 
387  {
389  MyMask m = MyMaskBits::kBaz | x;
390  ASSERT_TRUE(m);
391  }
392 
393  {
395  MyMask m = x & MyMaskBits::kBaz;
396  ASSERT_FALSE(m);
397  }
398 
399  {
401  MyMask m = MyMaskBits::kBaz & x;
402  ASSERT_FALSE(m);
403  }
404 
405  {
407  MyMask m = x ^ MyMaskBits::kBaz;
408  ASSERT_TRUE(m);
409  }
410 
411  {
413  MyMask m = MyMaskBits::kBaz ^ x;
414  ASSERT_TRUE(m);
415  }
416 
417  {
419  MyMask m = ~x;
420  ASSERT_TRUE(m);
421  }
422 
423  {
426  ASSERT_TRUE(x < m);
427  ASSERT_TRUE(x <= m);
428  }
429 
430  {
433  ASSERT_FALSE(x == m);
434  }
435 
436  {
439  ASSERT_TRUE(x != m);
440  }
441 
442  {
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