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 
28 namespace runtests {
29 
30 static constexpr char kExpectedJSONOutputPrefix[] = "{\n  \"tests\": [\n";
31 // We don't want to count the null terminator.
32 static 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.
37 class ScopedScriptFile {
38 
39 public:
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 
48 private:
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.
55 class ScopedTestFile {
56 
57 public:
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 
64 private:
65     const fbl::StringPiece path_;
66 };
67 
68 
69 // Creates a subdirectory of TestFsRoot() in its constructor and deletes it in
70 // its destructor.
71 class ScopedTestDir {
72 
73 public:
ScopedTestDir()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     }
~ScopedTestDir()82     ~ScopedTestDir() { CleanUpDir(path_.c_str()); }
basename()83     const char* basename() { return basename_.c_str(); }
path()84     const char* path() { return path_.c_str(); }
85 
86 private:
NextBasename()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.
CleanUpDir(const char * dir_path)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 
128 class TestStopwatch : public Stopwatch {
129 public:
Start()130     void Start() override { start_called_ = true; }
DurationInMsecs()131     int64_t DurationInMsecs() override {
132         BEGIN_HELPER;
133         EXPECT_TRUE(start_called_);
134         END_HELPER;
135         return 14u;
136     }
137 
138 private:
139     bool start_called_ = false;
140 };
141 
142 
143 // Returns the number of files or subdirectories in a given directory.
144 int NumEntriesInDir(const char* dir_path);
145 
146 // Returns true if and only if the contents of |file| match |expected|.
147 bool 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.
153 bool GetOutputFileRelPath(const fbl::StringPiece& output_dir,
154                           const fbl::StringPiece& test_path,
155                           fbl::String* output_file_rel_path);
156 
157 } // namespace runtests
158