diff options
author | Laszlo Csomor <laszlocsomor@google.com> | 2016-12-16 10:25:14 +0000 |
---|---|---|
committer | John Cater <jcater@google.com> | 2016-12-16 15:36:57 +0000 |
commit | 1324e83e04757b45b78b01a1ab24bab4c6156231 (patch) | |
tree | 3188bfee0fe47a96c91dd465a371802c46cea046 /src/test/cpp | |
parent | 52b34308428cfd117470c966d8df329f686fb9ed (diff) |
Bazel client, Windows: implement pipe handling
Create WindowsPipe which implements IPipe, and is
a Windows-native implementation of pipe handling.
It wraps the ::CreatePipe, ::WriteFile, and
::ReadFile API functions.
Start using WindowsPipe on MSYS.
Also move everything from file_test.cc into
file_posix_test.cc, because these test methods
heavily depend on POSIX and they test file_posix's
functionality anyway.
Also add tests for pipes: this will use the
platform-specific implementation of IPipe.
file_test.cc is now platform-independent, and we
can test it with --cpu=x64_windows_msvc (and it
passes!).
See https://github.com/bazelbuild/bazel/issues/2107
Might also fix:
https://github.com/bazelbuild/bazel/issues/2182
https://github.com/bazelbuild/bazel/issues/2248
--
PiperOrigin-RevId: 142240377
MOS_MIGRATED_REVID=142240377
Diffstat (limited to 'src/test/cpp')
-rw-r--r-- | src/test/cpp/util/BUILD | 11 | ||||
-rw-r--r-- | src/test/cpp/util/file_posix_test.cc | 139 | ||||
-rw-r--r-- | src/test/cpp/util/file_test.cc | 167 |
3 files changed, 171 insertions, 146 deletions
diff --git a/src/test/cpp/util/BUILD b/src/test/cpp/util/BUILD index 033fca6268..e16f14f4ce 100644 --- a/src/test/cpp/util/BUILD +++ b/src/test/cpp/util/BUILD @@ -20,10 +20,13 @@ cc_test( cc_test( name = "file_test", - srcs = [ - "file_posix_test.cc", - "file_test.cc", - ], + srcs = ["file_test.cc"] + select({ + "//src:windows_msvc": [ + ], + "//conditions:default": [ + "file_posix_test.cc", + ], + }), tags = ["manual"], deps = [ "//src/main/cpp/util:file", diff --git a/src/test/cpp/util/file_posix_test.cc b/src/test/cpp/util/file_posix_test.cc index 446ada9790..bd91343672 100644 --- a/src/test/cpp/util/file_posix_test.cc +++ b/src/test/cpp/util/file_posix_test.cc @@ -16,6 +16,7 @@ #include <algorithm> +#include "src/main/cpp/util/file.h" #include "src/main/cpp/util/file_platform.h" #include "gtest/gtest.h" @@ -25,6 +26,144 @@ using std::pair; using std::string; using std::vector; +static bool Symlink(const string& old_path, const string& new_path) { + return symlink(old_path.c_str(), new_path.c_str()) == 0; +} + +static bool CreateEmptyFile(const string& path) { + // From the man page of open (man 2 open): + // int open(const char *pathname, int flags, mode_t mode); + // + // mode specifies the permissions to use in case a new file is created. + // This argument must be supplied when O_CREAT is specified in flags; + // if O_CREAT is not specified, then mode is ignored. + int fd = open(path.c_str(), O_CREAT | O_WRONLY, 0700); + if (fd == -1) { + return false; + } + return close(fd) == 0; +} + +TEST(FileTest, JoinPath) { + std::string path = JoinPath("", ""); + ASSERT_EQ("", path); + + path = JoinPath("a", "b"); + ASSERT_EQ("a/b", path); + + path = JoinPath("a/", "b"); + ASSERT_EQ("a/b", path); + + path = JoinPath("a", "/b"); + ASSERT_EQ("a/b", path); + + path = JoinPath("a/", "/b"); + ASSERT_EQ("a/b", path); + + path = JoinPath("/", "/"); + ASSERT_EQ("/", path); +} + +void MockDirectoryListingFunction(const string& path, + DirectoryEntryConsumer* consume) { + if (path == "root") { + consume->Consume("root/file1", false); + consume->Consume("root/dir2", true); + consume->Consume("root/dir1", true); + } else if (path == "root/dir1") { + consume->Consume("root/dir1/dir3", true); + consume->Consume("root/dir1/file2", false); + } else if (path == "root/dir2") { + consume->Consume("root/dir2/file3", false); + } else if (path == "root/dir1/dir3") { + consume->Consume("root/dir1/dir3/file4", false); + consume->Consume("root/dir1/dir3/file5", false); + } else { + // Unexpected path + GTEST_FAIL(); + } +} + +TEST(FileTest, GetAllFilesUnder) { + vector<string> result; + _GetAllFilesUnder("root", &result, &MockDirectoryListingFunction); + std::sort(result.begin(), result.end()); + + vector<string> expected({"root/dir1/dir3/file4", "root/dir1/dir3/file5", + "root/dir1/file2", "root/dir2/file3", "root/file1"}); + ASSERT_EQ(expected, result); +} + +TEST(FileTest, MakeDirectories) { + const char* tmp_dir = getenv("TEST_TMPDIR"); + ASSERT_STRNE(tmp_dir, NULL); + const char* test_src_dir = getenv("TEST_SRCDIR"); + ASSERT_STRNE(NULL, test_src_dir); + + string dir = JoinPath(tmp_dir, "x/y/z"); + bool ok = MakeDirectories(dir, 0755); + ASSERT_TRUE(ok); + + // Changing permissions on an existing dir should work. + ok = MakeDirectories(dir, 0750); + ASSERT_TRUE(ok); + struct stat filestat = {}; + ASSERT_EQ(0, stat(dir.c_str(), &filestat)); + ASSERT_EQ(0750, filestat.st_mode & 0777); + + // srcdir shouldn't be writable. + // TODO(ulfjack): Fix this! + // string srcdir = JoinPath(test_src_dir, "x/y/z"); + // ok = MakeDirectories(srcdir, 0755); + // ASSERT_FALSE(ok); + // ASSERT_EQ(EACCES, errno); + + // Can't make a dir out of a file. + string non_dir = JoinPath(dir, "w"); + ASSERT_TRUE(CreateEmptyFile(non_dir)); + ok = MakeDirectories(non_dir, 0755); + ASSERT_FALSE(ok); + ASSERT_EQ(ENOTDIR, errno); + + // Valid symlink should work. + string symlink = JoinPath(tmp_dir, "z"); + ASSERT_TRUE(Symlink(dir, symlink)); + ok = MakeDirectories(symlink, 0755); + ASSERT_TRUE(ok); + + // Error: Symlink to a file. + symlink = JoinPath(tmp_dir, "w"); + ASSERT_TRUE(Symlink(non_dir, symlink)); + ok = MakeDirectories(symlink, 0755); + ASSERT_FALSE(ok); + ASSERT_EQ(ENOTDIR, errno); + + // Error: Symlink to a dir with wrong perms. + symlink = JoinPath(tmp_dir, "s"); + ASSERT_TRUE(Symlink("/", symlink)); + + // These perms will force a chmod() + // TODO(ulfjack): Fix this! + // ok = MakeDirectories(symlink, 0000); + // ASSERTFALSE(ok); + // ASSERT_EQ(EPERM, errno); + + // Edge cases. + ASSERT_FALSE(MakeDirectories("", 0755)); + ASSERT_EQ(EACCES, errno); + ASSERT_FALSE(MakeDirectories("/", 0755)); + ASSERT_EQ(EACCES, errno); +} + +TEST(FileTest, HammerMakeDirectories) { + const char* tmp_dir = getenv("TEST_TMPDIR"); + ASSERT_STRNE(tmp_dir, NULL); + + string path = JoinPath(tmp_dir, "x/y/z"); + // TODO(ulfjack): Fix this! + // ASSERT_LE(0, fork()); + // ASSERT_TRUE(MakeDirectories(path, 0755)); +} TEST(FilePosixTest, Which) { ASSERT_EQ("", Which("")); ASSERT_EQ("", Which("foo")); diff --git a/src/test/cpp/util/file_test.cc b/src/test/cpp/util/file_test.cc index eca1ab2ae0..49c52c0533 100644 --- a/src/test/cpp/util/file_test.cc +++ b/src/test/cpp/util/file_test.cc @@ -11,9 +11,10 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#include <fcntl.h> -#include <unistd.h> -#include <algorithm> +#include <string.h> + +#include <memory> // unique_ptr +#include <thread> // NOLINT (to slience Google-internal linter) #include "src/main/cpp/util/file.h" #include "src/main/cpp/util/file_platform.h" @@ -21,149 +22,31 @@ namespace blaze_util { -using std::string; -using std::vector; - -static bool Symlink(const string& old_path, const string& new_path) { - return symlink(old_path.c_str(), new_path.c_str()) == 0; -} - -static bool CreateEmptyFile(const string& path) { - // From the man page of open (man 2 open): - // int open(const char *pathname, int flags, mode_t mode); - // - // mode specifies the permissions to use in case a new file is created. - // This argument must be supplied when O_CREAT is specified in flags; - // if O_CREAT is not specified, then mode is ignored. - int fd = open(path.c_str(), O_CREAT | O_WRONLY, 0700); - if (fd == -1) { - return false; - } - return close(fd) == 0; -} - -TEST(FileTest, JoinPath) { - std::string path = JoinPath("", ""); - ASSERT_EQ("", path); - - path = JoinPath("a", "b"); - ASSERT_EQ("a/b", path); - - path = JoinPath("a/", "b"); - ASSERT_EQ("a/b", path); - - path = JoinPath("a", "/b"); - ASSERT_EQ("a/b", path); - - path = JoinPath("a/", "/b"); - ASSERT_EQ("a/b", path); - - path = JoinPath("/", "/"); - ASSERT_EQ("/", path); -} - -void MockDirectoryListingFunction(const string &path, - DirectoryEntryConsumer *consume) { - if (path == "root") { - consume->Consume("root/file1", false); - consume->Consume("root/dir2", true); - consume->Consume("root/dir1", true); - } else if (path == "root/dir1") { - consume->Consume("root/dir1/dir3", true); - consume->Consume("root/dir1/file2", false); - } else if (path == "root/dir2") { - consume->Consume("root/dir2/file3", false); - } else if (path == "root/dir1/dir3") { - consume->Consume("root/dir1/dir3/file4", false); - consume->Consume("root/dir1/dir3/file5", false); - } else { - // Unexpected path - GTEST_FAIL(); - } +TEST(FileTest, TestSingleThreadedPipe) { + std::unique_ptr<IPipe> pipe(CreatePipe()); + char buffer[50] = {0}; + ASSERT_TRUE(pipe.get()->Send("hello", 5)); + ASSERT_EQ(3, pipe.get()->Receive(buffer, 3)); + ASSERT_TRUE(pipe.get()->Send(" world", 6)); + ASSERT_EQ(5, pipe.get()->Receive(buffer + 3, 5)); + ASSERT_EQ(3, pipe.get()->Receive(buffer + 8, 40)); + ASSERT_EQ(0, strncmp(buffer, "hello world", 11)); } -TEST(FileTest, GetAllFilesUnder) { - vector<string> result; - _GetAllFilesUnder("root", &result, &MockDirectoryListingFunction); - std::sort(result.begin(), result.end()); - - vector<string> expected({"root/dir1/dir3/file4", - "root/dir1/dir3/file5", - "root/dir1/file2", - "root/dir2/file3", - "root/file1"}); - ASSERT_EQ(expected, result); -} - -TEST(FileTest, MakeDirectories) { - const char* tmp_dir = getenv("TEST_TMPDIR"); - ASSERT_STRNE(tmp_dir, NULL); - const char* test_src_dir = getenv("TEST_SRCDIR"); - ASSERT_STRNE(NULL, test_src_dir); - - string dir = JoinPath(tmp_dir, "x/y/z"); - bool ok = MakeDirectories(dir, 0755); - ASSERT_TRUE(ok); - - // Changing permissions on an existing dir should work. - ok = MakeDirectories(dir, 0750); - ASSERT_TRUE(ok); - struct stat filestat = {}; - ASSERT_EQ(0, stat(dir.c_str(), &filestat)); - ASSERT_EQ(0750, filestat.st_mode & 0777); - - // srcdir shouldn't be writable. - // TODO(ulfjack): Fix this! - // string srcdir = JoinPath(test_src_dir, "x/y/z"); - // ok = MakeDirectories(srcdir, 0755); - // ASSERT_FALSE(ok); - // ASSERT_EQ(EACCES, errno); - - // Can't make a dir out of a file. - string non_dir = JoinPath(dir, "w"); - ASSERT_TRUE(CreateEmptyFile(non_dir)); - ok = MakeDirectories(non_dir, 0755); - ASSERT_FALSE(ok); - ASSERT_EQ(ENOTDIR, errno); - - // Valid symlink should work. - string symlink = JoinPath(tmp_dir, "z"); - ASSERT_TRUE(Symlink(dir, symlink)); - ok = MakeDirectories(symlink, 0755); - ASSERT_TRUE(ok); - - // Error: Symlink to a file. - symlink = JoinPath(tmp_dir, "w"); - ASSERT_TRUE(Symlink(non_dir, symlink)); - ok = MakeDirectories(symlink, 0755); - ASSERT_FALSE(ok); - ASSERT_EQ(ENOTDIR, errno); - - // Error: Symlink to a dir with wrong perms. - symlink = JoinPath(tmp_dir, "s"); - ASSERT_TRUE(Symlink("/", symlink)); - - // These perms will force a chmod() - // TODO(ulfjack): Fix this! - // ok = MakeDirectories(symlink, 0000); - // ASSERTFALSE(ok); - // ASSERT_EQ(EPERM, errno); - - // Edge cases. - ASSERT_FALSE(MakeDirectories("", 0755)); - ASSERT_EQ(EACCES, errno); - ASSERT_FALSE(MakeDirectories("/", 0755)); - ASSERT_EQ(EACCES, errno); -} +TEST(FileTest, TestMultiThreadedPipe) { + std::unique_ptr<IPipe> pipe(CreatePipe()); + char buffer[50] = {0}; + std::thread writer_thread([&pipe]() { + ASSERT_TRUE(pipe.get()->Send("hello", 5)); + ASSERT_TRUE(pipe.get()->Send(" world", 6)); + }); -TEST(FileTest, HammerMakeDirectories) { - const char* tmp_dir = getenv("TEST_TMPDIR"); - ASSERT_STRNE(tmp_dir, NULL); + ASSERT_EQ(3, pipe.get()->Receive(buffer, 3)); + ASSERT_EQ(5, pipe.get()->Receive(buffer + 3, 5)); + ASSERT_EQ(3, pipe.get()->Receive(buffer + 8, 40)); + writer_thread.join(); - string path = JoinPath(tmp_dir, "x/y/z"); - // TODO(ulfjack): Fix this! - // ASSERT_LE(0, fork()); - // ASSERT_TRUE(MakeDirectories(path, 0755)); + ASSERT_EQ(0, strncmp(buffer, "hello world", 11)); } } // namespace blaze_util |