1//===- unittest/Support/ProgramTest.cpp -----------------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "llvm/Support/CommandLine.h" 11#include "llvm/Support/FileSystem.h" 12#include "llvm/Support/Path.h" 13#include "llvm/Support/Program.h" 14#include "gtest/gtest.h" 15#include <stdlib.h> 16#if defined(__APPLE__) 17# include <crt_externs.h> 18#elif !defined(_MSC_VER) 19// Forward declare environ in case it's not provided by stdlib.h. 20extern char **environ; 21#endif 22 23#if defined(LLVM_ON_UNIX) 24#include <unistd.h> 25void sleep_for(unsigned int seconds) { 26 sleep(seconds); 27} 28#elif defined(LLVM_ON_WIN32) 29#include <windows.h> 30void sleep_for(unsigned int seconds) { 31 Sleep(seconds * 1000); 32} 33#else 34#error sleep_for is not implemented on your platform. 35#endif 36 37// From TestMain.cpp. 38extern const char *TestMainArgv0; 39 40namespace { 41 42using namespace llvm; 43using namespace sys; 44 45static cl::opt<std::string> 46ProgramTestStringArg1("program-test-string-arg1"); 47static cl::opt<std::string> 48ProgramTestStringArg2("program-test-string-arg2"); 49 50static void CopyEnvironment(std::vector<const char *> &out) { 51#ifdef __APPLE__ 52 char **envp = *_NSGetEnviron(); 53#else 54 // environ seems to work for Windows and most other Unices. 55 char **envp = environ; 56#endif 57 while (*envp != nullptr) { 58 out.push_back(*envp); 59 ++envp; 60 } 61} 62 63TEST(ProgramTest, CreateProcessTrailingSlash) { 64 if (getenv("LLVM_PROGRAM_TEST_CHILD")) { 65 if (ProgramTestStringArg1 == "has\\\\ trailing\\" && 66 ProgramTestStringArg2 == "has\\\\ trailing\\") { 67 exit(0); // Success! The arguments were passed and parsed. 68 } 69 exit(1); 70 } 71 72 std::string my_exe = 73 sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); 74 const char *argv[] = { 75 my_exe.c_str(), 76 "--gtest_filter=ProgramTest.CreateProcessTrailingSlash", 77 "-program-test-string-arg1", "has\\\\ trailing\\", 78 "-program-test-string-arg2", "has\\\\ trailing\\", 79 nullptr 80 }; 81 82 // Add LLVM_PROGRAM_TEST_CHILD to the environment of the child. 83 std::vector<const char *> envp; 84 CopyEnvironment(envp); 85 envp.push_back("LLVM_PROGRAM_TEST_CHILD=1"); 86 envp.push_back(nullptr); 87 88 std::string error; 89 bool ExecutionFailed; 90 // Redirect stdout and stdin to NUL, but let stderr through. 91#ifdef LLVM_ON_WIN32 92 StringRef nul("NUL"); 93#else 94 StringRef nul("/dev/null"); 95#endif 96 const StringRef *redirects[] = { &nul, &nul, nullptr }; 97 int rc = ExecuteAndWait(my_exe, argv, &envp[0], redirects, 98 /*secondsToWait=*/ 10, /*memoryLimit=*/ 0, &error, 99 &ExecutionFailed); 100 EXPECT_FALSE(ExecutionFailed) << error; 101 EXPECT_EQ(0, rc); 102} 103 104TEST(ProgramTest, TestExecuteNoWait) { 105 using namespace llvm::sys; 106 107 if (getenv("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT")) { 108 sleep_for(/*seconds*/ 1); 109 exit(0); 110 } 111 112 std::string Executable = 113 sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); 114 const char *argv[] = { 115 Executable.c_str(), 116 "--gtest_filter=ProgramTest.TestExecuteNoWait", 117 nullptr 118 }; 119 120 // Add LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT to the environment of the child. 121 std::vector<const char *> envp; 122 CopyEnvironment(envp); 123 envp.push_back("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT=1"); 124 envp.push_back(nullptr); 125 126 std::string Error; 127 bool ExecutionFailed; 128 ProcessInfo PI1 = ExecuteNoWait(Executable, argv, &envp[0], nullptr, 0, 129 &Error, &ExecutionFailed); 130 ASSERT_FALSE(ExecutionFailed) << Error; 131 ASSERT_NE(PI1.Pid, 0) << "Invalid process id"; 132 133 unsigned LoopCount = 0; 134 135 // Test that Wait() with WaitUntilTerminates=true works. In this case, 136 // LoopCount should only be incremented once. 137 while (true) { 138 ++LoopCount; 139 ProcessInfo WaitResult = Wait(PI1, 0, true, &Error); 140 ASSERT_TRUE(Error.empty()); 141 if (WaitResult.Pid == PI1.Pid) 142 break; 143 } 144 145 EXPECT_EQ(LoopCount, 1u) << "LoopCount should be 1"; 146 147 ProcessInfo PI2 = ExecuteNoWait(Executable, argv, &envp[0], nullptr, 0, 148 &Error, &ExecutionFailed); 149 ASSERT_FALSE(ExecutionFailed) << Error; 150 ASSERT_NE(PI2.Pid, 0) << "Invalid process id"; 151 152 // Test that Wait() with SecondsToWait=0 performs a non-blocking wait. In this 153 // cse, LoopCount should be greater than 1 (more than one increment occurs). 154 while (true) { 155 ++LoopCount; 156 ProcessInfo WaitResult = Wait(PI2, 0, false, &Error); 157 ASSERT_TRUE(Error.empty()); 158 if (WaitResult.Pid == PI2.Pid) 159 break; 160 } 161 162 ASSERT_GT(LoopCount, 1u) << "LoopCount should be >1"; 163} 164 165TEST(ProgramTest, TestExecuteAndWaitTimeout) { 166 using namespace llvm::sys; 167 168 if (getenv("LLVM_PROGRAM_TEST_TIMEOUT")) { 169 sleep_for(/*seconds*/ 10); 170 exit(0); 171 } 172 173 std::string Executable = 174 sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1); 175 const char *argv[] = { 176 Executable.c_str(), 177 "--gtest_filter=ProgramTest.TestExecuteAndWaitTimeout", 178 nullptr 179 }; 180 181 // Add LLVM_PROGRAM_TEST_TIMEOUT to the environment of the child. 182 std::vector<const char *> envp; 183 CopyEnvironment(envp); 184 envp.push_back("LLVM_PROGRAM_TEST_TIMEOUT=1"); 185 envp.push_back(nullptr); 186 187 std::string Error; 188 bool ExecutionFailed; 189 int RetCode = 190 ExecuteAndWait(Executable, argv, &envp[0], nullptr, /*secondsToWait=*/1, 0, 191 &Error, &ExecutionFailed); 192 ASSERT_EQ(-2, RetCode); 193} 194 195TEST(ProgramTest, TestExecuteNegative) { 196 std::string Executable = "i_dont_exist"; 197 const char *argv[] = { Executable.c_str(), nullptr }; 198 199 { 200 std::string Error; 201 bool ExecutionFailed; 202 int RetCode = ExecuteAndWait(Executable, argv, nullptr, nullptr, 0, 0, 203 &Error, &ExecutionFailed); 204 ASSERT_TRUE(RetCode < 0) << "On error ExecuteAndWait should return 0 or " 205 "positive value indicating the result code"; 206 ASSERT_TRUE(ExecutionFailed); 207 ASSERT_FALSE(Error.empty()); 208 } 209 210 { 211 std::string Error; 212 bool ExecutionFailed; 213 ProcessInfo PI = ExecuteNoWait(Executable, argv, nullptr, nullptr, 0, 214 &Error, &ExecutionFailed); 215 ASSERT_EQ(PI.Pid, 0) 216 << "On error ExecuteNoWait should return an invalid ProcessInfo"; 217 ASSERT_TRUE(ExecutionFailed); 218 ASSERT_FALSE(Error.empty()); 219 } 220 221} 222 223} // end anonymous namespace 224