xref: /aosp_15_r20/external/angle/src/common/system_utils_unittest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 
6 // system_utils_unittest.cpp: Unit tests for ANGLE's system utility functions
7 
8 #include "gmock/gmock.h"
9 #include "gtest/gtest.h"
10 
11 #include "common/mathutil.h"
12 #include "common/system_utils.h"
13 #include "util/test_utils.h"
14 
15 #include <fstream>
16 #include <sstream>
17 #include <vector>
18 
19 #if defined(ANGLE_PLATFORM_POSIX)
20 #    include <signal.h>
21 #endif
22 
23 using namespace angle;
24 
25 namespace
26 {
27 // Test getting the executable path
TEST(SystemUtils,ExecutablePath)28 TEST(SystemUtils, ExecutablePath)
29 {
30     // TODO: fuchsia support. http://anglebug.com/42261836
31 #if !defined(ANGLE_PLATFORM_FUCHSIA)
32     std::string executablePath = GetExecutablePath();
33     EXPECT_NE("", executablePath);
34 #endif
35 }
36 
37 // Test getting the executable directory
TEST(SystemUtils,ExecutableDir)38 TEST(SystemUtils, ExecutableDir)
39 {
40     // TODO: fuchsia support. http://anglebug.com/42261836
41 #if !defined(ANGLE_PLATFORM_FUCHSIA)
42     std::string executableDir = GetExecutableDirectory();
43     EXPECT_NE("", executableDir);
44 
45     std::string executablePath = GetExecutablePath();
46     EXPECT_LT(executableDir.size(), executablePath.size());
47     EXPECT_EQ(0, strncmp(executableDir.c_str(), executablePath.c_str(), executableDir.size()));
48 #endif
49 }
50 
51 // Test setting environment variables
TEST(SystemUtils,Environment)52 TEST(SystemUtils, Environment)
53 {
54     constexpr char kEnvVarName[]  = "UNITTEST_ENV_VARIABLE";
55     constexpr char kEnvVarValue[] = "The quick brown fox jumps over the lazy dog";
56 
57     bool setEnvDone = SetEnvironmentVar(kEnvVarName, kEnvVarValue);
58     EXPECT_TRUE(setEnvDone);
59 
60     std::string readback = GetEnvironmentVar(kEnvVarName);
61     EXPECT_EQ(kEnvVarValue, readback);
62 
63     bool unsetEnvDone = UnsetEnvironmentVar(kEnvVarName);
64     EXPECT_TRUE(unsetEnvDone);
65 
66     readback = GetEnvironmentVar(kEnvVarName);
67     EXPECT_EQ("", readback);
68 }
69 
70 // Test CPU time measurement with a small operation
71 // (pretty much the measurement itself)
TEST(SystemUtils,CpuTimeSmallOp)72 TEST(SystemUtils, CpuTimeSmallOp)
73 {
74     double cpuTimeStart = GetCurrentProcessCpuTime();
75     double cpuTimeEnd   = GetCurrentProcessCpuTime();
76     EXPECT_GE(cpuTimeEnd, cpuTimeStart);
77 }
78 
79 // Test CPU time measurement with a sleepy operation
TEST(SystemUtils,CpuTimeSleepy)80 TEST(SystemUtils, CpuTimeSleepy)
81 {
82     double cpuTimeStart = GetCurrentProcessCpuTime();
83     angle::Sleep(1);
84     double cpuTimeEnd = GetCurrentProcessCpuTime();
85     EXPECT_GE(cpuTimeEnd, cpuTimeStart);
86 }
87 
88 // Test CPU time measurement with a heavy operation
TEST(SystemUtils,CpuTimeHeavyOp)89 TEST(SystemUtils, CpuTimeHeavyOp)
90 {
91     constexpr size_t bufferSize = 1048576;
92     std::vector<uint8_t> buffer(bufferSize, 1);
93     double cpuTimeStart = GetCurrentProcessCpuTime();
94     memset(buffer.data(), 0, bufferSize);
95     double cpuTimeEnd = GetCurrentProcessCpuTime();
96     EXPECT_GE(cpuTimeEnd, cpuTimeStart);
97 }
98 
99 #if defined(ANGLE_PLATFORM_POSIX)
TEST(SystemUtils,ConcatenatePathSimple)100 TEST(SystemUtils, ConcatenatePathSimple)
101 {
102     std::string path1    = "/this/is/path1";
103     std::string path2    = "this/is/path2";
104     std::string expected = "/this/is/path1/this/is/path2";
105     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
106 }
107 
TEST(SystemUtils,ConcatenatePath1Empty)108 TEST(SystemUtils, ConcatenatePath1Empty)
109 {
110     std::string path1    = "";
111     std::string path2    = "this/is/path2";
112     std::string expected = "this/is/path2";
113     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
114 }
115 
TEST(SystemUtils,ConcatenatePath2Empty)116 TEST(SystemUtils, ConcatenatePath2Empty)
117 {
118     std::string path1    = "/this/is/path1";
119     std::string path2    = "";
120     std::string expected = "/this/is/path1";
121     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
122 }
123 
TEST(SystemUtils,ConcatenatePath2FullPath)124 TEST(SystemUtils, ConcatenatePath2FullPath)
125 {
126     std::string path1    = "/this/is/path1";
127     std::string path2    = "/this/is/path2";
128     std::string expected = "/this/is/path2";
129     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
130 }
131 
TEST(SystemUtils,ConcatenatePathRedundantSeparators)132 TEST(SystemUtils, ConcatenatePathRedundantSeparators)
133 {
134     std::string path1    = "/this/is/path1/";
135     std::string path2    = "this/is/path2";
136     std::string expected = "/this/is/path1/this/is/path2";
137     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
138 }
139 
TEST(SystemUtils,IsFullPath)140 TEST(SystemUtils, IsFullPath)
141 {
142     std::string path1 = "/this/is/path1/";
143     std::string path2 = "this/is/path2";
144     EXPECT_TRUE(IsFullPath(path1));
145     EXPECT_FALSE(IsFullPath(path2));
146 }
147 #elif defined(ANGLE_PLATFORM_WINDOWS)
TEST(SystemUtils,ConcatenatePathSimple)148 TEST(SystemUtils, ConcatenatePathSimple)
149 {
150     std::string path1    = "C:\\this\\is\\path1";
151     std::string path2    = "this\\is\\path2";
152     std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
153     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
154 }
155 
TEST(SystemUtils,ConcatenatePath1Empty)156 TEST(SystemUtils, ConcatenatePath1Empty)
157 {
158     std::string path1    = "";
159     std::string path2    = "this\\is\\path2";
160     std::string expected = "this\\is\\path2";
161     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
162 }
163 
TEST(SystemUtils,ConcatenatePath2Empty)164 TEST(SystemUtils, ConcatenatePath2Empty)
165 {
166     std::string path1    = "C:\\this\\is\\path1";
167     std::string path2    = "";
168     std::string expected = "C:\\this\\is\\path1";
169     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
170 }
171 
TEST(SystemUtils,ConcatenatePath2FullPath)172 TEST(SystemUtils, ConcatenatePath2FullPath)
173 {
174     std::string path1    = "C:\\this\\is\\path1";
175     std::string path2    = "C:\\this\\is\\path2";
176     std::string expected = "C:\\this\\is\\path2";
177     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
178 }
179 
TEST(SystemUtils,ConcatenatePathRedundantSeparators)180 TEST(SystemUtils, ConcatenatePathRedundantSeparators)
181 {
182     std::string path1    = "C:\\this\\is\\path1\\";
183     std::string path2    = "this\\is\\path2";
184     std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
185     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
186 }
187 
TEST(SystemUtils,ConcatenatePathRedundantSeparators2)188 TEST(SystemUtils, ConcatenatePathRedundantSeparators2)
189 {
190     std::string path1    = "C:\\this\\is\\path1\\";
191     std::string path2    = "\\this\\is\\path2";
192     std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
193     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
194 }
195 
TEST(SystemUtils,ConcatenatePathRedundantSeparators3)196 TEST(SystemUtils, ConcatenatePathRedundantSeparators3)
197 {
198     std::string path1    = "C:\\this\\is\\path1";
199     std::string path2    = "\\this\\is\\path2";
200     std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
201     EXPECT_EQ(ConcatenatePath(path1, path2), expected);
202 }
203 
TEST(SystemUtils,IsFullPath)204 TEST(SystemUtils, IsFullPath)
205 {
206     std::string path1 = "C:\\this\\is\\path1\\";
207     std::string path2 = "this\\is\\path2";
208     EXPECT_TRUE(IsFullPath(path1));
209     EXPECT_FALSE(IsFullPath(path2));
210 }
211 #endif
212 
213 // Temporary file creation is not supported on Android right now.
214 #if defined(ANGLE_PLATFORM_ANDROID)
215 #    define MAYBE_CreateAndDeleteTemporaryFile DISABLED_CreateAndDeleteTemporaryFile
216 #    define MAYBE_CreateAndDeleteFileInTempDir DISABLED_CreateAndDeleteFileInTempDir
217 #else
218 #    define MAYBE_CreateAndDeleteTemporaryFile CreateAndDeleteTemporaryFile
219 #    define MAYBE_CreateAndDeleteFileInTempDir CreateAndDeleteFileInTempDir
220 #endif  // defined(ANGLE_PLATFORM_ANDROID)
221 
222 // Test creating/using temporary file
TEST(SystemUtils,MAYBE_CreateAndDeleteTemporaryFile)223 TEST(SystemUtils, MAYBE_CreateAndDeleteTemporaryFile)
224 {
225     Optional<std::string> path = CreateTemporaryFile();
226     ASSERT_TRUE(path.valid());
227     ASSERT_TRUE(!path.value().empty());
228 
229     const std::string testContents = "test output";
230 
231     // Test writing
232     std::ofstream out;
233     out.open(path.value());
234     ASSERT_TRUE(out.is_open());
235     out << testContents;
236     EXPECT_TRUE(out.good());
237     out.close();
238 
239     // Test reading
240     std::ifstream in;
241     in.open(path.value());
242     EXPECT_TRUE(in.is_open());
243     std::ostringstream sstr;
244     sstr << in.rdbuf();
245     EXPECT_EQ(sstr.str(), testContents);
246     in.close();
247 
248     // Test deleting
249     EXPECT_TRUE(DeleteSystemFile(path.value().c_str()));
250 }
251 
252 // Test creating/using file created in system's temporary directory
TEST(SystemUtils,MAYBE_CreateAndDeleteFileInTempDir)253 TEST(SystemUtils, MAYBE_CreateAndDeleteFileInTempDir)
254 {
255     Optional<std::string> tempDir = GetTempDirectory();
256     ASSERT_TRUE(tempDir.valid());
257 
258     Optional<std::string> path = CreateTemporaryFileInDirectory(tempDir.value());
259     ASSERT_TRUE(path.valid());
260     ASSERT_TRUE(!path.value().empty());
261 
262     const std::string testContents = "test output";
263 
264     // Test writing
265     std::ofstream out;
266     out.open(path.value());
267     ASSERT_TRUE(out.is_open());
268     out << "test output";
269     EXPECT_TRUE(out.good());
270     out.close();
271 
272     // Test reading
273     std::ifstream in;
274     in.open(path.value());
275     EXPECT_TRUE(in.is_open());
276     std::ostringstream sstr;
277     sstr << in.rdbuf();
278     EXPECT_EQ(sstr.str(), testContents);
279     in.close();
280 
281     // Test deleting
282     EXPECT_TRUE(DeleteSystemFile(path.value().c_str()));
283 }
284 
285 // Test retrieving page size
TEST(SystemUtils,PageSize)286 TEST(SystemUtils, PageSize)
287 {
288     size_t pageSize = GetPageSize();
289     EXPECT_TRUE(pageSize > 0);
290 }
291 
292 // mprotect is not supported on Fuchsia right now.
293 #if defined(ANGLE_PLATFORM_FUCHSIA)
294 #    define MAYBE_PageFaultHandlerInit DISABLED_PageFaultHandlerInit
295 #    define MAYBE_PageFaultHandlerProtect DISABLED_PageFaultHandlerProtect
296 #    define MAYBE_PageFaultHandlerDefaultHandler DISABLED_PageFaultHandlerDefaultHandler
297 // mprotect tests hang on macOS M1. Also applies to the iOS simulator.
298 #elif ANGLE_PLATFORM_MACOS || ANGLE_PLATFORM_IOS_FAMILY_SIMULATOR
299 #    define MAYBE_PageFaultHandlerInit PageFaultHandlerInit
300 #    define MAYBE_PageFaultHandlerProtect DISABLED_PageFaultHandlerProtect
301 #    define MAYBE_PageFaultHandlerDefaultHandler DISABLED_PageFaultHandlerDefaultHandler
302 #else
303 #    define MAYBE_PageFaultHandlerInit PageFaultHandlerInit
304 #    define MAYBE_PageFaultHandlerProtect PageFaultHandlerProtect
305 #    define MAYBE_PageFaultHandlerDefaultHandler PageFaultHandlerDefaultHandler
306 #endif  // defined(ANGLE_PLATFORM_FUCHSIA)
307 
308 // Test initializing and cleaning up page fault handler.
TEST(SystemUtils,MAYBE_PageFaultHandlerInit)309 TEST(SystemUtils, MAYBE_PageFaultHandlerInit)
310 {
311     PageFaultCallback callback = [](uintptr_t address) {
312         return PageFaultHandlerRangeType::InRange;
313     };
314 
315     std::unique_ptr<PageFaultHandler> handler(CreatePageFaultHandler(callback));
316 
317     EXPECT_TRUE(handler->enable());
318     EXPECT_TRUE(handler->disable());
319 }
320 
321 // Test write protecting and unprotecting memory
TEST(SystemUtils,MAYBE_PageFaultHandlerProtect)322 TEST(SystemUtils, MAYBE_PageFaultHandlerProtect)
323 {
324     size_t pageSize = GetPageSize();
325     EXPECT_TRUE(pageSize > 0);
326 
327     std::vector<float> data   = std::vector<float>(100);
328     size_t dataSize           = sizeof(float) * data.size();
329     uintptr_t dataStart       = reinterpret_cast<uintptr_t>(data.data());
330     uintptr_t protectionStart = rx::roundDownPow2(dataStart, pageSize);
331     uintptr_t protectionEnd   = rx::roundUpPow2(dataStart + dataSize, pageSize);
332 
333     std::mutex mutex;
334     bool handlerCalled = false;
335 
336     PageFaultCallback callback = [&mutex, pageSize, dataStart, dataSize,
337                                   &handlerCalled](uintptr_t address) {
338         if (address >= dataStart && address < dataStart + dataSize)
339         {
340             std::lock_guard<std::mutex> lock(mutex);
341             uintptr_t pageStart = rx::roundDownPow2(address, pageSize);
342             EXPECT_TRUE(UnprotectMemory(pageStart, pageSize));
343             handlerCalled = true;
344             return PageFaultHandlerRangeType::InRange;
345         }
346         else
347         {
348             return PageFaultHandlerRangeType::OutOfRange;
349         }
350     };
351 
352     std::unique_ptr<PageFaultHandler> handler(CreatePageFaultHandler(callback));
353     handler->enable();
354 
355     size_t protectionSize = protectionEnd - protectionStart;
356 
357     // Test Protect
358     EXPECT_TRUE(ProtectMemory(protectionStart, protectionSize));
359 
360     data[0] = 0.0;
361 
362     {
363         std::lock_guard<std::mutex> lock(mutex);
364         EXPECT_TRUE(handlerCalled);
365     }
366 
367     // Test Protect and unprotect
368     EXPECT_TRUE(ProtectMemory(protectionStart, protectionSize));
369     EXPECT_TRUE(UnprotectMemory(protectionStart, protectionSize));
370 
371     handlerCalled = false;
372     data[0]       = 0.0;
373     EXPECT_FALSE(handlerCalled);
374 
375     // Clean up
376     EXPECT_TRUE(handler->disable());
377 }
378 
379 // Tests basic usage of StripFilenameFromPath.
TEST(SystemUtils,StripFilenameFromPathUsage)380 TEST(SystemUtils, StripFilenameFromPathUsage)
381 {
382     EXPECT_EQ(StripFilenameFromPath("/path/to/tests/angle_tests"), "/path/to/tests");
383     EXPECT_EQ(StripFilenameFromPath("C:\\tests\\angle_tests.exe"), "C:\\tests");
384     EXPECT_EQ(StripFilenameFromPath("angle_tests"), "");
385 }
386 
387 #if defined(ANGLE_PLATFORM_POSIX)
388 std::mutex gCustomHandlerMutex;
389 bool gCustomHandlerCalled = false;
390 
CustomSegfaultHandlerFunction(int sig,siginfo_t * info,void * unused)391 void CustomSegfaultHandlerFunction(int sig, siginfo_t *info, void *unused)
392 {
393     std::lock_guard<std::mutex> lock(gCustomHandlerMutex);
394     gCustomHandlerCalled = true;
395 }
396 
397 // Test if the default page fault handler is called on OutOfRange.
TEST(SystemUtils,MAYBE_PageFaultHandlerDefaultHandler)398 TEST(SystemUtils, MAYBE_PageFaultHandlerDefaultHandler)
399 {
400     size_t pageSize = GetPageSize();
401     EXPECT_TRUE(pageSize > 0);
402 
403     std::vector<float> data   = std::vector<float>(100);
404     size_t dataSize           = sizeof(float) * data.size();
405     uintptr_t dataStart       = reinterpret_cast<uintptr_t>(data.data());
406     uintptr_t protectionStart = rx::roundDownPow2(dataStart, pageSize);
407     uintptr_t protectionEnd   = rx::roundUpPow2(dataStart + dataSize, pageSize);
408     size_t protectionSize     = protectionEnd - protectionStart;
409 
410     // Create custom handler
411     struct sigaction sigAction = {};
412     sigAction.sa_flags         = SA_SIGINFO;
413     sigAction.sa_sigaction     = &CustomSegfaultHandlerFunction;
414     sigemptyset(&sigAction.sa_mask);
415 
416     struct sigaction oldSigSegv = {};
417     ASSERT_TRUE(sigaction(SIGSEGV, &sigAction, &oldSigSegv) == 0);
418     struct sigaction oldSigBus = {};
419     ASSERT_TRUE(sigaction(SIGBUS, &sigAction, &oldSigBus) == 0);
420 
421     PageFaultCallback callback = [protectionStart, protectionSize](uintptr_t address) {
422         EXPECT_TRUE(UnprotectMemory(protectionStart, protectionSize));
423         return PageFaultHandlerRangeType::OutOfRange;
424     };
425 
426     std::unique_ptr<PageFaultHandler> handler(CreatePageFaultHandler(callback));
427     handler->enable();
428 
429     // Test Protect
430     EXPECT_TRUE(ProtectMemory(protectionStart, protectionSize));
431 
432     data[0] = 0.0;
433 
434     {
435         std::lock_guard<std::mutex> lock(gCustomHandlerMutex);
436         EXPECT_TRUE(gCustomHandlerCalled);
437     }
438 
439     // Clean up
440     EXPECT_TRUE(handler->disable());
441 
442     ASSERT_TRUE(sigaction(SIGSEGV, &oldSigSegv, nullptr) == 0);
443     ASSERT_TRUE(sigaction(SIGBUS, &oldSigBus, nullptr) == 0);
444 }
445 #else
TEST(SystemUtils,MAYBE_PageFaultHandlerDefaultHandler)446 TEST(SystemUtils, MAYBE_PageFaultHandlerDefaultHandler) {}
447 #endif
448 
449 // Tests basic usage of GetCurrentThreadId.
TEST(SystemUtils,GetCurrentThreadId)450 TEST(SystemUtils, GetCurrentThreadId)
451 {
452     constexpr size_t kThreadCount = 64;
453 
454     std::mutex mutex;
455     std::condition_variable condVar;
456 
457     std::vector<std::thread> threads;
458     std::set<ThreadId> threadIds;
459     size_t readyCount = 0;
460 
461     for (size_t i = 0; i < kThreadCount; ++i)
462     {
463         threads.emplace_back([&]() {
464             std::unique_lock<std::mutex> lock(mutex);
465 
466             // Get threadId
467             const angle::ThreadId threadId = angle::GetCurrentThreadId();
468             EXPECT_NE(threadId, angle::InvalidThreadId());
469             threadIds.insert(threadId);
470 
471             // Allow thread to finish only when all threads received the id.
472             // Otherwise new thread may reuse Id of already completed but not joined thread.
473             // Problem can be reproduced on Window platform, for example.
474             ++readyCount;
475             if (readyCount < kThreadCount)
476             {
477                 condVar.wait(lock, [&]() { return readyCount == kThreadCount; });
478             }
479             else
480             {
481                 condVar.notify_all();
482             }
483 
484             // Check that threadId is still the same.
485             EXPECT_EQ(threadId, angle::GetCurrentThreadId());
486         });
487     }
488 
489     for (size_t i = 0; i < kThreadCount; ++i)
490     {
491         threads[i].join();
492     }
493 
494     // Check that all threadIds were unique.
495     EXPECT_EQ(threadIds.size(), kThreadCount);
496 }
497 
498 }  // anonymous namespace
499