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;
29 };
30 
31 struct RWFoo {
32  RWMutex 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;
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;
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;
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 
297 
298  {
301  ASSERT_EQ(static_cast<uint32_t>(m1 & m2), 0u);
302  ASSERT_FALSE(m1 & m2);
303  }
304 
305  {
308  ASSERT_EQ(static_cast<uint32_t>(m1 | m2), ((1u << 0u) | (1u << 1u)));
309  ASSERT_TRUE(m1 | m2);
310  }
311 
312  {
315  ASSERT_EQ(static_cast<uint32_t>(m1 ^ m2), ((1u << 0u) ^ (1u << 1u)));
316  ASSERT_TRUE(m1 ^ m2);
317  }
318 
319  {
321  ASSERT_EQ(static_cast<uint32_t>(~m1), (~(1u << 0u)));
322  ASSERT_TRUE(m1);
323  }
324 
325  {
328  m2 = m1;
329  ASSERT_EQ(m2, MyMaskBits::kBar);
330  }
331 
332  {
334  ASSERT_TRUE(m);
335  }
336 
337  {
339  ASSERT_FALSE(m);
340  }
341 
342  {
344  ASSERT_TRUE(m);
345  }
346 
347  {
348  MyMask m = ~MyMaskBits::kBar;
349  ASSERT_TRUE(m);
350  }
351 
352  {
355  m2 |= m1;
356  ASSERT_EQ(m1, MyMaskBits::kBar);
358  ASSERT_EQ(m2, pred);
359  }
360 
361  {
364  m2 &= m1;
365  ASSERT_EQ(m1, MyMaskBits::kBar);
367  ASSERT_EQ(m2, pred);
368  }
369 
370  {
373  m2 ^= m1;
374  ASSERT_EQ(m1, MyMaskBits::kBar);
376  ASSERT_EQ(m2, pred);
377  }
378 
379  {
381  MyMask m = x | MyMaskBits::kBaz;
382  ASSERT_TRUE(m);
383  }
384 
385  {
387  MyMask m = MyMaskBits::kBaz | x;
388  ASSERT_TRUE(m);
389  }
390 
391  {
393  MyMask m = x & MyMaskBits::kBaz;
394  ASSERT_FALSE(m);
395  }
396 
397  {
399  MyMask m = MyMaskBits::kBaz & x;
400  ASSERT_FALSE(m);
401  }
402 
403  {
405  MyMask m = x ^ MyMaskBits::kBaz;
406  ASSERT_TRUE(m);
407  }
408 
409  {
411  MyMask m = MyMaskBits::kBaz ^ x;
412  ASSERT_TRUE(m);
413  }
414 
415  {
417  MyMask m = ~x;
418  ASSERT_TRUE(m);
419  }
420 
421  {
424  ASSERT_TRUE(x < m);
425  ASSERT_TRUE(x <= m);
426  }
427 
428  {
431  ASSERT_FALSE(x == m);
432  }
433 
434  {
437  ASSERT_TRUE(x != m);
438  }
439 
440  {
443  ASSERT_FALSE(x > m);
444  ASSERT_FALSE(x >= m);
445  }
446 }
447 
448 } // namespace testing
449 } // namespace impeller
A condition variable exactly similar to the one in libcxx with two major differences:
Definition: thread.h:143
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
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
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
void set_value(const T &value)
Definition: promise.h:38
std::future< T > get_future()
Definition: promise.h:36
int32_t x
TEST(AllocationSizeTest, CanCreateTypedAllocations)
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
IMPELLER_ENUM_IS_MASK(MyMaskBits)
A mask of typed enums.
Definition: mask.h:33
uint32_t rando_ivar IPLR_GUARDED_BY(mutex)=0
int a IPLR_GUARDED_BY(mtx)
const size_t start
#define IPLR_REQUIRES(...)
Definition: thread_safety.h:30