1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <dirent.h> 6#include <errno.h> 7#include <fcntl.h> 8#include <limits.h> 9#include <stdio.h> 10#include <stdlib.h> 11#include <sys/stat.h> 12#include <unistd.h> 13 14#include <fbl/auto_call.h> 15#include <fbl/string.h> 16#include <fbl/string_buffer.h> 17#include <fbl/unique_fd.h> 18#include <fbl/unique_ptr.h> 19#include <fbl/vector.h> 20#include <runtests-utils/runtests-utils.h> 21#include <unittest/unittest.h> 22 23#include "runtests-utils-test-globals.h" 24#include "runtests-utils-test-utils.h" 25 26 27namespace runtests { 28 29/////////////////////////////////////////////////////////////////////////////// 30// HELPER CLASSES 31/////////////////////////////////////////////////////////////////////////////// 32 33ScopedScriptFile::ScopedScriptFile(const fbl::StringPiece path, 34 const fbl::StringPiece contents) 35 : path_(path) { 36 const int fd = open(path_.data(), O_CREAT | O_WRONLY, S_IRWXU); 37 ZX_ASSERT_MSG(-1 != fd, "%s", strerror(errno)); 38 ZX_ASSERT( 39 sizeof(kScriptShebang) == 40 static_cast<size_t>(write(fd, kScriptShebang, sizeof(kScriptShebang)))); 41 ZX_ASSERT(contents.size() == 42 static_cast<size_t>(write(fd, contents.data(), contents.size()))); 43 ZX_ASSERT_MSG(-1 != close(fd), "%s", strerror(errno)); 44} 45 46ScopedScriptFile::~ScopedScriptFile() { 47 remove(path_.data()); 48} 49 50fbl::StringPiece ScopedScriptFile::path() const { 51 return path_; 52} 53 54ScopedTestFile::ScopedTestFile( 55 const fbl::StringPiece path, const fbl::StringPiece file) 56 : path_(path) { 57 fbl::unique_fd input_fd{open(file.data(), O_RDONLY)}; 58 ZX_ASSERT_MSG(input_fd, "%s", strerror(errno)); 59 60 fbl::unique_fd output_fd{open(path_.data(), O_CREAT | O_WRONLY, S_IRWXU)}; 61 ZX_ASSERT_MSG(output_fd, "%s", strerror(errno)); 62 63 constexpr size_t kBufSize = 1024; 64 65 char buf[kBufSize]; 66 ssize_t n; 67 while ((n = read(input_fd.get(), buf, kBufSize)) > 0) { 68 ZX_ASSERT_MSG(write(output_fd.get(), buf, n) == n, "write failed: %s", strerror(errno)); 69 } 70 ZX_ASSERT_MSG(n != -1, "read failed: %s", strerror(errno)); 71} 72 73ScopedTestFile::~ScopedTestFile() { 74 remove(path_.data()); 75} 76 77fbl::StringPiece ScopedTestFile::path() const { 78 return path_; 79} 80 81int ScopedTestDir::num_test_dirs_created_ = 0; 82 83/////////////////////////////////////////////////////////////////////////////// 84// FILE I/O HELPERS 85/////////////////////////////////////////////////////////////////////////////// 86 87// Returns the number of files or subdirectories in a given directory. 88int NumEntriesInDir(const char* dir_path) { 89 struct dirent* entry; 90 int num_entries = 0; 91 DIR* dp; 92 93 if (!(dp = opendir(dir_path))) { 94 // dir_path actually points to a file. Return -1 by convention. 95 return -1; 96 } 97 while ((entry = readdir(dp))) { 98 // Skip "." and "..". 99 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { 100 continue; 101 } 102 ++num_entries; 103 } 104 closedir(dp); 105 return num_entries; 106} 107 108// Returns true if and only if the contents of |file| match |expected|. 109bool CompareFileContents(FILE* file, const char* expected) { 110 BEGIN_HELPER; 111 // Get the size of the file contents, copy it into a buffer, and compare. 112 ASSERT_EQ(0, fseek(file, 0, SEEK_END)); 113 const long unsigned int size = ftell(file); 114 rewind(file); 115 fbl::unique_ptr<char[]> buf(new char[size + 1]); 116 buf[size] = 0; 117 ASSERT_EQ(size, fread(buf.get(), sizeof(char), size, file)); 118 EXPECT_STR_EQ(expected, buf.get()); 119 END_HELPER; 120} 121 122// Computes the relative path within |output_dir| of the output file of the 123// test at |test_path|, setting |output_file_rel_path| as its value if 124// successful. 125// Returns true iff successful. 126bool GetOutputFileRelPath(const fbl::StringPiece& output_dir, 127 const fbl::StringPiece& test_path, 128 fbl::String* output_file_rel_path) { 129 if (output_file_rel_path == nullptr) { 130 printf("FAILURE: |output_file_rel_path| was null."); 131 return false; 132 } 133 fbl::String dir_of_test_output = JoinPath(output_dir, test_path); 134 DIR* dp = opendir(dir_of_test_output.c_str()); 135 if (dp == nullptr) { 136 printf("FAILURE: could not open directory: %s\n", dir_of_test_output.c_str()); 137 return false; 138 } 139 struct dirent* entry; 140 int num_entries = 0; 141 fbl::String output_file_name; 142 while ((entry = readdir(dp))) { 143 // Skip "." and "..". 144 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { 145 continue; 146 } 147 if (entry->d_type != DT_REG) { 148 continue; 149 } 150 output_file_name = fbl::String(entry->d_name); 151 ++num_entries; 152 } 153 closedir(dp); 154 *output_file_rel_path = JoinPath(test_path, output_file_name); 155 if (num_entries != 1) { 156 printf("FAILURE: there are %d entries in %s. There should only be a " 157 "single output file\n", 158 num_entries, dir_of_test_output.c_str()); 159 } 160 return num_entries == 1; 161} 162 163namespace { 164 165// This ensures that ScopedTestDir and ScopedScriptFile, which we make heavy 166// use of in these tests, are indeed scoped and tear down without error. 167bool ScopedDirsAndFilesAreIndeedScoped() { 168 BEGIN_TEST; 169 170 // Entering a test case, test_dir.path() should be empty. 171 EXPECT_EQ(0, NumEntriesInDir(TestFsRoot())); 172 173 { 174 ScopedTestDir dir; 175 EXPECT_EQ(1, NumEntriesInDir(TestFsRoot())); 176 EXPECT_EQ(0, NumEntriesInDir(dir.path())); 177 { 178 fbl::String file_name1 = JoinPath(dir.path(), "a.sh"); 179 ScopedScriptFile file1(file_name1, "A"); 180 EXPECT_EQ(1, NumEntriesInDir(dir.path())); 181 { 182 fbl::String file_name2 = JoinPath(dir.path(), "b.sh"); 183 ScopedScriptFile file2(file_name2, "B"); 184 EXPECT_EQ(2, NumEntriesInDir(dir.path())); 185 } 186 EXPECT_EQ(1, NumEntriesInDir(dir.path())); 187 } 188 EXPECT_EQ(0, NumEntriesInDir(dir.path())); 189 } 190 191 EXPECT_EQ(0, NumEntriesInDir(TestFsRoot())); 192 193 { 194 ScopedTestDir dir1; 195 ScopedTestDir dir2; 196 ScopedTestDir dir3; 197 EXPECT_EQ(3, NumEntriesInDir(TestFsRoot())); 198 } 199 200 EXPECT_EQ(0, NumEntriesInDir(TestFsRoot())); 201 202 END_TEST; 203} 204 205BEGIN_TEST_CASE(TestHelpers) 206RUN_TEST(ScopedDirsAndFilesAreIndeedScoped) 207END_TEST_CASE(TestHelpers) 208 209} // namespace 210} // namespace runtests 211