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