summaryrefslogtreecommitdiff
path: root/tools/slang-test/os.h
blob: 3466f818caef7f1c72ba0527299c127c5b610618 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// os.h

#include "../../source/core/slang-io.h"

// This file encapsulates the platform-specific operations needed by the test
// runner that are not already provided by the core Slang libs

#ifdef _WIN32

// Include Windows header in a way that minimized namespace pollution.
// TODO: We could try to avoid including this at all, but it would
// mean trying to hide certain struct layouts, which would add
// more dynamic allocation.
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX

#else

#include <dirent.h>
#include <errno.h>
#include <poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#endif

// A simple set of error codes for possible runtime failures
enum OSError
{
    kOSError_None = 0,
    kOSError_InvalidArgument,
    kOSError_OperationFailed,
    kOSError_FileNotFound,
};

// A helper type used during enumeration of files in a directory.
struct OSFindFilesResult
{
    Slang::String				directoryPath_;
    Slang::String				filePath_;
#ifdef WIN32
    HANDLE				findHandle_;
    WIN32_FIND_DATAW	fileData_;
    DWORD				requiredMask_;
    DWORD				disallowedMask_;
    OSError				error_;
#else
    DIR*         directory_;
    dirent*      entry_;
#endif

    bool findNextFile();

    struct Iterator
    {
        OSFindFilesResult* context_;

        bool operator!=(Iterator other) const { return context_ != other.context_; }
        void operator++()
        {
            if (!context_->findNextFile())
            {
                context_ = NULL;
            }
        }
        Slang::String const& operator*() const
        {
            return context_->filePath_;
        }
    };

    Iterator begin()
    {
#ifdef WIN32
        Iterator result = { findHandle_ ? this : NULL };
#else
        Iterator result = { entry_ ? this : NULL };
#endif
        return result;
    }

    Iterator end()
    {
        Iterator result = { NULL };
        return result;
    }
};

// Enumerate subdirectories in the given `directoryPath` and return a logical
// collection of the results that can be iterated with a range-based
// `for` loop:
//
// for( auto subdir : osFindChildDirectories(dir))
// { ... }
//
// Each element in the range is a `Slang::String` representing the
// path to a subdirecotry of the directory.
OSFindFilesResult osFindChildDirectories(
    Slang::String directoryPath);

// Enumerate files in the given `directoryPath` that match the provided
// `pattern` as a simplified regex for files to return (e.g., "*.txt")
// and return a logical collection of the results
// that can be iterated with a range-based `for` loop:
//
// for( auto file : osFindFilesInDirectoryMatchingPattern(dir, "*.txt"))
// { ... }
//
// Each element in the range is a `Slang::String` representing the
// path to a file in the directory.
OSFindFilesResult osFindFilesInDirectoryMatchingPattern(
    Slang::String directoryPath,
    Slang::String pattern);

// Enumerate files in the given `directoryPath`  and return a logical
// collection of the results that can be iterated with a range-based
// `for` loop:
//
// for( auto file : osFindFilesInDirectory(dir))
// { ... }
//
// Each element in the range is a `Slang::String` representing the
// path to a file in the directory.
OSFindFilesResult osFindFilesInDirectory(
    Slang::String directoryPath);


// An `OSProcessSpawner` can be used to launch a process, and handles
// putting together the arguments in the form required by the target
// platform, as well as capturing any output from the process (both
// standard output and standard error) as strings.
struct OSProcessSpawner
{
    // Set the executable name for the process to be spawned.
    // Note: this call must be made before any arguments are pushed.
    void pushExecutableName(
        Slang::String executableName);

    // Set the executable name for the process to be spawned.
    // Note: this call must be made before any arguments are pushed.
    void pushExecutablePath(
        Slang::String executablePath);

    // Append an argument for the process to be spawned.
    void pushArgument(
        Slang::String argument);

    // Get a printable version of the command line
    // that will be run (can be used for debugging)
    Slang::String getCommandLine();

    // Attempt to spawn the process, and wait for it to complete.
    // Returns an error if the attempt to spawn and/or wait fails,
    // but returns `kOSError_None` if the process is run to completion,
    // whether or not the process returns "successfully" (with a zero
    // result code);
    OSError spawnAndWaitForCompletion();

    // If the process is successfully spawned and completes, then
    // the user can query the result code that the process produce
    // on exit, along with the output it wrote to stdout and stderr.
    typedef int ResultCode;
    ResultCode getResultCode() { return resultCode_; }
    Slang::String const& getStandardOutput() { return standardOutput_; }
    Slang::String const& getStandardError() { return standardError_; }

    // "private" data follows
    Slang::String standardOutput_;
    Slang::String standardError_;
    ResultCode resultCode_;
    Slang::String executableName_;
#ifdef WIN32
    Slang::StringBuilder commandLine_;
#else
    Slang::List<Slang::String>  arguments_;

#endif
    // Is the executable specified by path, rather than just by name?
    bool isExecutablePath_;
};

char const* osGetExecutableSuffix();