Add more test_utils functions.

Includes methods for creating temporary files, deleting files, and
reading files into a string. Also renames GetPathSeparator to mention
it's only used for environment variables. Includes a new virtual type
angle::Process that will be used to implement cross-platform async
Process launching for tests. Also includes a way to specify a custom
crash handler callback.

Also adds a few unit tests for the new functionality. They are disabled
on Android because the functions are not needed by the new test runner.

Bug: angleproject:3162
Change-Id: I3e2c2e9837608884c98379fa0f78c9ffbe158d73
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1821940
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jonah Ryan-Davis <jonahr@google.com>
This commit is contained in:
Jamie Madill 2019-10-09 10:26:25 -04:00 коммит произвёл Commit Bot
Родитель 7e23efd3a5
Коммит 5fcfcea4a9
20 изменённых файлов: 1088 добавлений и 453 удалений

Просмотреть файл

@ -22,7 +22,7 @@ bool PrependPathToEnvironmentVar(const char *variableName, const char *path)
else
{
buf = path;
buf += GetPathSeparator();
buf += GetPathSeparatorForEnvironmentVar();
buf += oldValue;
newValue = buf.c_str();
}

Просмотреть файл

@ -22,7 +22,7 @@ bool SetCWD(const char *dirName);
bool SetEnvironmentVar(const char *variableName, const char *value);
bool UnsetEnvironmentVar(const char *variableName);
std::string GetEnvironmentVar(const char *variableName);
const char *GetPathSeparator();
const char *GetPathSeparatorForEnvironmentVar();
bool PrependPathToEnvironmentVar(const char *variableName, const char *path);
bool IsDirectory(const char *filename);

Просмотреть файл

