blob: 41b304cb4791ec660178ed5b643913c1f5113edf [file] [log] [blame]
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// system_utils_unittest.cpp: Unit tests for ANGLE's system utility functions
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "common/mathutil.h"
#include "common/system_utils.h"
#include "util/test_utils.h"
#include <vector>
#if defined(ANGLE_PLATFORM_POSIX)
# include <signal.h>
#endif
using namespace angle;
namespace
{
// Test getting the executable path
TEST(SystemUtils, ExecutablePath)
{
// TODO: fuchsia support. http://anglebug.com/3161
#if !defined(ANGLE_PLATFORM_FUCHSIA)
std::string executablePath = GetExecutablePath();
EXPECT_NE("", executablePath);
#endif
}
// Test getting the executable directory
TEST(SystemUtils, ExecutableDir)
{
// TODO: fuchsia support. http://anglebug.com/3161
#if !defined(ANGLE_PLATFORM_FUCHSIA)
std::string executableDir = GetExecutableDirectory();
EXPECT_NE("", executableDir);
std::string executablePath = GetExecutablePath();
EXPECT_LT(executableDir.size(), executablePath.size());
EXPECT_EQ(0, strncmp(executableDir.c_str(), executablePath.c_str(), executableDir.size()));
#endif
}
// Test setting environment variables
TEST(SystemUtils, Environment)
{
constexpr char kEnvVarName[] = "UNITTEST_ENV_VARIABLE";
constexpr char kEnvVarValue[] = "The quick brown fox jumps over the lazy dog";
bool setEnvDone = SetEnvironmentVar(kEnvVarName, kEnvVarValue);
EXPECT_TRUE(setEnvDone);
std::string readback = GetEnvironmentVar(kEnvVarName);
EXPECT_EQ(kEnvVarValue, readback);
bool unsetEnvDone = UnsetEnvironmentVar(kEnvVarName);
EXPECT_TRUE(unsetEnvDone);
readback = GetEnvironmentVar(kEnvVarName);
EXPECT_EQ("", readback);
}
// Test CPU time measurement with a small operation
// (pretty much the measurement itself)
TEST(SystemUtils, CpuTimeSmallOp)
{
double cpuTimeStart = GetCurrentProcessCpuTime();
double cpuTimeEnd = GetCurrentProcessCpuTime();
EXPECT_GE(cpuTimeEnd, cpuTimeStart);
}
// Test CPU time measurement with a sleepy operation
TEST(SystemUtils, CpuTimeSleepy)
{
double cpuTimeStart = GetCurrentProcessCpuTime();
angle::Sleep(1);
double cpuTimeEnd = GetCurrentProcessCpuTime();
EXPECT_GE(cpuTimeEnd, cpuTimeStart);
}
// Test CPU time measurement with a heavy operation
TEST(SystemUtils, CpuTimeHeavyOp)
{
constexpr size_t bufferSize = 1048576;
std::vector<uint8_t> buffer(bufferSize, 1);
double cpuTimeStart = GetCurrentProcessCpuTime();
memset(buffer.data(), 0, bufferSize);
double cpuTimeEnd = GetCurrentProcessCpuTime();
EXPECT_GE(cpuTimeEnd, cpuTimeStart);
}
#if defined(ANGLE_PLATFORM_POSIX)
TEST(SystemUtils, ConcatenatePathSimple)
{
std::string path1 = "/this/is/path1";
std::string path2 = "this/is/path2";
std::string expected = "/this/is/path1/this/is/path2";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, ConcatenatePath1Empty)
{
std::string path1 = "";
std::string path2 = "this/is/path2";
std::string expected = "this/is/path2";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, ConcatenatePath2Empty)
{
std::string path1 = "/this/is/path1";
std::string path2 = "";
std::string expected = "/this/is/path1";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, ConcatenatePath2FullPath)
{
std::string path1 = "/this/is/path1";
std::string path2 = "/this/is/path2";
std::string expected = "/this/is/path2";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, ConcatenatePathRedundantSeparators)
{
std::string path1 = "/this/is/path1/";
std::string path2 = "this/is/path2";
std::string expected = "/this/is/path1/this/is/path2";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, IsFullPath)
{
std::string path1 = "/this/is/path1/";
std::string path2 = "this/is/path2";
EXPECT_TRUE(IsFullPath(path1));
EXPECT_FALSE(IsFullPath(path2));
}
#elif defined(ANGLE_PLATFORM_WINDOWS)
TEST(SystemUtils, ConcatenatePathSimple)
{
std::string path1 = "C:\\this\\is\\path1";
std::string path2 = "this\\is\\path2";
std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, ConcatenatePath1Empty)
{
std::string path1 = "";
std::string path2 = "this\\is\\path2";
std::string expected = "this\\is\\path2";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, ConcatenatePath2Empty)
{
std::string path1 = "C:\\this\\is\\path1";
std::string path2 = "";
std::string expected = "C:\\this\\is\\path1";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, ConcatenatePath2FullPath)
{
std::string path1 = "C:\\this\\is\\path1";
std::string path2 = "C:\\this\\is\\path2";
std::string expected = "C:\\this\\is\\path2";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, ConcatenatePathRedundantSeparators)
{
std::string path1 = "C:\\this\\is\\path1\\";
std::string path2 = "this\\is\\path2";
std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, ConcatenatePathRedundantSeparators2)
{
std::string path1 = "C:\\this\\is\\path1\\";
std::string path2 = "\\this\\is\\path2";
std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, ConcatenatePathRedundantSeparators3)
{
std::string path1 = "C:\\this\\is\\path1";
std::string path2 = "\\this\\is\\path2";
std::string expected = "C:\\this\\is\\path1\\this\\is\\path2";
EXPECT_EQ(ConcatenatePath(path1, path2), expected);
}
TEST(SystemUtils, IsFullPath)
{
std::string path1 = "C:\\this\\is\\path1\\";
std::string path2 = "this\\is\\path2";
EXPECT_TRUE(IsFullPath(path1));
EXPECT_FALSE(IsFullPath(path2));
}
#endif
// Test retrieving page size
TEST(SystemUtils, PageSize)
{
size_t pageSize = GetPageSize();
EXPECT_TRUE(pageSize > 0);
}
// mprotect is not supported on Fuchsia right now.
#if defined(ANGLE_PLATFORM_FUCHSIA)
# define MAYBE_PageFaultHandlerInit DISABLED_PageFaultHandlerInit
# define MAYBE_PageFaultHandlerProtect DISABLED_PageFaultHandlerProtect
# define MAYBE_PageFaultHandlerDefaultHandler DISABLED_PageFaultHandlerDefaultHandler
// mprotect tests hang on macOS M1.
#elif defined(ANGLE_PLATFORM_MACOS)
# define MAYBE_PageFaultHandlerInit PageFaultHandlerInit
# define MAYBE_PageFaultHandlerProtect DISABLED_PageFaultHandlerProtect
# define MAYBE_PageFaultHandlerDefaultHandler DISABLED_PageFaultHandlerDefaultHandler
#else
# define MAYBE_PageFaultHandlerInit PageFaultHandlerInit
# define MAYBE_PageFaultHandlerProtect PageFaultHandlerProtect
# define MAYBE_PageFaultHandlerDefaultHandler PageFaultHandlerDefaultHandler
#endif // defined(ANGLE_PLATFORM_FUCHSIA)
// Test initializing and cleaning up page fault handler.
TEST(SystemUtils, MAYBE_PageFaultHandlerInit)
{
PageFaultCallback callback = [](uintptr_t address) {
return PageFaultHandlerRangeType::InRange;
};
std::unique_ptr<PageFaultHandler> handler(CreatePageFaultHandler(callback));
EXPECT_TRUE(handler->enable());
EXPECT_TRUE(handler->disable());
}
// Test write protecting and unprotecting memory
TEST(SystemUtils, MAYBE_PageFaultHandlerProtect)
{
size_t pageSize = GetPageSize();
EXPECT_TRUE(pageSize > 0);
std::vector<float> data = std::vector<float>(100);
size_t dataSize = sizeof(float) * data.size();
uintptr_t dataStart = reinterpret_cast<uintptr_t>(data.data());
uintptr_t protectionStart = rx::roundDownPow2(dataStart, pageSize);
uintptr_t protectionEnd = rx::roundUpPow2(dataStart + dataSize, pageSize);
std::mutex mutex;
bool handlerCalled = false;
PageFaultCallback callback = [&mutex, pageSize, dataStart, dataSize,
&handlerCalled](uintptr_t address) {
if (address >= dataStart && address < dataStart + dataSize)
{
std::lock_guard<std::mutex> lock(mutex);
uintptr_t pageStart = rx::roundDownPow2(address, pageSize);
EXPECT_TRUE(UnprotectMemory(pageStart, pageSize));
handlerCalled = true;
return PageFaultHandlerRangeType::InRange;
}
else
{
return PageFaultHandlerRangeType::OutOfRange;
}
};
std::unique_ptr<PageFaultHandler> handler(CreatePageFaultHandler(callback));
handler->enable();
size_t protectionSize = protectionEnd - protectionStart;
// Test Protect
EXPECT_TRUE(ProtectMemory(protectionStart, protectionSize));
data[0] = 0.0;
{
std::lock_guard<std::mutex> lock(mutex);
EXPECT_TRUE(handlerCalled);
}
// Test Protect and unprotect
EXPECT_TRUE(ProtectMemory(protectionStart, protectionSize));
EXPECT_TRUE(UnprotectMemory(protectionStart, protectionSize));
handlerCalled = false;
data[0] = 0.0;
EXPECT_FALSE(handlerCalled);
// Clean up
EXPECT_TRUE(handler->disable());
}
#if defined(ANGLE_PLATFORM_POSIX)
std::mutex gCustomHandlerMutex;
bool gCustomHandlerCalled = false;
void CustomSegfaultHandlerFunction(int sig, siginfo_t *info, void *unused)
{
std::lock_guard<std::mutex> lock(gCustomHandlerMutex);
gCustomHandlerCalled = true;
}
// Test if the default page fault handler is called on OutOfRange.
TEST(SystemUtils, MAYBE_PageFaultHandlerDefaultHandler)
{
size_t pageSize = GetPageSize();
EXPECT_TRUE(pageSize > 0);
std::vector<float> data = std::vector<float>(100);
size_t dataSize = sizeof(float) * data.size();
uintptr_t dataStart = reinterpret_cast<uintptr_t>(data.data());
uintptr_t protectionStart = rx::roundDownPow2(dataStart, pageSize);
uintptr_t protectionEnd = rx::roundUpPow2(dataStart + dataSize, pageSize);
size_t protectionSize = protectionEnd - protectionStart;
// Create custom handler
struct sigaction sigAction = {};
sigAction.sa_flags = SA_SIGINFO;
sigAction.sa_sigaction = &CustomSegfaultHandlerFunction;
sigemptyset(&sigAction.sa_mask);
struct sigaction oldSigSegv = {};
ASSERT_TRUE(sigaction(SIGSEGV, &sigAction, &oldSigSegv) == 0);
struct sigaction oldSigBus = {};
ASSERT_TRUE(sigaction(SIGBUS, &sigAction, &oldSigBus) == 0);
PageFaultCallback callback = [protectionStart, protectionSize](uintptr_t address) {
EXPECT_TRUE(UnprotectMemory(protectionStart, protectionSize));
return PageFaultHandlerRangeType::OutOfRange;
};
std::unique_ptr<PageFaultHandler> handler(CreatePageFaultHandler(callback));
handler->enable();
// Test Protect
EXPECT_TRUE(ProtectMemory(protectionStart, protectionSize));
data[0] = 0.0;
{
std::lock_guard<std::mutex> lock(gCustomHandlerMutex);
EXPECT_TRUE(gCustomHandlerCalled);
}
// Clean up
EXPECT_TRUE(handler->disable());
ASSERT_TRUE(sigaction(SIGSEGV, &oldSigSegv, nullptr) == 0);
ASSERT_TRUE(sigaction(SIGBUS, &oldSigBus, nullptr) == 0);
}
#else
TEST(SystemUtils, MAYBE_PageFaultHandlerDefaultHandler) {}
#endif
} // anonymous namespace