/external/bsd/llvm/dist/llvm/unittests/Support/ProgramTest.cpp

https://gitlab.com/storedmirrors/minix · C++ · 323 lines · 257 code · 41 blank · 25 comment · 24 complexity · 5f544d0463f3484deda6faab19ac27dc MD5 · raw file

  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. #include "llvm/Support/CommandLine.h"
  10. #include "llvm/Support/FileSystem.h"
  11. #include "llvm/Support/Path.h"
  12. #include "llvm/Support/Program.h"
  13. #include "gtest/gtest.h"
  14. #include <stdlib.h>
  15. #if defined(__APPLE__)
  16. # include <crt_externs.h>
  17. #elif !defined(_MSC_VER)
  18. // Forward declare environ in case it's not provided by stdlib.h.
  19. extern char **environ;
  20. #endif
  21. #if defined(LLVM_ON_UNIX)
  22. #include <unistd.h>
  23. void sleep_for(unsigned int seconds) {
  24. sleep(seconds);
  25. }
  26. #elif defined(LLVM_ON_WIN32)
  27. #include <windows.h>
  28. void sleep_for(unsigned int seconds) {
  29. Sleep(seconds * 1000);
  30. }
  31. #else
  32. #error sleep_for is not implemented on your platform.
  33. #endif
  34. #define ASSERT_NO_ERROR(x) \
  35. if (std::error_code ASSERT_NO_ERROR_ec = x) { \
  36. SmallString<128> MessageStorage; \
  37. raw_svector_ostream Message(MessageStorage); \
  38. Message << #x ": did not return errc::success.\n" \
  39. << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \
  40. << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \
  41. GTEST_FATAL_FAILURE_(MessageStorage.c_str()); \
  42. } else { \
  43. }
  44. // From TestMain.cpp.
  45. extern const char *TestMainArgv0;
  46. namespace {
  47. using namespace llvm;
  48. using namespace sys;
  49. static cl::opt<std::string>
  50. ProgramTestStringArg1("program-test-string-arg1");
  51. static cl::opt<std::string>
  52. ProgramTestStringArg2("program-test-string-arg2");
  53. static void CopyEnvironment(std::vector<const char *> &out) {
  54. #ifdef __APPLE__
  55. char **envp = *_NSGetEnviron();
  56. #else
  57. // environ seems to work for Windows and most other Unices.
  58. char **envp = environ;
  59. #endif
  60. while (*envp != nullptr) {
  61. out.push_back(*envp);
  62. ++envp;
  63. }
  64. }
  65. #ifdef LLVM_ON_WIN32
  66. TEST(ProgramTest, CreateProcessLongPath) {
  67. if (getenv("LLVM_PROGRAM_TEST_LONG_PATH"))
  68. exit(0);
  69. // getMainExecutable returns an absolute path; prepend the long-path prefix.
  70. std::string MyAbsExe =
  71. sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
  72. std::string MyExe;
  73. if (!StringRef(MyAbsExe).startswith("\\\\?\\"))
  74. MyExe.append("\\\\?\\");
  75. MyExe.append(MyAbsExe);
  76. const char *ArgV[] = {
  77. MyExe.c_str(),
  78. "--gtest_filter=ProgramTest.CreateProcessLongPath",
  79. nullptr
  80. };
  81. // Add LLVM_PROGRAM_TEST_LONG_PATH to the environment of the child.
  82. std::vector<const char *> EnvP;
  83. CopyEnvironment(EnvP);
  84. EnvP.push_back("LLVM_PROGRAM_TEST_LONG_PATH=1");
  85. EnvP.push_back(nullptr);
  86. // Redirect stdout to a long path.
  87. SmallString<128> TestDirectory;
  88. ASSERT_NO_ERROR(
  89. fs::createUniqueDirectory("program-redirect-test", TestDirectory));
  90. SmallString<256> LongPath(TestDirectory);
  91. LongPath.push_back('\\');
  92. // MAX_PATH = 260
  93. LongPath.append(260 - TestDirectory.size(), 'a');
  94. StringRef LongPathRef(LongPath);
  95. std::string Error;
  96. bool ExecutionFailed;
  97. const StringRef *Redirects[] = { nullptr, &LongPathRef, nullptr };
  98. int RC = ExecuteAndWait(MyExe, ArgV, &EnvP[0], Redirects,
  99. /*secondsToWait=*/ 10, /*memoryLimit=*/ 0, &Error,
  100. &ExecutionFailed);
  101. EXPECT_FALSE(ExecutionFailed) << Error;
  102. EXPECT_EQ(0, RC);
  103. // Remove the long stdout.
  104. ASSERT_NO_ERROR(fs::remove(Twine(LongPath)));
  105. ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory)));
  106. }
  107. #endif
  108. TEST(ProgramTest, CreateProcessTrailingSlash) {
  109. if (getenv("LLVM_PROGRAM_TEST_CHILD")) {
  110. if (ProgramTestStringArg1 == "has\\\\ trailing\\" &&
  111. ProgramTestStringArg2 == "has\\\\ trailing\\") {
  112. exit(0); // Success! The arguments were passed and parsed.
  113. }
  114. exit(1);
  115. }
  116. std::string my_exe =
  117. sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
  118. const char *argv[] = {
  119. my_exe.c_str(),
  120. "--gtest_filter=ProgramTest.CreateProcessTrailingSlash",
  121. "-program-test-string-arg1", "has\\\\ trailing\\",
  122. "-program-test-string-arg2", "has\\\\ trailing\\",
  123. nullptr
  124. };
  125. // Add LLVM_PROGRAM_TEST_CHILD to the environment of the child.
  126. std::vector<const char *> envp;
  127. CopyEnvironment(envp);
  128. envp.push_back("LLVM_PROGRAM_TEST_CHILD=1");
  129. envp.push_back(nullptr);
  130. std::string error;
  131. bool ExecutionFailed;
  132. // Redirect stdout and stdin to NUL, but let stderr through.
  133. #ifdef LLVM_ON_WIN32
  134. StringRef nul("NUL");
  135. #else
  136. StringRef nul("/dev/null");
  137. #endif
  138. const StringRef *redirects[] = { &nul, &nul, nullptr };
  139. int rc = ExecuteAndWait(my_exe, argv, &envp[0], redirects,
  140. /*secondsToWait=*/ 10, /*memoryLimit=*/ 0, &error,
  141. &ExecutionFailed);
  142. EXPECT_FALSE(ExecutionFailed) << error;
  143. EXPECT_EQ(0, rc);
  144. }
  145. TEST(ProgramTest, TestExecuteNoWait) {
  146. using namespace llvm::sys;
  147. if (getenv("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT")) {
  148. sleep_for(/*seconds*/ 1);
  149. exit(0);
  150. }
  151. std::string Executable =
  152. sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
  153. const char *argv[] = {
  154. Executable.c_str(),
  155. "--gtest_filter=ProgramTest.TestExecuteNoWait",
  156. nullptr
  157. };
  158. // Add LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT to the environment of the child.
  159. std::vector<const char *> envp;
  160. CopyEnvironment(envp);
  161. envp.push_back("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT=1");
  162. envp.push_back(nullptr);
  163. std::string Error;
  164. bool ExecutionFailed;
  165. ProcessInfo PI1 = ExecuteNoWait(Executable, argv, &envp[0], nullptr, 0,
  166. &Error, &ExecutionFailed);
  167. ASSERT_FALSE(ExecutionFailed) << Error;
  168. ASSERT_NE(PI1.Pid, 0) << "Invalid process id";
  169. unsigned LoopCount = 0;
  170. // Test that Wait() with WaitUntilTerminates=true works. In this case,
  171. // LoopCount should only be incremented once.
  172. while (true) {
  173. ++LoopCount;
  174. ProcessInfo WaitResult = Wait(PI1, 0, true, &Error);
  175. ASSERT_TRUE(Error.empty());
  176. if (WaitResult.Pid == PI1.Pid)
  177. break;
  178. }
  179. EXPECT_EQ(LoopCount, 1u) << "LoopCount should be 1";
  180. ProcessInfo PI2 = ExecuteNoWait(Executable, argv, &envp[0], nullptr, 0,
  181. &Error, &ExecutionFailed);
  182. ASSERT_FALSE(ExecutionFailed) << Error;
  183. ASSERT_NE(PI2.Pid, 0) << "Invalid process id";
  184. // Test that Wait() with SecondsToWait=0 performs a non-blocking wait. In this
  185. // cse, LoopCount should be greater than 1 (more than one increment occurs).
  186. while (true) {
  187. ++LoopCount;
  188. ProcessInfo WaitResult = Wait(PI2, 0, false, &Error);
  189. ASSERT_TRUE(Error.empty());
  190. if (WaitResult.Pid == PI2.Pid)
  191. break;
  192. }
  193. ASSERT_GT(LoopCount, 1u) << "LoopCount should be >1";
  194. }
  195. TEST(ProgramTest, TestExecuteAndWaitTimeout) {
  196. using namespace llvm::sys;
  197. if (getenv("LLVM_PROGRAM_TEST_TIMEOUT")) {
  198. sleep_for(/*seconds*/ 10);
  199. exit(0);
  200. }
  201. std::string Executable =
  202. sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
  203. const char *argv[] = {
  204. Executable.c_str(),
  205. "--gtest_filter=ProgramTest.TestExecuteAndWaitTimeout",
  206. nullptr
  207. };
  208. // Add LLVM_PROGRAM_TEST_TIMEOUT to the environment of the child.
  209. std::vector<const char *> envp;
  210. CopyEnvironment(envp);
  211. envp.push_back("LLVM_PROGRAM_TEST_TIMEOUT=1");
  212. envp.push_back(nullptr);
  213. std::string Error;
  214. bool ExecutionFailed;
  215. int RetCode =
  216. ExecuteAndWait(Executable, argv, &envp[0], nullptr, /*secondsToWait=*/1, 0,
  217. &Error, &ExecutionFailed);
  218. ASSERT_EQ(-2, RetCode);
  219. }
  220. TEST(ProgramTest, TestExecuteNegative) {
  221. std::string Executable = "i_dont_exist";
  222. const char *argv[] = { Executable.c_str(), nullptr };
  223. {
  224. std::string Error;
  225. bool ExecutionFailed;
  226. int RetCode = ExecuteAndWait(Executable, argv, nullptr, nullptr, 0, 0,
  227. &Error, &ExecutionFailed);
  228. ASSERT_TRUE(RetCode < 0) << "On error ExecuteAndWait should return 0 or "
  229. "positive value indicating the result code";
  230. ASSERT_TRUE(ExecutionFailed);
  231. ASSERT_FALSE(Error.empty());
  232. }
  233. {
  234. std::string Error;
  235. bool ExecutionFailed;
  236. ProcessInfo PI = ExecuteNoWait(Executable, argv, nullptr, nullptr, 0,
  237. &Error, &ExecutionFailed);
  238. ASSERT_EQ(PI.Pid, 0)
  239. << "On error ExecuteNoWait should return an invalid ProcessInfo";
  240. ASSERT_TRUE(ExecutionFailed);
  241. ASSERT_FALSE(Error.empty());
  242. }
  243. }
  244. #ifdef LLVM_ON_WIN32
  245. const char utf16le_text[] =
  246. "\x6c\x00\x69\x00\x6e\x00\x67\x00\xfc\x00\x69\x00\xe7\x00\x61\x00";
  247. const char utf16be_text[] =
  248. "\x00\x6c\x00\x69\x00\x6e\x00\x67\x00\xfc\x00\x69\x00\xe7\x00\x61";
  249. #endif
  250. const char utf8_text[] = "\x6c\x69\x6e\x67\xc3\xbc\x69\xc3\xa7\x61";
  251. TEST(ProgramTest, TestWriteWithSystemEncoding) {
  252. SmallString<128> TestDirectory;
  253. ASSERT_NO_ERROR(fs::createUniqueDirectory("program-test", TestDirectory));
  254. errs() << "Test Directory: " << TestDirectory << '\n';
  255. errs().flush();
  256. SmallString<128> file_pathname(TestDirectory);
  257. path::append(file_pathname, "international-file.txt");
  258. // Only on Windows we should encode in UTF16. For other systems, use UTF8
  259. ASSERT_NO_ERROR(sys::writeFileWithEncoding(file_pathname.c_str(), utf8_text,
  260. sys::WEM_UTF16));
  261. int fd = 0;
  262. ASSERT_NO_ERROR(fs::openFileForRead(file_pathname.c_str(), fd));
  263. #if defined(LLVM_ON_WIN32)
  264. char buf[18];
  265. ASSERT_EQ(::read(fd, buf, 18), 18);
  266. if (strncmp(buf, "\xfe\xff", 2) == 0) { // UTF16-BE
  267. ASSERT_EQ(strncmp(&buf[2], utf16be_text, 16), 0);
  268. } else if (strncmp(buf, "\xff\xfe", 2) == 0) { // UTF16-LE
  269. ASSERT_EQ(strncmp(&buf[2], utf16le_text, 16), 0);
  270. } else {
  271. FAIL() << "Invalid BOM in UTF-16 file";
  272. }
  273. #else
  274. char buf[10];
  275. ASSERT_EQ(::read(fd, buf, 10), 10);
  276. ASSERT_EQ(strncmp(buf, utf8_text, 10), 0);
  277. #endif
  278. ::close(fd);
  279. ASSERT_NO_ERROR(fs::remove(file_pathname.str()));
  280. ASSERT_NO_ERROR(fs::remove(TestDirectory.str()));
  281. }
  282. } // end anonymous namespace