@ -18,56 +18,6 @@
namespace angle
{
namespace
{
struct ScopedPipe
{
~ScopedPipe()
{
closeEndPoint(0);
closeEndPoint(1);
}
void closeEndPoint(int index)
{
if (fds[index] >= 0)
{
close(fds[index]);
fds[index] = -1;
}
}
int fds[2] = {
-1,
-1,
};
};
void ReadEntireFile(int fd, std::string *out)
{
out->clear();
while (true)
{
char buffer[256];
ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
// If interrupted, retry.
if (bytesRead < 0 && errno == EINTR)
{
continue;
}
// If failed, or nothing to read, we are done.
if (bytesRead <= 0)
{
break;
}
out->append(buffer, bytesRead);
}
}
} // anonymous namespace
Optional<std::string> GetCWD()
{
std::array<char, 4096> pathBuf;
@ -100,117 +50,11 @@ std::string GetEnvironmentVar(const char *variableName)
return (value == nullptr ? std::string() : std::string(value));
}
const char *GetPathSeparator()
const char *GetPathSeparatorForEnvironmentVar()
{
return ":";
}
bool RunApp(const std::vector<const char *> &args,
std::string *stdoutOut,
std::string *stderrOut,
int *exitCodeOut)
{
if (args.size() == 0 || args.back() != nullptr)
{
return false;
}
ScopedPipe stdoutPipe;
ScopedPipe stderrPipe;
// Create pipes for stdout and stderr.
if (stdoutOut && pipe(stdoutPipe.fds) != 0)
{
return false;
}
if (stderrOut && pipe(stderrPipe.fds) != 0)
{
return false;
}
pid_t pid = fork();
if (pid < 0)
{
return false;
}
if (pid == 0)
{
// Child. Execute the application.
// Redirect stdout and stderr to the pipe fds.
if (stdoutOut)
{
if (dup2(stdoutPipe.fds[1], STDOUT_FILENO) < 0)
{
_exit(errno);
}
}
if (stderrOut)
{
if (dup2(stderrPipe.fds[1], STDERR_FILENO) < 0)
{
_exit(errno);
}
}
// Execute the application, which doesn't return unless failed. Note: execv takes argv as
// `char * const *` for historical reasons. It is safe to const_cast it:
//
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
//
// > The statement about argv[] and envp[] being constants is included to make explicit to
// future writers of language bindings that these objects are completely constant. Due to a
// limitation of the ISO C standard, it is not possible to state that idea in standard C.
// Specifying two levels of const- qualification for the argv[] and envp[] parameters for
// the exec functions may seem to be the natural choice, given that these functions do not
// modify either the array of pointers or the characters to which the function points, but
// this would disallow existing correct code. Instead, only the array of pointers is noted
// as constant.
execv(args[0], const_cast<char *const *>(args.data()));
_exit(errno);
}
// Parent. Read child output from the pipes and clean it up.
// Close the write end of the pipes, so EOF can be generated when child exits.
stdoutPipe.closeEndPoint(1);
stderrPipe.closeEndPoint(1);
// Read back the output of the child.
if (stdoutOut)
{
ReadEntireFile(stdoutPipe.fds[0], stdoutOut);
}
if (stderrOut)
{
ReadEntireFile(stderrPipe.fds[0], stderrOut);
}
// Cleanup the child.
int status = 0;
do
{
pid_t changedPid = waitpid(pid, &status, 0);
if (changedPid < 0 && errno == EINTR)
{
continue;
}
if (changedPid < 0)
{
return false;
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
// Retrieve the error code.
if (exitCodeOut)
{
*exitCodeOut = WEXITSTATUS(status);
}
return true;
}
class PosixLibrary : public Library
{
public:

Просмотреть файл

@ -9,46 +9,11 @@
#include "gtest/gtest.h"
#include "common/system_utils.h"
#include "common/system_utils_unittest_helper.h"
using namespace angle;
namespace
{
#if defined(ANGLE_PLATFORM_WINDOWS)
constexpr char kRunAppHelperExecutable[] = "angle_unittests_helper.exe";
#else
constexpr char kRunAppHelperExecutable[] = "angle_unittests_helper";
#endif
// Transforms various line endings into C/Unix line endings:
//
// - A\nB -> A\nB
// - A\rB -> A\nB
// - A\r\nB -> A\nB
std::string NormalizeNewLines(const std::string &str)
{
std::string result;
for (size_t i = 0; i < str.size(); ++i)
{
if (str[i] == '\r')
{
if (i + 1 < str.size() && str[i + 1] == '\n')
{
++i;
}
result += '\n';
}
else
{
result += str[i];
}
}
return result;
}
// Test getting the executable path
TEST(SystemUtils, ExecutablePath)
{
@ -95,48 +60,4 @@ TEST(SystemUtils, Environment)
readback = GetEnvironmentVar(kEnvVarName);
EXPECT_EQ("", readback);
}
// Test running an external application and receiving its output
TEST(SystemUtils, RunApp)
{
#if defined(ANGLE_PLATFORM_ANDROID)
// TODO: android support. http://anglebug.com/3125
return;
#endif
#if defined(ANGLE_PLATFORM_FUCHSIA)
// TODO: fuchsia support. http://anglebug.com/3161
return;
#endif
std::string executablePath = GetExecutableDirectory();
EXPECT_NE(executablePath, "");
executablePath += "/";
executablePath += kRunAppHelperExecutable;
std::vector<const char *> args = {executablePath.c_str(), kRunAppTestArg1, kRunAppTestArg2,
nullptr};
std::string stdoutOutput;
std::string stderrOutput;
int exitCode = EXIT_FAILURE;
// Test that the application can be executed.
bool ranApp = RunApp(args, &stdoutOutput, &stderrOutput, &exitCode);
EXPECT_TRUE(ranApp);
EXPECT_EQ(kRunAppTestStdout, NormalizeNewLines(stdoutOutput));
EXPECT_EQ(kRunAppTestStderr, NormalizeNewLines(stderrOutput));
EXPECT_EQ(EXIT_SUCCESS, exitCode);
// Test that environment variables reach the cild.
bool setEnvDone = SetEnvironmentVar(kRunAppTestEnvVarName, kRunAppTestEnvVarValue);
EXPECT_TRUE(setEnvDone);
ranApp = RunApp(args, &stdoutOutput, &stderrOutput, &exitCode);
EXPECT_TRUE(ranApp);
EXPECT_EQ("", stdoutOutput);
EXPECT_EQ(kRunAppTestEnvVarValue, NormalizeNewLines(stderrOutput));
EXPECT_EQ(EXIT_SUCCESS, exitCode);
}
} // anonymous namespace

Просмотреть файл

@ -15,56 +15,6 @@
namespace angle
{
namespace
{
struct ScopedPipe
{
~ScopedPipe()
{
closeReadHandle();
closeWriteHandle();
}
void closeReadHandle()
{
if (readHandle)
{
CloseHandle(readHandle);
readHandle = nullptr;
}
}
void closeWriteHandle()
{
if (writeHandle)
{
CloseHandle(writeHandle);
writeHandle = nullptr;
}
}
HANDLE readHandle = nullptr;
HANDLE writeHandle = nullptr;
};
void ReadEntireFile(HANDLE handle, std::string *out)
{
out->clear();
while (true)
{
char buffer[256];
DWORD bytesRead;
BOOL success = ReadFile(handle, buffer, sizeof(buffer), &bytesRead, nullptr);
if (!success || bytesRead == 0)
{
break;
}
out->append(buffer, bytesRead);
}
}
} // anonymous namespace
std::string GetExecutablePath()
{
std::array<char, MAX_PATH> executableFileBuf;
@ -126,7 +76,7 @@ std::string GetEnvironmentVar(const char *variableName)
}
}
const char *GetPathSeparator()
const char *GetPathSeparatorForEnvironmentVar()
{
return ";";
}
@ -142,117 +92,6 @@ double GetCurrentTime()
return static_cast<double>(curTime.QuadPart) / frequency.QuadPart;
}
bool RunApp(const std::vector<const char *> &args,
std::string *stdoutOut,
std::string *stderrOut,
int *exitCodeOut)
{
ScopedPipe stdoutPipe;
ScopedPipe stderrPipe;
SECURITY_ATTRIBUTES sa_attr;
// Set the bInheritHandle flag so pipe handles are inherited.
sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
sa_attr.bInheritHandle = TRUE;
sa_attr.lpSecurityDescriptor = nullptr;
// Create pipes for stdout and stderr. Ensure the read handles to the pipes are not inherited.
if (stdoutOut && !CreatePipe(&stdoutPipe.readHandle, &stdoutPipe.writeHandle, &sa_attr, 0) &&
!SetHandleInformation(stdoutPipe.readHandle, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
if (stderrOut && !CreatePipe(&stderrPipe.readHandle, &stderrPipe.writeHandle, &sa_attr, 0) &&
!SetHandleInformation(stderrPipe.readHandle, HANDLE_FLAG_INHERIT, 0))
{
return false;
}
// Concat the nicely separated arguments into one string so the application has to reparse it.
// We don't support quotation and spaces in arguments currently.
std::vector<char> commandLineString;
for (const char *arg : args)
{
if (arg)
{
if (!commandLineString.empty())
{
commandLineString.push_back(' ');
}
commandLineString.insert(commandLineString.end(), arg, arg + strlen(arg));
}
}
commandLineString.push_back('\0');
STARTUPINFOA startInfo = {};
startInfo.cb = sizeof(STARTUPINFOA);
startInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
if (stdoutOut)
{
startInfo.hStdOutput = stdoutPipe.writeHandle;
}
else
{
startInfo.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
}
if (stderrOut)
{
startInfo.hStdError = stderrPipe.writeHandle;
}
else
{
startInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
}
if (stderrOut || stdoutOut)
{
startInfo.dwFlags |= STARTF_USESTDHANDLES;
}
// Create the child process.
PROCESS_INFORMATION processInfo = {};
if (!CreateProcessA(nullptr, commandLineString.data(), nullptr, nullptr,
TRUE, // Handles are inherited.
0, nullptr, nullptr, &startInfo, &processInfo))
{
return false;
}
// Close the write end of the pipes, so EOF can be generated when child exits.
stdoutPipe.closeWriteHandle();
stderrPipe.closeWriteHandle();
// Read back the output of the child.
if (stdoutOut)
{
ReadEntireFile(stdoutPipe.readHandle, stdoutOut);
}
if (stderrOut)
{
ReadEntireFile(stderrPipe.readHandle, stderrOut);
}
// Cleanup the child.
bool success = WaitForSingleObject(processInfo.hProcess, INFINITE) == WAIT_OBJECT_0;
if (success)
{
DWORD exitCode = 0;
success = GetExitCodeProcess(processInfo.hProcess, &exitCode);
if (success)
{
*exitCodeOut = static_cast<int>(exitCode);
}
}
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
return success;
}
class Win32Library : public Library
{
public:
@ -326,4 +165,5 @@ void BreakDebugger()
{
__debugbreak();
}
} // namespace angle

Просмотреть файл

@ -45,8 +45,8 @@ if (!build_with_chromium) {
}
}
angle_executable("angle_unittests_helper") {
sources = angle_unittests_helper_sources
angle_executable("test_utils_unittest_helper") {
sources = test_utils_unittest_helper_sources
deps = [
"${angle_root}:angle_common",
@ -104,7 +104,7 @@ angle_test("angle_unittests") {
# SystemUtils.RunApp, the only unittest using a helper binary, is not supported on these
# platforms yet.
data_deps = [
":angle_unittests_helper",
":test_utils_unittest_helper",
]
}
}

Просмотреть файл

@ -15,7 +15,6 @@ angle_unittests_sources = [
"../common/matrix_utils_unittest.cpp",
"../common/string_utils_unittest.cpp",
"../common/system_utils_unittest.cpp",
"../common/system_utils_unittest_helper.h",
"../common/utilities_unittest.cpp",
"../common/vector_utils_unittest.cpp",
"../feature_support_util/feature_support_util_unittest.cpp",
@ -130,6 +129,8 @@ angle_unittests_sources = [
"../tests/test_utils/ShaderCompileTreeTest.h",
"../tests/test_utils/ShaderCompileTreeTest.cpp",
"../tests/test_utils/ShaderExtensionTest.h",
"../../util/test_utils_unittest.cpp",
"../../util/test_utils_unittest_helper.h",
]
# TODO(jmadill): should probably call this windows sources
@ -138,9 +139,9 @@ angle_unittests_hlsl_sources = [
"../tests/compiler_tests/UnrollFlatten_test.cpp",
]
angle_unittests_helper_sources = [
"../common/system_utils_unittest_helper.cpp",
"../common/system_utils_unittest_helper.h",
test_utils_unittest_helper_sources = [
"../../util/test_utils_unittest_helper.cpp",
"../../util/test_utils_unittest_helper.h",
]
if (is_android) {

Просмотреть файл

@ -151,10 +151,9 @@ class GLMark2Benchmark : public testing::TestWithParam<GLMark2BenchmarkTestParam
}
args.push_back(nullptr);
std::string output;
int exitCode;
bool success = RunApp(args, &output, nullptr, &exitCode);
ProcessHandle process(args, true, false);
ASSERT_TRUE(process && process->started());
ASSERT_TRUE(process->finish());
// Restore the current working directory for the next tests.
if (cwd.valid())
@ -162,11 +161,11 @@ class GLMark2Benchmark : public testing::TestWithParam<GLMark2BenchmarkTestParam
SetCWD(cwd.value().c_str());
}
ASSERT_TRUE(success);
ASSERT_EQ(EXIT_SUCCESS, exitCode);
ASSERT_EQ(EXIT_SUCCESS, process->getExitCode());
if (!OneFrame())
{
std::string output = process->getStdout();
parseOutput(output, benchmarkName, completeRun);
}
}

Просмотреть файл

@ -320,15 +320,15 @@ bool RunSeparateProcessesForEachConfig(int *argc, char *argv[])
std::vector<const char *> childArgs = commonArgs;
childArgs.push_back(configStr.c_str());
int exitCode = 0;
if (!RunApp(childArgs, nullptr, nullptr, &exitCode))
ProcessHandle process(childArgs, false, false);
if (!process->started() || !process->finish())
{
std::cerr << "Launching child config " << config << " failed.\n";
}
else if (exitCode != 0)
else if (process->getExitCode() != 0)
{
std::cerr << "Child config " << config << " failed with exit code " << exitCode
<< ".\n";
std::cerr << "Child config " << config << " failed with exit code "
<< process->getExitCode() << ".\n";
success = false;
}
}
@ -481,7 +481,7 @@ void ANGLETestBase::ANGLETestSetUp()
{
mSetUpCalled = true;
InitCrashHandler();
InitCrashHandler(nullptr);
gDefaultPlatformMethods.overrideWorkaroundsD3D = TestPlatform_overrideWorkaroundsD3D;
gDefaultPlatformMethods.overrideFeaturesVk = TestPlatform_overrideFeaturesVk;

Просмотреть файл

@ -43,7 +43,7 @@ void PrintStackBacktrace()
// No implementations yet.
}
void InitCrashHandler()
void InitCrashHandler(CrashCallback *callback)
{
// No implementations yet.
}
@ -140,7 +140,7 @@ static constexpr int kSignals[] = {
SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP,
};
void InitCrashHandler()
void InitCrashHandler(CrashCallback *callback)
{
for (int sig : kSignals)
{

Просмотреть файл

@ -10,10 +10,13 @@
#include <errno.h>
#include <sched.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <cstdarg>
#include <cstring>
#include "common/debug.h"
#include "common/platform.h"
#if !defined(ANGLE_PLATFORM_FUCHSIA)
@ -26,6 +29,222 @@
namespace angle
{
namespace
{
struct ScopedPipe
{
~ScopedPipe()
{
closeEndPoint(0);
closeEndPoint(1);
}
void closeEndPoint(int index)
{
if (fds[index] >= 0)
{
close(fds[index]);
fds[index] = -1;
}
}
bool valid() const { return fds[0] != -1 || fds[1] != -1; }
int fds[2] = {
-1,
-1,
};
};
void ReadEntireFile(int fd, std::string *out)
{
out->clear();
while (true)
{
char buffer[256];
ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
// If interrupted, retry.
if (bytesRead < 0 && errno == EINTR)
{
continue;
}
// If failed, or nothing to read, we are done.
if (bytesRead <= 0)
{
break;
}
out->append(buffer, bytesRead);
}
}
class PosixProcess : public Process
{
public:
PosixProcess(const std::vector<const char *> &commandLineArgs,
bool captureStdOut,
bool captureStdErr)
{
#if defined(ANGLE_PLATFORM_FUCHSIA)
ANGLE_UNUSED_VARIABLE(ReadEntireFile);
ANGLE_UNUSED_VARIABLE(mExitCode);
ANGLE_UNUSED_VARIABLE(mPID);
#else
if (commandLineArgs.empty() || commandLineArgs.back() != nullptr)
{
return;
}
// Create pipes for stdout and stderr.
if (captureStdOut && pipe(mStdoutPipe.fds) != 0)
{
return;
}
if (captureStdErr && pipe(mStderrPipe.fds) != 0)
{
return;
}
mPID = fork();
if (mPID < 0)
{
return;
}
mStarted = true;
if (mPID == 0)
{
// Child. Execute the application.
// Redirect stdout and stderr to the pipe fds.
if (captureStdOut)
{
if (dup2(mStdoutPipe.fds[1], STDOUT_FILENO) < 0)
{
_exit(errno);
}
}
if (captureStdErr)
{
if (dup2(mStderrPipe.fds[1], STDERR_FILENO) < 0)
{
_exit(errno);
}
}
// Execute the application, which doesn't return unless failed. Note: execv takes argv
// as `char * const *` for historical reasons. It is safe to const_cast it:
//
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
//
// > The statement about argv[] and envp[] being constants is included to make explicit
// to future writers of language bindings that these objects are completely constant.
// Due to a limitation of the ISO C standard, it is not possible to state that idea in
// standard C. Specifying two levels of const- qualification for the argv[] and envp[]
// parameters for the exec functions may seem to be the natural choice, given that these
// functions do not modify either the array of pointers or the characters to which the
// function points, but this would disallow existing correct code. Instead, only the
// array of pointers is noted as constant.
execv(commandLineArgs[0], const_cast<char *const *>(commandLineArgs.data()));
_exit(errno);
}
// Parent continues execution.
#endif // defined(ANGLE_PLATFORM_FUCHSIA)
}
~PosixProcess() override {}
bool started() override { return mStarted; }
bool finish() override
{
if (!mStarted)
{
return false;
}
#if defined(ANGLE_PLATFORM_FUCHSIA)
return false;
#else
// Close the write end of the pipes, so EOF can be generated when child exits.
// Then read back the output of the child.
if (mStdoutPipe.valid())
{
mStdoutPipe.closeEndPoint(1);
ReadEntireFile(mStdoutPipe.fds[0], &mStdout);
}
if (mStderrPipe.valid())
{
mStderrPipe.closeEndPoint(1);
ReadEntireFile(mStderrPipe.fds[0], &mStderr);
}
// Cleanup the child.
int status = 0;
do
{
pid_t changedPid = waitpid(mPID, &status, 0);
if (changedPid < 0 && errno == EINTR)
{
continue;
}
if (changedPid < 0)
{
return false;
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
// Retrieve the error code.
mExitCode = WEXITSTATUS(status);
return true;
#endif // defined(ANGLE_PLATFORM_FUCHSIA)
}
bool finished() override
{
if (!mStarted)
{
return false;
}
return (::kill(mPID, 0) != 0);
}
int getExitCode() override { return 0; }
bool kill() override
{
if (!mStarted)
{
return false;
}
if (finished())
{
return true;
}
return (::kill(mPID, SIGTERM) == 0);
}
private:
bool mStarted = false;
ScopedPipe mStdoutPipe;
ScopedPipe mStderrPipe;
int mExitCode = 0;
pid_t mPID = -1;
};
std::string TempFileName()
{
return std::string(".angle.XXXXXX");
}
} // anonymous namespace
void Sleep(unsigned int milliseconds)
{
// On Windows Sleep(0) yields while it isn't guaranteed by Posix's sleep
@ -94,4 +313,68 @@ bool StabilizeCPUForBenchmarking()
return false;
#endif
}
bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen)
{
const char *tmp = getenv("TMPDIR");
if (tmp)
{
strncpy(tempDirOut, tmp, maxDirNameLen);
return true;
}
#if defined(ANGLE_PLATFORM_ANDROID)
// TODO(jmadill): Android support. http://anglebug.com/3162
// return PathService::Get(DIR_CACHE, path);
return false;
#else
strncpy(tempDirOut, "/tmp", maxDirNameLen);
return true;
#endif
}
bool CreateTemporaryFileInDir(const char *dir, char *tempFileNameOut, uint32_t maxFileNameLen)
{
std::string tempFile = TempFileName();
sprintf(tempFileNameOut, "%s/%s", dir, tempFile.c_str());
int fd = mkstemp(tempFileNameOut);
close(fd);
return fd != -1;
}
bool DeleteFile(const char *path)
{
return unlink(path) == 0;
}
Process *LaunchProcess(const std::vector<const char *> &args,
bool captureStdout,
bool captureStderr)
{
return new PosixProcess(args, captureStdout, captureStderr);
}
int NumberOfProcessors()
{
// sysconf returns the number of "logical" (not "physical") processors on both
// Mac and Linux. So we get the number of max available "logical" processors.
//
// Note that the number of "currently online" processors may be fewer than the
// returned value of NumberOfProcessors(). On some platforms, the kernel may
// make some processors offline intermittently, to save power when system
// loading is low.
//
// One common use case that needs to know the processor count is to create
// optimal number of threads for optimization. It should make plan according
// to the number of "max available" processors instead of "currently online"
// ones. The kernel should be smart enough to make all processors online when
// it has sufficient number of threads waiting to run.
long res = sysconf(_SC_NPROCESSORS_CONF);
if (res == -1)
{
return 1;
}
return static_cast<int>(res);
}
} // namespace angle

Просмотреть файл

@ -4,33 +4,26 @@
// found in the LICENSE file.
//
#include "shader_utils.h"
#include "util/shader_utils.h"
#include <cstring>
#include <fstream>
#include <iostream>
#include <vector>
#include "util/test_utils.h"
namespace
{
std::string ReadFileToString(const std::string &source)
bool ReadEntireFile(const std::string &filePath, std::string *contentsOut)
{
std::ifstream stream(source.c_str());
if (!stream)
{
std::cerr << "Failed to load shader file: " << source;
return "";
}
std::string result;
stream.seekg(0, std::ios::end);
result.reserve(static_cast<unsigned int>(stream.tellg()));
stream.seekg(0, std::ios::beg);
result.assign((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
return result;
constexpr uint32_t kMaxBufferSize = 2000;
char buffer[kMaxBufferSize] = {};
if (!angle::ReadEntireFileToString(filePath.c_str(), buffer, kMaxBufferSize) ||
strlen(buffer) == 0)
return false;
*contentsOut = buffer;
return true;
}
GLuint CompileProgramInternal(const char *vsSource,
@ -124,9 +117,10 @@ GLuint CompileShader(GLenum type, const char *source)
GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath)
{
std::string source = ReadFileToString(sourcePath);
if (source.empty())
std::string source;
if (!ReadEntireFile(sourcePath, &source))
{
std::cerr << "Error reading shader file: " << sourcePath << "\n";
return 0;
}
@ -214,10 +208,17 @@ GLuint CompileProgramWithGS(const char *vsSource, const char *gsSource, const ch
GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath)
{
std::string vsSource = ReadFileToString(vsPath);
std::string fsSource = ReadFileToString(fsPath);
if (vsSource.empty() || fsSource.empty())
std::string vsSource;
if (!ReadEntireFile(vsPath, &vsSource))
{
std::cerr << "Error reading shader: " << vsPath << "\n";
return 0;
}
std::string fsSource;
if (!ReadEntireFile(fsPath, &fsSource))
{
std::cerr << "Error reading shader: " << fsPath << "\n";
return 0;
}

97
util/test_utils.cpp Normal file
Просмотреть файл

@ -0,0 +1,97 @@
//
// 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: Defines common utility functions
#include "util/test_utils.h"
#include <cstring>
#include <fstream>
namespace angle
{
bool CreateTemporaryFile(char *tempFileNameOut, uint32_t maxFileNameLen)
{
constexpr uint32_t kMaxPath = 1000u;
char tempPath[kMaxPath];
if (!GetTempDir(tempPath, kMaxPath))
return false;
return CreateTemporaryFileInDir(tempPath, tempFileNameOut, maxFileNameLen);
}
bool GetFileSize(const char *filePath, uint32_t *sizeOut)
{
std::ifstream stream(filePath);
if (!stream)
{
return false;
}
stream.seekg(0, std::ios::end);
*sizeOut = static_cast<uint32_t>(stream.tellg());
return true;
}
bool ReadEntireFileToString(const char *filePath, char *contentsOut, uint32_t maxLen)
{
std::ifstream stream(filePath);
if (!stream)
{
return false;
}
std::string contents;
stream.seekg(0, std::ios::end);
contents.reserve(static_cast<unsigned int>(stream.tellg()));
stream.seekg(0, std::ios::beg);
contents.assign((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
strncpy(contentsOut, contents.c_str(), maxLen);
return true;
}
// static
Process::~Process() = default;
ProcessHandle::ProcessHandle() : mProcess(nullptr) {}
ProcessHandle::ProcessHandle(Process *process) : mProcess(process) {}
ProcessHandle::ProcessHandle(const std::vector<const char *> &args,
bool captureStdout,
bool captureStderr)
: mProcess(LaunchProcess(args, captureStdout, captureStderr))
{}
ProcessHandle::~ProcessHandle()
{
reset();
}
ProcessHandle::ProcessHandle(ProcessHandle &&other) : mProcess(other.mProcess)
{
other.mProcess = nullptr;
}
ProcessHandle &ProcessHandle::operator=(ProcessHandle &&rhs)
{
std::swap(mProcess, rhs.mProcess);
return *this;
}
void ProcessHandle::reset()
{
if (mProcess)
{
delete mProcess;
mProcess = nullptr;
}
}
} // namespace angle

Просмотреть файл

@ -9,9 +9,12 @@
#ifndef UTIL_TEST_UTILS_H_
#define UTIL_TEST_UTILS_H_
#include <functional>
#include <string>
#include <vector>
#include "common/angleutils.h"
#include "util/Timer.h"
#include "util/util_export.h"
namespace angle
@ -28,12 +31,96 @@ ANGLE_UTIL_EXPORT void WriteDebugMessage(const char *format, ...);
ANGLE_UTIL_EXPORT bool StabilizeCPUForBenchmarking();
// Set a crash handler to print stack traces.
ANGLE_UTIL_EXPORT void InitCrashHandler();
using CrashCallback = std::function<void()>;
ANGLE_UTIL_EXPORT void InitCrashHandler(CrashCallback *callback);
ANGLE_UTIL_EXPORT void TerminateCrashHandler();
// Print a stack back trace.
ANGLE_UTIL_EXPORT void PrintStackBacktrace();
// Get temporary directory.
ANGLE_UTIL_EXPORT bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen);
// Creates a temporary file. The full path is placed in |path|, and the
// function returns true if was successful in creating the file. The file will
// be empty and all handles closed after this function returns.
ANGLE_UTIL_EXPORT bool CreateTemporaryFile(char *tempFileNameOut, uint32_t maxFileNameLen);
// Same as CreateTemporaryFile but the file is created in |dir|.
ANGLE_UTIL_EXPORT bool CreateTemporaryFileInDir(const char *dir,
char *tempFileNameOut,
uint32_t maxFileNameLen);
// Deletes a file or directory.
ANGLE_UTIL_EXPORT bool DeleteFile(const char *path);
// Reads a file contents into a string.
ANGLE_UTIL_EXPORT bool ReadEntireFileToString(const char *filePath,
char *contentsOut,
uint32_t maxLen);
// Compute a file's size.
ANGLE_UTIL_EXPORT bool GetFileSize(const char *filePath, uint32_t *sizeOut);
class ProcessHandle;
class ANGLE_UTIL_EXPORT Process : angle::NonCopyable
{
public:
virtual bool started() = 0;
virtual bool finished() = 0;
virtual bool finish() = 0;
virtual bool kill() = 0;
virtual int getExitCode() = 0;
double getElapsedTimeSeconds() const { return mTimer.getElapsedTime(); }
const std::string &getStdout() const { return mStdout; }
const std::string &getStderr() const { return mStderr; }
protected:
friend class ProcessHandle;
virtual ~Process();
Timer mTimer;
std::string mStdout;
std::string mStderr;
};
class ANGLE_UTIL_EXPORT ProcessHandle final : angle::NonCopyable
{
public:
ProcessHandle();
ProcessHandle(Process *process);
ProcessHandle(const std::vector<const char *> &args, bool captureStdout, bool captureStderr);
~ProcessHandle();
ProcessHandle(ProcessHandle &&other);
ProcessHandle &operator=(ProcessHandle &&rhs);
Process *operator->() { return mProcess; }
const Process *operator->() const { return mProcess; }
operator bool() const { return mProcess != nullptr; }
void reset();
private:
Process *mProcess;
};
// Launch a process and optionally get the output. Uses a vector of c strings as command line
// arguments to the child process. Returns a Process handle which can be used to retrieve
// the stdout and stderr outputs as well as the exit code.
//
// Pass false for stdoutOut/stderrOut if you don't need to capture them.
//
// On success, returns a Process pointer with started() == true.
// On failure, returns a Process pointer with started() == false.
ANGLE_UTIL_EXPORT Process *LaunchProcess(const std::vector<const char *> &args,
bool captureStdout,
bool captureStderr);
ANGLE_UTIL_EXPORT int NumberOfProcessors();
} // namespace angle
#endif // UTIL_TEST_UTILS_H_

Просмотреть файл

@ -0,0 +1,184 @@
//
// 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.
// test_utils_unittest.cpp: Unit tests for ANGLE's test utility functions
#include "gtest/gtest.h"
#include "common/system_utils.h"
#include "util/Timer.h"
#include "util/test_utils.h"
#include "util/test_utils_unittest_helper.h"
using namespace angle;
namespace
{
#if defined(ANGLE_PLATFORM_WINDOWS)
constexpr char kRunAppHelperExecutable[] = "test_utils_unittest_helper.exe";
#else
constexpr char kRunAppHelperExecutable[] = "test_utils_unittest_helper";
#endif
// Transforms various line endings into C/Unix line endings:
//
// - A\nB -> A\nB
// - A\rB -> A\nB
// - A\r\nB -> A\nB
std::string NormalizeNewLines(const std::string &str)
{
std::string result;
for (size_t i = 0; i < str.size(); ++i)
{
if (str[i] == '\r')
{
if (i + 1 < str.size() && str[i + 1] == '\n')
{
++i;
}
result += '\n';
}
else
{
result += str[i];
}
}
return result;
}
// Tests that Sleep() actually waits some time.
TEST(TestUtils, Sleep)
{
Timer timer;
timer.start();
angle::Sleep(500);
timer.stop();
// Use a slightly fuzzy range
EXPECT_GT(timer.getElapsedTime(), 0.48);
}
constexpr uint32_t kMaxPath = 1000;
// Temporary file creation is not supported on Android right now.
#if defined(ANGLE_PLATFORM_ANDROID)
# define MAYBE_CreateAndDeleteTemporaryFile DISABLED_CreateAndDeleteTemporaryFile
# define MAYBE_CreateAndDeleteFileInTempDir DISABLED_CreateAndDeleteFileInTempDir
#else
# define MAYBE_CreateAndDeleteTemporaryFile CreateAndDeleteTemporaryFile
# define MAYBE_CreateAndDeleteFileInTempDir CreateAndDeleteFileInTempDir
#endif // defined(ANGLE_PLATFORM_ANDROID)
// Test creating and deleting temporary file.
TEST(TestUtils, MAYBE_CreateAndDeleteTemporaryFile)
{
char path[kMaxPath] = {};
ASSERT_TRUE(CreateTemporaryFile(path, kMaxPath));
ASSERT_TRUE(strlen(path) > 0);
const char kOutputString[] = "test output";
FILE *fp = fopen(path, "wt");
ASSERT_NE(fp, nullptr);
int retval = fputs(kOutputString, fp);
fclose(fp);
EXPECT_GE(retval, 0);
// Test ReadEntireFileToString
char actualString[kMaxPath];
EXPECT_TRUE(ReadEntireFileToString(path, actualString, kMaxPath));
EXPECT_EQ(strcmp(actualString, kOutputString), 0);
// Delete the temporary file.
EXPECT_TRUE(angle::DeleteFile(path));
}
// Tests creating and deleting a file in the system temp dir.
TEST(TestUtils, MAYBE_CreateAndDeleteFileInTempDir)
{
char tempDir[kMaxPath];
ASSERT_TRUE(GetTempDir(tempDir, kMaxPath));
char path[kMaxPath] = {};
ASSERT_TRUE(CreateTemporaryFileInDir(tempDir, path, kMaxPath));
ASSERT_TRUE(strlen(path) > 0);
const char kOutputString[] = "test output";
FILE *fp = fopen(path, "wt");
ASSERT_NE(fp, nullptr);
int retval = fputs(kOutputString, fp);
fclose(fp);
EXPECT_GE(retval, 0);
// Test ReadEntireFileToString
char actualString[kMaxPath];
EXPECT_TRUE(ReadEntireFileToString(path, actualString, kMaxPath));
EXPECT_EQ(strcmp(actualString, kOutputString), 0);
// Delete the temporary file.
EXPECT_TRUE(angle::DeleteFile(path));
}
// Test running an external application and receiving its output
TEST(TestUtils, RunApp)
{
#if defined(ANGLE_PLATFORM_ANDROID)
// TODO: android support. http://anglebug.com/3125
return;
#endif
#if defined(ANGLE_PLATFORM_FUCHSIA)
// TODO: fuchsia support. http://anglebug.com/3161
return;
#endif
std::string executablePath = GetExecutableDirectory();
EXPECT_NE(executablePath, "");
executablePath += "/";
executablePath += kRunAppHelperExecutable;
std::vector<const char *> args = {executablePath.c_str(), kRunAppTestArg1, kRunAppTestArg2,
nullptr};
// Test that the application can be executed.
{
ProcessHandle process(args, true, true);
EXPECT_TRUE(process->started());
EXPECT_TRUE(process->finish());
EXPECT_TRUE(process->finished());
EXPECT_EQ(kRunAppTestStdout, NormalizeNewLines(process->getStdout()));
EXPECT_EQ(kRunAppTestStderr, NormalizeNewLines(process->getStderr()));
EXPECT_EQ(EXIT_SUCCESS, process->getExitCode());
}
// Test that environment variables reach the cild.
{
bool setEnvDone = SetEnvironmentVar(kRunAppTestEnvVarName, kRunAppTestEnvVarValue);
EXPECT_TRUE(setEnvDone);
ProcessHandle process(LaunchProcess(args, true, true));
EXPECT_TRUE(process->started());
EXPECT_TRUE(process->finish());
EXPECT_EQ("", process->getStdout());
EXPECT_EQ(kRunAppTestEnvVarValue, NormalizeNewLines(process->getStderr()));
EXPECT_EQ(EXIT_SUCCESS, process->getExitCode());
}
}
// Verify that NumberOfProcessors returns something sane.
TEST(TestUtils, NumberOfProcessors)
{
int numProcs = angle::NumberOfProcessors();
EXPECT_GT(numProcs, 0);
EXPECT_LT(numProcs, 1000);
}
} // namespace

Просмотреть файл

@ -5,10 +5,12 @@
// system_utils_unittest_helper.cpp: Helper to the SystemUtils.RunApp unittest
#include "common/system_utils_unittest_helper.h"
#include <string.h>
#include "test_utils_unittest_helper.h"
#include "common/system_utils.h"
#include <string.h>
int main(int argc, char **argv)
{
if (argc != 3 || strcmp(argv[1], kRunAppTestArg1) != 0 || strcmp(argv[2], kRunAppTestArg2) != 0)

Просмотреть файл

@ -12,6 +12,7 @@ util_sources = [
"util/random_utils.h",
"util/shader_utils.cpp",
"util/shader_utils.h",
"util/test_utils.cpp",
"util/test_utils.h",
"util/util_export.h",
"util/util_gl.h",

Просмотреть файл

@ -11,8 +11,14 @@
#include <stdarg.h>
#include <windows.h>
#include <array>
#include <iostream>
#include <vector>
#include <aclapi.h>
#include "common/angleutils.h"
#include "anglebase/no_destructor.h"
#include "util/windows/third_party/StackWalker/src/StackWalker.h"
namespace angle
@ -105,6 +111,312 @@ LONG WINAPI StackTraceCrashHandler(EXCEPTION_POINTERS *e)
// The compiler wants us to return something. This is what we'd do if we didn't _exit().
return EXCEPTION_EXECUTE_HANDLER;
}
CrashCallback *gCrashHandlerCallback;
LONG WINAPI CrashHandler(EXCEPTION_POINTERS *e)
{
if (gCrashHandlerCallback)
{
(*gCrashHandlerCallback)();
}
return StackTraceCrashHandler(e);
}
struct ScopedPipe
{
~ScopedPipe()
{
closeReadHandle();
closeWriteHandle();
}
bool closeReadHandle()
{
if (readHandle)
{
if (::CloseHandle(readHandle) == FALSE)
{
std::cerr << "Error closing write handle: " << GetLastError();
return false;
}
readHandle = nullptr;
}
return true;
}
bool closeWriteHandle()
{
if (writeHandle)
{
if (::CloseHandle(writeHandle) == FALSE)
{
std::cerr << "Error closing write handle: " << GetLastError();
return false;
}
writeHandle = nullptr;
}
return true;
}
bool valid() const { return readHandle != nullptr || writeHandle != nullptr; }
bool initPipe(SECURITY_ATTRIBUTES *securityAttribs)
{
if (::CreatePipe(&readHandle, &writeHandle, securityAttribs, 0) == FALSE)
{
std::cerr << "Error creating pipe: " << GetLastError() << "\n";
return false;
}
// Ensure the read handles to the pipes are not inherited.
if (::SetHandleInformation(readHandle, HANDLE_FLAG_INHERIT, 0) == FALSE)
{
std::cerr << "Error setting handle info on pipe: " << GetLastError() << "\n";
return false;
}
return true;
}
HANDLE readHandle = nullptr;
HANDLE writeHandle = nullptr;
};
// Returns false on EOF or error.
void ReadFromFile(bool blocking, HANDLE handle, std::string *out)
{
char buffer[8192];
DWORD bytesRead = 0;
while (true)
{
if (!blocking)
{
BOOL success = ::PeekNamedPipe(handle, nullptr, 0, nullptr, &bytesRead, nullptr);
if (success == FALSE || bytesRead == 0)
return;
}
BOOL success = ::ReadFile(handle, buffer, sizeof(buffer), &bytesRead, nullptr);
if (success == FALSE || bytesRead == 0)
return;
out->append(buffer, bytesRead);
}
// unreachable.
}
// Returns the Win32 last error code or ERROR_SUCCESS if the last error code is
// ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND. This is useful in cases where
// the absence of a file or path is a success condition (e.g., when attempting
// to delete an item in the filesystem).
bool ReturnSuccessOnNotFound()
{
const DWORD error_code = ::GetLastError();
return (error_code == ERROR_FILE_NOT_FOUND || error_code == ERROR_PATH_NOT_FOUND);
}
class WindowsProcess : public Process
{
public:
WindowsProcess(const std::vector<const char *> &commandLineArgs,
bool captureStdOut,
bool captureStdErr)
{
mProcessInfo.hProcess = INVALID_HANDLE_VALUE;
mProcessInfo.hThread = INVALID_HANDLE_VALUE;
std::vector<char> commandLineString;
for (const char *arg : commandLineArgs)
{
if (arg)
{
if (!commandLineString.empty())
{
commandLineString.push_back(' ');
}
commandLineString.insert(commandLineString.end(), arg, arg + strlen(arg));
}
}
commandLineString.push_back('\0');
// Set the bInheritHandle flag so pipe handles are inherited.
SECURITY_ATTRIBUTES securityAttribs;
securityAttribs.nLength = sizeof(SECURITY_ATTRIBUTES);
securityAttribs.bInheritHandle = TRUE;
securityAttribs.lpSecurityDescriptor = nullptr;
STARTUPINFOA startInfo = {};
// Create pipes for stdout and stderr.
startInfo.cb = sizeof(STARTUPINFOA);
startInfo.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE);
if (captureStdOut)
{
if (!mStdoutPipe.initPipe(&securityAttribs))
{
return;
}
startInfo.hStdOutput = mStdoutPipe.writeHandle;
}
else
{
startInfo.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
}
if (captureStdErr)
{
if (!mStderrPipe.initPipe(&securityAttribs))
{
return;
}
startInfo.hStdError = mStderrPipe.writeHandle;
}
else
{
startInfo.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
}
if (captureStdOut || captureStdErr)
{
startInfo.dwFlags |= STARTF_USESTDHANDLES;
}
// Create the child process.
if (::CreateProcessA(nullptr, commandLineString.data(), nullptr, nullptr,
TRUE, // Handles are inherited.
0, nullptr, nullptr, &startInfo, &mProcessInfo) == FALSE)
{
std::cerr << "CreateProcessA Error code: " << GetLastError() << "\n";
return;
}
// Close the write end of the pipes, so EOF can be generated when child exits.
if (!mStdoutPipe.closeWriteHandle() || !mStderrPipe.closeWriteHandle())
return;
mStarted = true;
mTimer.start();
}
~WindowsProcess() override
{
if (mProcessInfo.hProcess != INVALID_HANDLE_VALUE)
{
::CloseHandle(mProcessInfo.hProcess);
}
if (mProcessInfo.hThread != INVALID_HANDLE_VALUE)
{
::CloseHandle(mProcessInfo.hThread);
}
}
bool started() override { return mStarted; }
bool finish() override
{
if (mStdoutPipe.valid())
{
ReadFromFile(true, mStdoutPipe.readHandle, &mStdout);
}
if (mStderrPipe.valid())
{
ReadFromFile(true, mStderrPipe.readHandle, &mStderr);
}
DWORD result = ::WaitForSingleObject(mProcessInfo.hProcess, INFINITE);
mTimer.stop();
return result == WAIT_OBJECT_0;
}
bool finished() override
{
if (!mStarted)
return false;
// Pipe stdin and stdout.
if (mStdoutPipe.valid())
{
ReadFromFile(false, mStdoutPipe.readHandle, &mStdout);
}
if (mStderrPipe.valid())
{
ReadFromFile(false, mStderrPipe.readHandle, &mStderr);
}
DWORD result = ::WaitForSingleObject(mProcessInfo.hProcess, 0);
if (result == WAIT_OBJECT_0)
{
mTimer.stop();
return true;
}
if (result == WAIT_TIMEOUT)
return false;
mTimer.stop();
std::cerr << "Unexpected result from WaitForSingleObject: " << result
<< ". Last error: " << ::GetLastError() << "\n";
return false;
}
int getExitCode() override
{
if (!mStarted)
return -1;
if (mProcessInfo.hProcess == INVALID_HANDLE_VALUE)
return -1;
DWORD exitCode = 0;
if (::GetExitCodeProcess(mProcessInfo.hProcess, &exitCode) == FALSE)
return -1;
return static_cast<int>(exitCode);
}
bool kill() override
{
if (!mStarted)
return true;
HANDLE newHandle;
if (::DuplicateHandle(::GetCurrentProcess(), mProcessInfo.hProcess, ::GetCurrentProcess(),
&newHandle, PROCESS_ALL_ACCESS, false,
DUPLICATE_CLOSE_SOURCE) == FALSE)
{
std::cerr << "Error getting permission to terminate process: " << ::GetLastError()
<< "\n";
return false;
}
mProcessInfo.hProcess = newHandle;
if (::TerminateThread(mProcessInfo.hThread, 1) == FALSE)
{
std::cerr << "TerminateThread failed: " << GetLastError() << "\n";
return false;
}
if (::TerminateProcess(mProcessInfo.hProcess, 1) == FALSE)
{
std::cerr << "TerminateProcess failed: " << GetLastError() << "\n";
return false;
}
mStarted = false;
mTimer.stop();
return true;
}
private:
bool mStarted = false;
ScopedPipe mStdoutPipe;
ScopedPipe mStderrPipe;
PROCESS_INFORMATION mProcessInfo = {};
};
} // anonymous namespace
void Sleep(unsigned int milliseconds)
@ -127,13 +439,18 @@ void WriteDebugMessage(const char *format, ...)
OutputDebugStringA(buffer.data());
}
void InitCrashHandler()
void InitCrashHandler(CrashCallback *callback)
{
SetUnhandledExceptionFilter(StackTraceCrashHandler);
if (callback)
{
gCrashHandlerCallback = callback;
}
SetUnhandledExceptionFilter(CrashHandler);
}
void TerminateCrashHandler()
{
gCrashHandlerCallback = nullptr;
SetUnhandledExceptionFilter(nullptr);
}
@ -144,4 +461,61 @@ void PrintStackBacktrace()
RtlCaptureContext(&context);
PrintBacktrace(&context);
}
Process *LaunchProcess(const std::vector<const char *> &args,
bool captureStdout,
bool captureStderr)
{
return new WindowsProcess(args, captureStdout, captureStderr);
}
bool GetTempDir(char *tempDirOut, uint32_t maxDirNameLen)
{
DWORD pathLen = ::GetTempPathA(maxDirNameLen, tempDirOut);
return (pathLen < MAX_PATH && pathLen > 0);
}
bool CreateTemporaryFileInDir(const char *dir, char *tempFileNameOut, uint32_t maxFileNameLen)
{
char fileName[MAX_PATH + 1];
if (::GetTempFileNameA(dir, "ANGLE", 0, fileName) == 0)
return false;
strncpy(tempFileNameOut, fileName, maxFileNameLen);
return true;
}
bool DeleteFile(const char *path)
{
if (strlen(path) >= MAX_PATH)
return false;
const DWORD attr = ::GetFileAttributesA(path);
// Report success if the file or path does not exist.
if (attr == INVALID_FILE_ATTRIBUTES)
{
return ReturnSuccessOnNotFound();
}
// Clear the read-only bit if it is set.
if ((attr & FILE_ATTRIBUTE_READONLY) &&
!::SetFileAttributesA(path, attr & ~FILE_ATTRIBUTE_READONLY))
{
// It's possible for |path| to be gone now under a race with other deleters.
return ReturnSuccessOnNotFound();
}
// We don't handle directories right now.
if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
{
return false;
}
return !!::DeleteFileA(path) ? true : ReturnSuccessOnNotFound();
}
int NumberOfProcessors()
{
return ::GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
}
} // namespace angle

Просмотреть файл

@ -11,9 +11,10 @@
#include <windows.h>
#include <array>
#include "util/random_utils.h"
namespace angle
{
void SetLowPriorityProcess()
{
SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);
@ -21,15 +22,15 @@ void SetLowPriorityProcess()
bool StabilizeCPUForBenchmarking()
{
if (SetThreadAffinityMask(GetCurrentThread(), 1) == 0)
if (::SetThreadAffinityMask(::GetCurrentThread(), 1) == 0)
{
return false;
}
if (SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS) == FALSE)
if (::SetPriorityClass(::GetCurrentProcess(), REALTIME_PRIORITY_CLASS) == FALSE)
{
return false;
}
if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) == FALSE)
if (::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) == FALSE)
{
return false;
}