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