[libc++] Generate symlinks in static_test_env on the fly
authorSergej Jaskiewicz <jaskiewiczs@icloud.com>
Wed, 15 Apr 2020 13:21:27 +0000 (16:21 +0300)
committerSergej Jaskiewicz <jaskiewiczs@icloud.com>
Tue, 5 May 2020 22:13:18 +0000 (01:13 +0300)
Instead of storing static_test_env (with all the symlinks) in the repo,
we create it on the fly to be cross-toolchain-friendly. The primary
use case for this are Windows-hosted cross-toolchains. Windows doesn't
really have a concept of symlinks. So, when the monorepo is cloned,
those symlinks turn to ordinary text files. Previously, if we
cross-compiled libc++ for some symlink-friendly system (e. g. Linux) and
ran tests on the target system, some tests would fail. This patch makes
them pass.

Differential Revision: https://reviews.llvm.org/D78200

24 files changed:
libcxx/test/std/input.output/filesystems/Inputs/static_test_env/bad_symlink [deleted symlink]
libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/symlink_to_dir3 [deleted symlink]
libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_dir [deleted symlink]
libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_empty_file [deleted symlink]
libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.cons/path.pass.cpp
libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/file_size.pass.cpp
libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
libcxx/test/std/input.output/filesystems/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
libcxx/test/std/input.output/filesystems/class.directory_iterator/directory_iterator.members/ctor.pass.cpp
libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
libcxx/test/std/input.output/filesystems/class.rec.dir.itr/rec.dir.itr.nonmembers/begin_end.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.canonical/canonical.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.equivalent/equivalent.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.file_size/file_size.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_directory/is_directory.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_empty/is_empty.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.is_symlink/is_symlink.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.last_write_time/last_write_time.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.relative/relative.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.space/space.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.status/status.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.symlink_status/symlink_status.pass.cpp
libcxx/test/std/input.output/filesystems/fs.op.funcs/fs.op.weakly_canonical/weakly_canonical.pass.cpp
libcxx/test/support/filesystem_test_helper.h

diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/bad_symlink b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/bad_symlink
deleted file mode 120000 (symlink)
index 76646be..0000000
+++ /dev/null
@@ -1 +0,0 @@
-dne
\ No newline at end of file
diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/symlink_to_dir3 b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/dir1/dir2/symlink_to_dir3
deleted file mode 120000 (symlink)
index 3979139..0000000
+++ /dev/null
@@ -1 +0,0 @@
-dir3
\ No newline at end of file
diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_dir b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_dir
deleted file mode 120000 (symlink)
index df490f8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-dir1
\ No newline at end of file
diff --git a/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_empty_file b/libcxx/test/std/input.output/filesystems/Inputs/static_test_env/symlink_to_empty_file
deleted file mode 120000 (symlink)
index b79b689..0000000
+++ /dev/null
@@ -1 +0,0 @@
-empty_file
\ No newline at end of file
index 850be12..6731bd8 100644 (file)
@@ -121,6 +121,8 @@ TEST_CASE(path_ctor_calls_refresh) {
 TEST_CASE(path_ctor_dne) {
   using namespace fs;
 
+  static_test_env static_env;
+
   {
     std::error_code ec = GetTestEC();
     directory_entry ent(StaticEnv::DNE, ec);
index a06c4a6..287ea5c 100644 (file)
@@ -60,6 +60,7 @@ TEST_CASE(test_constructor_signatures)
 
 TEST_CASE(test_construction_from_bad_path)
 {
+    static_test_env static_env;
     std::error_code ec;
     directory_options opts = directory_options::none;
     const directory_iterator endIt;
@@ -169,6 +170,7 @@ TEST_CASE(test_open_on_empty_directory_equals_end)
 
 TEST_CASE(test_open_on_directory_succeeds)
 {
+    static_test_env static_env;
     const path testDir = StaticEnv::Dir;
     std::set<path> dir_contents(std::begin(StaticEnv::DirIterationList),
                                 std::end(  StaticEnv::DirIterationList));
@@ -190,6 +192,7 @@ TEST_CASE(test_open_on_directory_succeeds)
 
 TEST_CASE(test_open_on_file_fails)
 {
+    static_test_env static_env;
     const path testFile = StaticEnv::File;
     const directory_iterator endIt{};
     {
@@ -225,6 +228,7 @@ TEST_CASE(test_open_on_dot_dir)
 
 TEST_CASE(test_open_on_symlink)
 {
+    static_test_env static_env;
     const path symlinkToDir = StaticEnv::SymlinkToDir;
     std::set<path> dir_contents;
     for (path const& p : StaticEnv::DirIterationList) {
index 1c76505..dc3046c 100644 (file)
@@ -43,6 +43,7 @@ TEST_CASE(test_increment_signatures)
 
 TEST_CASE(test_prefix_increment)
 {
+    static_test_env static_env;
     const path testDir = StaticEnv::Dir;
     const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
                                       std::end(  StaticEnv::RecDirIterationList));
@@ -66,6 +67,7 @@ TEST_CASE(test_prefix_increment)
 
 TEST_CASE(test_postfix_increment)
 {
+    static_test_env static_env;
     const path testDir = StaticEnv::Dir;
     const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
                                       std::end(  StaticEnv::RecDirIterationList));
@@ -89,6 +91,7 @@ TEST_CASE(test_postfix_increment)
 
 TEST_CASE(test_increment_method)
 {
+    static_test_env static_env;
     const path testDir = StaticEnv::Dir;
     const std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
                                       std::end(  StaticEnv::RecDirIterationList));
@@ -113,6 +116,7 @@ TEST_CASE(test_increment_method)
 
 TEST_CASE(test_follow_symlinks)
 {
+    static_test_env static_env;
     const path testDir = StaticEnv::Dir;
     auto const& IterList = StaticEnv::RecDirFollowSymlinksIterationList;
 
index 24eaf84..e08d7c0 100644 (file)
@@ -43,6 +43,7 @@ TEST_CASE(test_function_signatures)
 
 TEST_CASE(test_ranged_for_loop)
 {
+    static_test_env static_env;
     const path testDir = StaticEnv::Dir;
     std::set<path> dir_contents(std::begin(StaticEnv::RecDirIterationList),
                                 std::end(  StaticEnv::RecDirIterationList));
index de2fa54..7644d86 100644 (file)
@@ -47,6 +47,7 @@ TEST_CASE(signature_test)
 // Each scope tests one of the cases.
 TEST_CASE(test_canonical)
 {
+    static_test_env static_env;
     CWDGuard guard;
     // has_root_name() && has_root_directory()
     const path Root = StaticEnv::Root;
index 6f27a4c..df9e6b1 100644 (file)
@@ -55,6 +55,7 @@ TEST_CASE(file_size_non_empty)
 
 TEST_CASE(symlink_test_case)
 {
+    static_test_env static_env;
     const path p = StaticEnv::File;
     const path p2 = StaticEnv::SymlinkToFile;
     TEST_CHECK(file_size(p) == file_size(p2));
@@ -62,6 +63,7 @@ TEST_CASE(symlink_test_case)
 
 TEST_CASE(file_size_error_cases)
 {
+  static_test_env static_env;
   struct {
     path p;
     std::errc expected_err;
index 35dd5d4..05a313a 100644 (file)
@@ -69,6 +69,7 @@ TEST_CASE(test_exist_not_found)
 
 TEST_CASE(static_env_test)
 {
+    static_test_env static_env;
     TEST_CHECK(is_directory(StaticEnv::Dir));
     TEST_CHECK(is_directory(StaticEnv::SymlinkToDir));
     TEST_CHECK(!is_directory(StaticEnv::File));
index e339324..c92c66c 100644 (file)
@@ -45,6 +45,7 @@ TEST_CASE(test_exist_not_found)
 
 TEST_CASE(test_is_empty_directory)
 {
+    static_test_env static_env;
     TEST_CHECK(!is_empty(StaticEnv::Dir));
     TEST_CHECK(!is_empty(StaticEnv::SymlinkToDir));
 }
index 75ab605..603a1da 100644 (file)
@@ -63,6 +63,7 @@ TEST_CASE(is_symlink_status_test)
 
 TEST_CASE(static_env_test)
 {
+    static_test_env static_env;
     struct TestCase {
         path p;
         bool expect;
index 3ef4c53..4335c7a 100644 (file)
@@ -350,6 +350,7 @@ TEST_CASE(signature_test)
 
 TEST_CASE(read_last_write_time_static_env_test)
 {
+    static_test_env static_env;
     using C = file_time_type::clock;
     file_time_type min = file_time_type::min();
     {
index 2a8d829..21529d3 100644 (file)
@@ -54,18 +54,21 @@ TEST_CASE(test_signature_3) {
 }
 
 TEST_CASE(test_signature_4) {
+  static_test_env static_env;
   fs::path p(StaticEnv::SymlinkToDir);
   const fs::path output = fs::weakly_canonical(p);
   TEST_CHECK(output == std::string(StaticEnv::Dir));
 }
 
 TEST_CASE(test_signature_5) {
+  static_test_env static_env;
   fs::path p(StaticEnv::SymlinkToDir / "dir2/.");
   const fs::path output = fs::weakly_canonical(p);
   TEST_CHECK(output == std::string(StaticEnv::Dir / "dir2"));
 }
 
 TEST_CASE(test_signature_6) {
+  static_test_env static_env;
   // FIXME? If the trailing separator occurs in a part of the path that exists,
   // it is omitted. Otherwise it is added to the end of the result.
   fs::path p(StaticEnv::SymlinkToDir / "dir2/./");
@@ -74,24 +77,28 @@ TEST_CASE(test_signature_6) {
 }
 
 TEST_CASE(test_signature_7) {
+  static_test_env static_env;
   fs::path p(StaticEnv::SymlinkToDir / "dir2/DNE/./");
   const fs::path output = fs::weakly_canonical(p);
   TEST_CHECK(output == std::string(StaticEnv::Dir / "dir2/DNE/"));
 }
 
 TEST_CASE(test_signature_8) {
+  static_test_env static_env;
   fs::path p(StaticEnv::SymlinkToDir / "dir2");
   const fs::path output = fs::weakly_canonical(p);
   TEST_CHECK(output == std::string(StaticEnv::Dir2));
 }
 
 TEST_CASE(test_signature_9) {
+  static_test_env static_env;
   fs::path p(StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..");
   const fs::path output = fs::weakly_canonical(p);
   TEST_CHECK(output == std::string(StaticEnv::Dir2 / ""));
 }
 
 TEST_CASE(test_signature_10) {
+  static_test_env static_env;
   fs::path p(StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2");
   const fs::path output = fs::weakly_canonical(p);
   TEST_CHECK(output == std::string(StaticEnv::Dir2 / "DNE/DNE2"));
index 4a0936b..51b02bb 100644 (file)
@@ -80,6 +80,8 @@ TEST_CASE(test_error_reporting)
 
 TEST_CASE(basic_space_test)
 {
+    static_test_env static_env;
+
     // All the test cases should reside on the same filesystem and therefore
     // should have the same expected result. Compute this expected result
     // one and check that it looks semi-sane.
index 60c99ee..dfbd6bd 100644 (file)
@@ -103,6 +103,7 @@ TEST_CASE(test_status_cannot_resolve)
 
 TEST_CASE(status_file_types_test)
 {
+    static_test_env static_env;
     scoped_test_env env;
     struct TestCase {
       path p;
index 350076f..77f870f 100644 (file)
@@ -110,6 +110,7 @@ TEST_CASE(test_symlink_status_cannot_resolve)
 
 TEST_CASE(symlink_status_file_types_test)
 {
+    static_test_env static_env;
     scoped_test_env env;
     struct TestCase {
       path p;
index 8553317..86bd4c3 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "filesystem_include.h"
 
-#include <unistd.h> // for ftruncate
+#include <unistd.h> // for ftruncate, symlink, unlink
 
 #include <cassert>
 #include <cstdio> // for printf
@@ -105,6 +105,13 @@ static const fs::path RecDirFollowSymlinksIterationList[] = {
     makePath("dir1/dir2/symlink_to_dir3/file5"),
 };
 
+static const std::pair<fs::path, fs::path> SymlinkList[] = {
+    {"dne",        makePath("bad_symlink")},
+    {"dir1",       makePath("symlink_to_dir")},
+    {"empty_file", makePath("symlink_to_empty_file")},
+    {"dir3",       makePath("dir1/dir2/symlink_to_dir3")}
+};
+
 } // namespace StaticEnv
 
 namespace random_utils {
@@ -121,6 +128,39 @@ inline char random_hex_char() {
 
 } // namespace random_utils
 
+/// A RAII object that prepares the 'static_test_env' directory for usage in
+/// tests.
+///
+/// Namely, it creates some symlinks that tests expect to be present.
+/// We do it here instead of storing them in the repo to be
+/// cross-toolchain-friendly. The primary use case for this are Windows-hosted
+/// cross-toolchains.
+/// Windows doesn't really have a concept of symlinks. So, when the monorepo
+/// is cloned, those symlinks turn to ordinary text files.
+/// If we cross-compiled libc++ for some symlink-friendly system
+/// (e. g. Linux) and ran tests on the target system, some tests would fail.
+/// Instead, we create symlinks here, when a test is already being executed.
+class static_test_env {
+public:
+  static_test_env() {
+    for (const auto *link = std::begin(StaticEnv::SymlinkList);
+         link != std::end(StaticEnv::SymlinkList);
+         ++link) {
+      int ret = ::symlink(link->first.c_str(), link->second.c_str());
+      assert(ret == 0);
+    }
+  }
+
+  ~static_test_env() {
+    for (const auto *link = std::begin(StaticEnv::SymlinkList);
+         link != std::end(StaticEnv::SymlinkList);
+         ++link) {
+      int ret = ::unlink(link->second.c_str());
+      assert(ret == 0);
+    }
+  }
+};
+
 struct scoped_test_env
 {
     scoped_test_env() : test_root(random_path()) {