| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <dirent.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <fbl/auto_call.h> |
| #include <fbl/string.h> |
| #include <fbl/string_buffer.h> |
| #include <fbl/unique_fd.h> |
| #include <fbl/unique_ptr.h> |
| #include <fbl/vector.h> |
| #include <runtests-utils/runtests-utils.h> |
| #include <unittest/unittest.h> |
| |
| #include "runtests-utils-test-globals.h" |
| #include "runtests-utils-test-utils.h" |
| |
| |
| namespace runtests { |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // HELPER CLASSES |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| ScopedScriptFile::ScopedScriptFile(const fbl::StringPiece path, |
| const fbl::StringPiece contents) |
| : path_(path) { |
| const int fd = open(path_.data(), O_CREAT | O_WRONLY, S_IRWXU); |
| ZX_ASSERT_MSG(-1 != fd, "%s", strerror(errno)); |
| ZX_ASSERT( |
| sizeof(kScriptShebang) == |
| static_cast<size_t>(write(fd, kScriptShebang, sizeof(kScriptShebang)))); |
| ZX_ASSERT(contents.size() == |
| static_cast<size_t>(write(fd, contents.data(), contents.size()))); |
| ZX_ASSERT_MSG(-1 != close(fd), "%s", strerror(errno)); |
| } |
| |
| ScopedScriptFile::~ScopedScriptFile() { |
| remove(path_.data()); |
| } |
| |
| fbl::StringPiece ScopedScriptFile::path() const { |
| return path_; |
| } |
| |
| ScopedTestFile::ScopedTestFile( |
| const fbl::StringPiece path, const fbl::StringPiece file) |
| : path_(path) { |
| fbl::unique_fd input_fd{open(file.data(), O_RDONLY)}; |
| ZX_ASSERT_MSG(input_fd, "%s", strerror(errno)); |
| |
| fbl::unique_fd output_fd{open(path_.data(), O_CREAT | O_WRONLY, S_IRWXU)}; |
| ZX_ASSERT_MSG(output_fd, "%s", strerror(errno)); |
| |
| constexpr size_t kBufSize = 1024; |
| |
| char buf[kBufSize]; |
| ssize_t n; |
| while ((n = read(input_fd.get(), buf, kBufSize)) > 0) { |
| ZX_ASSERT_MSG(write(output_fd.get(), buf, n) == n, "write failed: %s", strerror(errno)); |
| } |
| ZX_ASSERT_MSG(n != -1, "read failed: %s", strerror(errno)); |
| } |
| |
| ScopedTestFile::~ScopedTestFile() { |
| remove(path_.data()); |
| } |
| |
| fbl::StringPiece ScopedTestFile::path() const { |
| return path_; |
| } |
| |
| int ScopedTestDir::num_test_dirs_created_ = 0; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // FILE I/O HELPERS |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| // Returns the number of files or subdirectories in a given directory. |
| int NumEntriesInDir(const char* dir_path) { |
| struct dirent* entry; |
| int num_entries = 0; |
| DIR* dp; |
| |
| if (!(dp = opendir(dir_path))) { |
| // dir_path actually points to a file. Return -1 by convention. |
| return -1; |
| } |
| while ((entry = readdir(dp))) { |
| // Skip "." and "..". |
| if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { |
| continue; |
| } |
| ++num_entries; |
| } |
| closedir(dp); |
| return num_entries; |
| } |
| |
| // Computes the relative path within |output_dir| of the output file of the |
| // test at |test_path|, setting |output_file_rel_path| as its value if |
| // successful. |
| // Returns true iff successful. |
| bool GetOutputFileRelPath(const fbl::StringPiece& output_dir, |
| const fbl::StringPiece& test_path, |
| fbl::String* output_file_rel_path) { |
| if (output_file_rel_path == nullptr) { |
| printf("FAILURE: |output_file_rel_path| was null."); |
| return false; |
| } |
| fbl::String dir_of_test_output = JoinPath(output_dir, test_path); |
| DIR* dp = opendir(dir_of_test_output.c_str()); |
| if (dp == nullptr) { |
| printf("FAILURE: could not open directory: %s\n", dir_of_test_output.c_str()); |
| return false; |
| } |
| struct dirent* entry; |
| int num_entries = 0; |
| fbl::String output_file_name; |
| while ((entry = readdir(dp))) { |
| // Skip "." and "..". |
| if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { |
| continue; |
| } |
| if (entry->d_type != DT_REG) { |
| continue; |
| } |
| output_file_name = fbl::String(entry->d_name); |
| ++num_entries; |
| } |
| closedir(dp); |
| *output_file_rel_path = JoinPath(test_path, output_file_name); |
| if (num_entries != 1) { |
| printf("FAILURE: there are %d entries in %s. There should only be a " |
| "single output file\n", |
| num_entries, dir_of_test_output.c_str()); |
| } |
| return num_entries == 1; |
| } |
| |
| namespace { |
| |
| // This ensures that ScopedTestDir and ScopedScriptFile, which we make heavy |
| // use of in these tests, are indeed scoped and tear down without error. |
| bool ScopedDirsAndFilesAreIndeedScoped() { |
| BEGIN_TEST; |
| |
| // Entering a test case, test_dir.path() should be empty. |
| EXPECT_EQ(0, NumEntriesInDir(TestFsRoot())); |
| |
| { |
| ScopedTestDir dir; |
| EXPECT_EQ(1, NumEntriesInDir(TestFsRoot())); |
| EXPECT_EQ(0, NumEntriesInDir(dir.path())); |
| { |
| fbl::String file_name1 = JoinPath(dir.path(), "a.sh"); |
| ScopedScriptFile file1(file_name1, "A"); |
| EXPECT_EQ(1, NumEntriesInDir(dir.path())); |
| { |
| fbl::String file_name2 = JoinPath(dir.path(), "b.sh"); |
| ScopedScriptFile file2(file_name2, "B"); |
| EXPECT_EQ(2, NumEntriesInDir(dir.path())); |
| } |
| EXPECT_EQ(1, NumEntriesInDir(dir.path())); |
| } |
| EXPECT_EQ(0, NumEntriesInDir(dir.path())); |
| } |
| |
| EXPECT_EQ(0, NumEntriesInDir(TestFsRoot())); |
| |
| { |
| ScopedTestDir dir1; |
| ScopedTestDir dir2; |
| ScopedTestDir dir3; |
| EXPECT_EQ(3, NumEntriesInDir(TestFsRoot())); |
| } |
| |
| EXPECT_EQ(0, NumEntriesInDir(TestFsRoot())); |
| |
| END_TEST; |
| } |
| |
| BEGIN_TEST_CASE(TestHelpers) |
| RUN_TEST(ScopedDirsAndFilesAreIndeedScoped) |
| END_TEST_CASE(TestHelpers) |
| |
| } // namespace |
| } // namespace runtests |