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#pragma once 6 7#include <dirent.h> 8#include <errno.h> 9#include <fcntl.h> 10#include <limits.h> 11#include <stdio.h> 12#include <stdlib.h> 13#include <sys/stat.h> 14#include <unistd.h> 15 16#include <fbl/auto_call.h> 17#include <fbl/string.h> 18#include <fbl/string_buffer.h> 19#include <fbl/unique_fd.h> 20#include <fbl/unique_ptr.h> 21#include <fbl/vector.h> 22#include <runtests-utils/runtests-utils.h> 23#include <unittest/unittest.h> 24 25#include "runtests-utils-test-globals.h" 26 27 28namespace runtests { 29 30static constexpr char kExpectedJSONOutputPrefix[] = "{\n \"tests\": [\n"; 31// We don't want to count the null terminator. 32static constexpr size_t kExpectedJSONOutputPrefixSize = 33 sizeof(kExpectedJSONOutputPrefix) - 1; 34 35// Creates a script file with given contents in its constructor and deletes it 36// in its destructor. 37class ScopedScriptFile { 38 39public: 40 // |path| is the path of the file to be created. Should start with 41 // kMemFsPath. |contents| are the script contents. Shebang line will be 42 // added automatically. 43 ScopedScriptFile(const fbl::StringPiece path, 44 const fbl::StringPiece contents); 45 ~ScopedScriptFile(); 46 fbl::StringPiece path() const; 47 48private: 49 const fbl::StringPiece path_; 50}; 51 52 53// Creates a script file with given contents in its constructor and deletes it 54// in its destructor. 55class ScopedTestFile { 56 57public: 58 // |path| is the path of the file to be created. Should start with kMemFsPath. 59 // |contents| are the script contents. Shebang line will be added automatically. 60 ScopedTestFile(const fbl::StringPiece path, const fbl::StringPiece file); 61 ~ScopedTestFile(); 62 fbl::StringPiece path() const; 63 64private: 65 const fbl::StringPiece path_; 66}; 67 68 69// Creates a subdirectory of TestFsRoot() in its constructor and deletes it in 70// its destructor. 71class ScopedTestDir { 72 73public: 74 ScopedTestDir() 75 : basename_(NextBasename()), path_(JoinPath(TestFsRoot(), basename_)) { 76 if (mkdir(path_.c_str(), 0755)) { 77 printf("FAILURE: mkdir failed to open %s: %s\n", path_.c_str(), 78 strerror(errno)); 79 exit(1); 80 } 81 } 82 ~ScopedTestDir() { CleanUpDir(path_.c_str()); } 83 const char* basename() { return basename_.c_str(); } 84 const char* path() { return path_.c_str(); } 85 86private: 87 fbl::String NextBasename() { 88 // More than big enough to print INT_MAX. 89 char buf[64]; 90 sprintf(buf, "%d", num_test_dirs_created_++); 91 return fbl::String(buf); 92 } 93 94 // Recursively removes the directory at |dir_path| and its contents. 95 static void CleanUpDir(const char* dir_path) { 96 struct dirent* entry; 97 DIR* dp; 98 99 dp = opendir(dir_path); 100 if (dp == nullptr) { 101 // File found; remove it. 102 remove(dir_path); 103 return; 104 } 105 106 while ((entry = readdir(dp))) { 107 // Skip "." and "..". 108 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { 109 continue; 110 } 111 fbl::String sub_dir_name = JoinPath(dir_path, entry->d_name); 112 CleanUpDir(sub_dir_name.c_str()); 113 } 114 closedir(dp); 115 116 // Directory is now empty: remove it. 117 rmdir(dir_path); 118 } 119 120 const fbl::String basename_; 121 const fbl::String path_; 122 123 // Used to generate unique subdirectories of TestFsRoot(). 124 static int num_test_dirs_created_; 125}; 126 127 128class TestStopwatch : public Stopwatch { 129public: 130 void Start() override { start_called_ = true; } 131 int64_t DurationInMsecs() override { 132 BEGIN_HELPER; 133 EXPECT_TRUE(start_called_); 134 END_HELPER; 135 return 14u; 136 } 137 138private: 139 bool start_called_ = false; 140}; 141 142 143// Returns the number of files or subdirectories in a given directory. 144int NumEntriesInDir(const char* dir_path); 145 146// Returns true if and only if the contents of |file| match |expected|. 147bool CompareFileContents(FILE* file, const char* expected); 148 149// Computes the relative path within |output_dir| of the output file of the 150// test at |test_path|, setting |output_file_rel_path| as its value if 151// successful. 152// Returns true iff successful. 153bool GetOutputFileRelPath(const fbl::StringPiece& output_dir, 154 const fbl::StringPiece& test_path, 155 fbl::String* output_file_rel_path); 156 157} // namespace runtests 158