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/ConvertUTF.h"
11#include "llvm/Support/CommandLine.h"
12#include "llvm/Support/FileSystem.h"
13#include "llvm/Support/Path.h"
14#include "llvm/Support/Program.h"
15#include "gtest/gtest.h"
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#define ASSERT_NO_ERROR(x)                                                     \
39  if (std::error_code ASSERT_NO_ERROR_ec = x) {                                \
40    SmallString<128> MessageStorage;                                           \
41    raw_svector_ostream Message(MessageStorage);                               \
42    Message << #x ": did not return errc::success.\n"                          \
43            << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n"          \
44            << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n";      \
45    GTEST_FATAL_FAILURE_(MessageStorage.c_str());                              \
46  } else {                                                                     \
47  }
48// From TestMain.cpp.
49extern const char *TestMainArgv0;
50
51namespace {
52
53using namespace llvm;
54using namespace sys;
55
56static cl::opt<std::string>
57ProgramTestStringArg1("program-test-string-arg1");
58static cl::opt<std::string>
59ProgramTestStringArg2("program-test-string-arg2");
60
61class ProgramEnvTest : public testing::Test {
62  std::vector<const char *> EnvTable;
63  std::vector<std::string> EnvStorage;
64
65protected:
66  void SetUp() override {
67    auto EnvP = [] {
68#if defined(LLVM_ON_WIN32)
69      _wgetenv(L"TMP"); // Populate _wenviron, initially is null
70      return _wenviron;
71#elif defined(__APPLE__)
72      return *_NSGetEnviron();
73#else
74      return environ;
75#endif
76    }();
77    ASSERT_TRUE(EnvP);
78
79    auto prepareEnvVar = [this](decltype(*EnvP) Var) {
80#if defined(LLVM_ON_WIN32)
81      // On Windows convert UTF16 encoded variable to UTF8
82      auto Len = wcslen(Var);
83      ArrayRef<char> Ref{reinterpret_cast<char const *>(Var),
84                         Len * sizeof(*Var)};
85      EnvStorage.emplace_back();
86      auto convStatus = convertUTF16ToUTF8String(Ref, EnvStorage.back());
87      EXPECT_TRUE(convStatus);
88      return EnvStorage.back().c_str();
89#else
90      return Var;
91#endif
92    };
93
94    while (*EnvP != nullptr) {
95      EnvTable.emplace_back(prepareEnvVar(*EnvP));
96      ++EnvP;
97    }
98  }
99
100  void TearDown() override {
101    EnvTable.clear();
102    EnvStorage.clear();
103  }
104
105  void addEnvVar(const char *Var) {
106    ASSERT_TRUE(EnvTable.empty() || EnvTable.back()) << "Env table sealed";
107    EnvTable.emplace_back(Var);
108  }
109
110  const char **getEnviron() {
111    if (EnvTable.back() != nullptr)
112      EnvTable.emplace_back(nullptr); // Seal table.
113    return &EnvTable[0];
114  }
115};
116
117#ifdef LLVM_ON_WIN32
118TEST_F(ProgramEnvTest, CreateProcessLongPath) {
119  if (getenv("LLVM_PROGRAM_TEST_LONG_PATH"))
120    exit(0);
121
122  // getMainExecutable returns an absolute path; prepend the long-path prefix.
123  std::string MyAbsExe =
124      sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
125  std::string MyExe;
126  if (!StringRef(MyAbsExe).startswith("\\\\?\\"))
127    MyExe.append("\\\\?\\");
128  MyExe.append(MyAbsExe);
129
130  const char *ArgV[] = {
131    MyExe.c_str(),
132    "--gtest_filter=ProgramEnvTest.CreateProcessLongPath",
133    nullptr
134  };
135
136  // Add LLVM_PROGRAM_TEST_LONG_PATH to the environment of the child.
137  addEnvVar("LLVM_PROGRAM_TEST_LONG_PATH=1");
138
139  // Redirect stdout to a long path.
140  SmallString<128> TestDirectory;
141  ASSERT_NO_ERROR(
142    fs::createUniqueDirectory("program-redirect-test", TestDirectory));
143  SmallString<256> LongPath(TestDirectory);
144  LongPath.push_back('\\');
145  // MAX_PATH = 260
146  LongPath.append(260 - TestDirectory.size(), 'a');
147  StringRef LongPathRef(LongPath);
148
149  std::string Error;
150  bool ExecutionFailed;
151  const StringRef *Redirects[] = { nullptr, &LongPathRef, nullptr };
152  int RC = ExecuteAndWait(MyExe, ArgV, getEnviron(), Redirects,
153    /*secondsToWait=*/ 10, /*memoryLimit=*/ 0, &Error,
154    &ExecutionFailed);
155  EXPECT_FALSE(ExecutionFailed) << Error;
156  EXPECT_EQ(0, RC);
157
158  // Remove the long stdout.
159  ASSERT_NO_ERROR(fs::remove(Twine(LongPath)));
160  ASSERT_NO_ERROR(fs::remove(Twine(TestDirectory)));
161}
162#endif
163
164TEST_F(ProgramEnvTest, CreateProcessTrailingSlash) {
165  if (getenv("LLVM_PROGRAM_TEST_CHILD")) {
166    if (ProgramTestStringArg1 == "has\\\\ trailing\\" &&
167        ProgramTestStringArg2 == "has\\\\ trailing\\") {
168      exit(0);  // Success!  The arguments were passed and parsed.
169    }
170    exit(1);
171  }
172
173  std::string my_exe =
174      sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
175  const char *argv[] = {
176    my_exe.c_str(),
177    "--gtest_filter=ProgramEnvTest.CreateProcessTrailingSlash",
178    "-program-test-string-arg1", "has\\\\ trailing\\",
179    "-program-test-string-arg2", "has\\\\ trailing\\",
180    nullptr
181  };
182
183  // Add LLVM_PROGRAM_TEST_CHILD to the environment of the child.
184  addEnvVar("LLVM_PROGRAM_TEST_CHILD=1");
185
186  std::string error;
187  bool ExecutionFailed;
188  // Redirect stdout and stdin to NUL, but let stderr through.
189#ifdef LLVM_ON_WIN32
190  StringRef nul("NUL");
191#else
192  StringRef nul("/dev/null");
193#endif
194  const StringRef *redirects[] = { &nul, &nul, nullptr };
195  int rc = ExecuteAndWait(my_exe, argv, getEnviron(), redirects,
196                          /*secondsToWait=*/ 10, /*memoryLimit=*/ 0, &error,
197                          &ExecutionFailed);
198  EXPECT_FALSE(ExecutionFailed) << error;
199  EXPECT_EQ(0, rc);
200}
201
202TEST_F(ProgramEnvTest, TestExecuteNoWait) {
203  using namespace llvm::sys;
204
205  if (getenv("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT")) {
206    sleep_for(/*seconds*/ 1);
207    exit(0);
208  }
209
210  std::string Executable =
211      sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
212  const char *argv[] = {
213    Executable.c_str(),
214    "--gtest_filter=ProgramEnvTest.TestExecuteNoWait",
215    nullptr
216  };
217
218  // Add LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT to the environment of the child.
219  addEnvVar("LLVM_PROGRAM_TEST_EXECUTE_NO_WAIT=1");
220
221  std::string Error;
222  bool ExecutionFailed;
223  ProcessInfo PI1 = ExecuteNoWait(Executable, argv, getEnviron(), nullptr, 0,
224                                  &Error, &ExecutionFailed);
225  ASSERT_FALSE(ExecutionFailed) << Error;
226  ASSERT_NE(PI1.Pid, ProcessInfo::InvalidPid) << "Invalid process id";
227
228  unsigned LoopCount = 0;
229
230  // Test that Wait() with WaitUntilTerminates=true works. In this case,
231  // LoopCount should only be incremented once.
232  while (true) {
233    ++LoopCount;
234    ProcessInfo WaitResult = llvm::sys::Wait(PI1, 0, true, &Error);
235    ASSERT_TRUE(Error.empty());
236    if (WaitResult.Pid == PI1.Pid)
237      break;
238  }
239
240  EXPECT_EQ(LoopCount, 1u) << "LoopCount should be 1";
241
242  ProcessInfo PI2 = ExecuteNoWait(Executable, argv, getEnviron(), nullptr, 0,
243                                  &Error, &ExecutionFailed);
244  ASSERT_FALSE(ExecutionFailed) << Error;
245  ASSERT_NE(PI2.Pid, ProcessInfo::InvalidPid) << "Invalid process id";
246
247  // Test that Wait() with SecondsToWait=0 performs a non-blocking wait. In this
248  // cse, LoopCount should be greater than 1 (more than one increment occurs).
249  while (true) {
250    ++LoopCount;
251    ProcessInfo WaitResult = llvm::sys::Wait(PI2, 0, false, &Error);
252    ASSERT_TRUE(Error.empty());
253    if (WaitResult.Pid == PI2.Pid)
254      break;
255  }
256
257  ASSERT_GT(LoopCount, 1u) << "LoopCount should be >1";
258}
259
260TEST_F(ProgramEnvTest, TestExecuteAndWaitTimeout) {
261  using namespace llvm::sys;
262
263  if (getenv("LLVM_PROGRAM_TEST_TIMEOUT")) {
264    sleep_for(/*seconds*/ 10);
265    exit(0);
266  }
267
268  std::string Executable =
269      sys::fs::getMainExecutable(TestMainArgv0, &ProgramTestStringArg1);
270  const char *argv[] = {
271    Executable.c_str(),
272    "--gtest_filter=ProgramEnvTest.TestExecuteAndWaitTimeout",
273    nullptr
274  };
275
276  // Add LLVM_PROGRAM_TEST_TIMEOUT to the environment of the child.
277 addEnvVar("LLVM_PROGRAM_TEST_TIMEOUT=1");
278
279  std::string Error;
280  bool ExecutionFailed;
281  int RetCode =
282      ExecuteAndWait(Executable, argv, getEnviron(), nullptr, /*secondsToWait=*/1, 0,
283                     &Error, &ExecutionFailed);
284  ASSERT_EQ(-2, RetCode);
285}
286
287TEST(ProgramTest, TestExecuteNegative) {
288  std::string Executable = "i_dont_exist";
289  const char *argv[] = { Executable.c_str(), nullptr };
290
291  {
292    std::string Error;
293    bool ExecutionFailed;
294    int RetCode = ExecuteAndWait(Executable, argv, nullptr, nullptr, 0, 0,
295                                 &Error, &ExecutionFailed);
296    ASSERT_TRUE(RetCode < 0) << "On error ExecuteAndWait should return 0 or "
297                                "positive value indicating the result code";
298    ASSERT_TRUE(ExecutionFailed);
299    ASSERT_FALSE(Error.empty());
300  }
301
302  {
303    std::string Error;
304    bool ExecutionFailed;
305    ProcessInfo PI = ExecuteNoWait(Executable, argv, nullptr, nullptr, 0,
306                                   &Error, &ExecutionFailed);
307    ASSERT_EQ(PI.Pid, ProcessInfo::InvalidPid)
308        << "On error ExecuteNoWait should return an invalid ProcessInfo";
309    ASSERT_TRUE(ExecutionFailed);
310    ASSERT_FALSE(Error.empty());
311  }
312
313}
314
315#ifdef LLVM_ON_WIN32
316const char utf16le_text[] =
317    "\x6c\x00\x69\x00\x6e\x00\x67\x00\xfc\x00\x69\x00\xe7\x00\x61\x00";
318const char utf16be_text[] =
319    "\x00\x6c\x00\x69\x00\x6e\x00\x67\x00\xfc\x00\x69\x00\xe7\x00\x61";
320#endif
321const char utf8_text[] = "\x6c\x69\x6e\x67\xc3\xbc\x69\xc3\xa7\x61";
322
323TEST(ProgramTest, TestWriteWithSystemEncoding) {
324  SmallString<128> TestDirectory;
325  ASSERT_NO_ERROR(fs::createUniqueDirectory("program-test", TestDirectory));
326  errs() << "Test Directory: " << TestDirectory << '\n';
327  errs().flush();
328  SmallString<128> file_pathname(TestDirectory);
329  path::append(file_pathname, "international-file.txt");
330  // Only on Windows we should encode in UTF16. For other systems, use UTF8
331  ASSERT_NO_ERROR(sys::writeFileWithEncoding(file_pathname.c_str(), utf8_text,
332                                             sys::WEM_UTF16));
333  int fd = 0;
334  ASSERT_NO_ERROR(fs::openFileForRead(file_pathname.c_str(), fd));
335#if defined(LLVM_ON_WIN32)
336  char buf[18];
337  ASSERT_EQ(::read(fd, buf, 18), 18);
338  if (strncmp(buf, "\xfe\xff", 2) == 0) { // UTF16-BE
339    ASSERT_EQ(strncmp(&buf[2], utf16be_text, 16), 0);
340  } else if (strncmp(buf, "\xff\xfe", 2) == 0) { // UTF16-LE
341    ASSERT_EQ(strncmp(&buf[2], utf16le_text, 16), 0);
342  } else {
343    FAIL() << "Invalid BOM in UTF-16 file";
344  }
345#else
346  char buf[10];
347  ASSERT_EQ(::read(fd, buf, 10), 10);
348  ASSERT_EQ(strncmp(buf, utf8_text, 10), 0);
349#endif
350  ::close(fd);
351  ASSERT_NO_ERROR(fs::remove(file_pathname.str()));
352  ASSERT_NO_ERROR(fs::remove(TestDirectory.str()));
353}
354
355} // end anonymous namespace
356