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