Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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"
10
11namespace impeller {
12
13enum class MyMaskBits : uint32_t {
14 kFoo = 0,
15 kBar = 1 << 0,
16 kBaz = 1 << 1,
17 kBang = 1 << 2,
18};
19
21
23
24namespace testing {
25
26struct Foo {
27 Mutex mtx;
29};
30
31struct RWFoo {
32 RWMutex mtx;
34};
35
36TEST(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
45TEST(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
53TEST(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)
64 f.mtx.UnlockReader();
65}
66
67TEST(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)
81 }
82
83 // f.mtx.UnlockReader(); <--- Static analysis error.
84}
85
86TEST(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
93struct CVTest {
94 Mutex mutex;
96 uint32_t rando_ivar IPLR_GUARDED_BY(mutex) = 0;
97};
98
99TEST(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
122TEST(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
143TEST(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
160TEST(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
205TEST(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
250TEST(BaseTest, NoExceptionPromiseValue) {
252 std::future future = wrapper.get_future();
253 wrapper.set_value(123);
254 ASSERT_EQ(future.get(), 123);
255}
256
257TEST(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
266TEST(BaseTest, CanUseTypedMasks) {
267 {
268 MyMask mask;
269 ASSERT_EQ(static_cast<uint32_t>(mask), 0u);
270 ASSERT_FALSE(mask);
271 }
272
273 {
275 ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
276 ASSERT_TRUE(mask);
277 }
278
279 {
281 MyMask mask(mask2);
282 ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
283 ASSERT_TRUE(mask);
284 }
285
286 {
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 {
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 {
384 ASSERT_TRUE(m);
385 }
386
387 {
390 ASSERT_TRUE(m);
391 }
392
393 {
396 ASSERT_FALSE(m);
397 }
398
399 {
402 ASSERT_FALSE(m);
403 }
404
405 {
408 ASSERT_TRUE(m);
409 }
410
411 {
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
#define TEST(S, s, D, expected)
#define test(name)
skvx::Vec< 2, uint32_t > mask2
A condition variable exactly similar to the one in libcxx with two major differences:
Definition thread.h:146
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
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
std::future< T > get_future()
Definition promise.h:36
void set_value(const T &value)
Definition promise.h:38
static bool b
struct MyStruct a[10]
GAsyncResult * result
static constexpr uint64_t kThreadCount
#define FML_ALLOW_UNUSED_LOCAL(x)
#define IMPELLER_ENUM_IS_MASK(enum_name)
Declare this in the "impeller" namespace to make the enum maskable.
Definition mask.h:21
double x
std::string SPrintF(const char *format,...)
Definition strings.cc:12
A mask of typed enums.
Definition mask.h:33
uint32_t rando_ivar IPLR_GUARDED_BY(mutex)=0
int a IPLR_GUARDED_BY(mtx)
#define IPLR_REQUIRES(...)