]> git.saurik.com Git - apple/hfs.git/blob - tests/hfs-tests.mm
054d1f6c4a6280e810769e8006009578166a186a
[apple/hfs.git] / tests / hfs-tests.mm
1 //
2 // hfs-tests.mm
3 // hfs
4 //
5 // Created by Chris Suter on 8/11/15.
6 //
7 //
8
9 #include <Foundation/Foundation.h>
10 #include <unordered_map>
11 #include <string>
12 #include <list>
13 #include <unistd.h>
14 #include <getopt.h>
15 #include <err.h>
16 #include <fcntl.h>
17 #include <sysexits.h>
18 #include <stdlib.h>
19 #include <iostream>
20 #include <mach-o/dyld.h>
21 #include <sys/param.h>
22
23 #include "hfs-tests.h"
24 #include "test-utils.h"
25 #include "disk-image.h"
26 #include "systemx.h"
27
28 #define INSTALL_PATH "/AppleInternal/CoreOS/tests/hfs/hfs-tests"
29
30 typedef std::unordered_map<std::string, test_t *> tests_t;
31 static tests_t *tests;
32
33 static std::list<bool (^)(void)> cleanups;
34
35 int test_cleanup(bool (^ cleanup)(void))
36 {
37 cleanups.push_front(cleanup);
38 return 0;
39 }
40
41 void do_cleanups(void)
42 {
43 bool progress = true;
44 int attempts = 0;
45 while (progress || (attempts < 2)) {
46 size_t i, sz = cleanups.size();
47
48 progress = false;
49 for (i = 0; i < sz; i++) {
50 bool (^cleanup)(void) = cleanups.front();
51 cleanups.pop_front();
52 if (cleanup() == false)
53 cleanups.push_back(cleanup);
54 else
55 progress = true;
56 }
57
58 if (!progress)
59 attempts++;
60 else
61 attempts = 0; // reset
62 }
63 }
64
65 void register_test(test_t *test)
66 {
67 if (!tests)
68 tests = new tests_t;
69
70 tests->insert({ test->name, test });
71 }
72
73 void usage()
74 {
75 printf("hfs-tests [--test <test>|--plist] <list|run>\n");
76 exit(EX_USAGE);
77 }
78
79 static int run_test(test_t *test)
80 {
81 int ret;
82
83 @autoreleasepool {
84 test_ctx_t ctx = {};
85
86 std::cout << "[TEST] " << test->name << std::endl
87 << "[BEGIN]" << std::endl;
88
89 std::flush(std::cout);
90
91 if (test->run_as_root && geteuid() != 0 && seteuid(0) != 0) {
92 std::cout << "error: " << test->name
93 << ": needs to run as root!" << std::endl;
94 ret = 1;
95 } else {
96 if (!test->run_as_root && geteuid() == 0)
97 seteuid(501);
98 ret = test->test_fn(&ctx);
99 fflush(stdout);
100 }
101 }
102
103 return ret;
104 }
105
106 int main(int argc, char *argv[])
107 {
108 if (!tests)
109 tests = new tests_t;
110
111 @autoreleasepool {
112 const char *test = NULL;
113 bool plist = false, nospawn = false;
114 int ch;
115
116 static struct option longopts[] = {
117 { "test", required_argument, NULL, 't' },
118 { "plist", no_argument, NULL, 'p' },
119 { "no-spawn", no_argument, NULL, 'n' }, // private
120 { NULL, 0, NULL, 0 }
121 };
122
123 while ((ch = getopt_long(argc, argv, "bf:", longopts, NULL)) != -1) {
124 switch (ch) {
125 case 't':
126 test = optarg;
127 break;
128 case 'p':
129 plist = true;
130 break;
131 case 'n':
132 nospawn = true;
133 break;
134 default:
135 usage();
136 }
137 }
138
139 char progname[MAXPATHLEN];
140 uint32_t sz = MAXPATHLEN;
141 assert(!_NSGetExecutablePath(progname, &sz));
142
143 argc -= optind;
144 argv += optind;
145
146 if (argc != 1)
147 usage();
148
149 int ret = 0;
150
151 if (!strcmp(argv[0], "list")) {
152 if (plist) {
153 NSMutableArray *cases = [NSMutableArray new];
154
155 for (auto it = tests->begin(); it != tests->end(); ++it) {
156 NSMutableDictionary *test_case = [@{
157 @"TestName": @(it->first.c_str()),
158 @"Command": @[ @INSTALL_PATH, @"--test",
159 @(it->first.c_str()), @"run"]
160 } mutableCopy];
161
162 test_case[@"AsRoot"] = (id)kCFBooleanTrue;
163
164 [cases addObject:test_case];
165 }
166
167 std::cout
168 << (char *)[[NSPropertyListSerialization
169 dataWithPropertyList:@{
170 @"Project": @"hfs",
171 @"Tests": cases
172 }
173 format:NSPropertyListXMLFormat_v1_0
174 options:0
175 error:NULL] bytes] << std::endl;
176 } else {
177 for (auto it = tests->begin(); it != tests->end(); ++it) {
178 std::cout << it->first << std::endl;
179 }
180 }
181 } else if (!strcmp(argv[0], "run")) {
182 disk_image_t *di = NULL;
183
184 if (!nospawn) {
185 // Set up the shared disk image
186 assert(!systemx("/bin/rm", SYSTEMX_QUIET, "-rf", SHARED_MOUNT, NULL));
187 di = disk_image_get();
188 }
189
190 if (!test) {
191 // Run all tests
192 for (auto it = tests->begin(); it != tests->end(); ++it) {
193 test_t *test = it->second;
194
195 int res = systemx(progname, "--test", test->name, "--no-spawn", "run", NULL);
196
197 if (res)
198 std::cout << "[FAIL] " << test->name << std::endl;
199 else
200 std::cout << "[PASS] " << test->name << std::endl;
201
202 if (!ret)
203 ret = res;
204 }
205 } else {
206 auto it = tests->find(test);
207 if (it == tests->end()) {
208 std::cout << "unknown test: " << test << std::endl;
209 ret = EX_USAGE;
210 } else {
211 if (nospawn) {
212 atexit_b(^{
213 do_cleanups();
214 });
215 ret = run_test(it->second);
216 }
217 else {
218 test_t *test = it->second;
219
220 int ret = systemx(progname, "--test", test->name, "--no-spawn", "run", NULL);
221
222 if (ret)
223 std::cout << "[FAIL] " << test->name << std::endl;
224 else
225 std::cout << "[PASS] " << test->name << std::endl;
226 }
227 }
228 }
229
230 if (di) {
231 disk_image_cleanup(di);
232 systemx("/bin/rm", SYSTEMX_QUIET, "-rf", "/tmp/mnt", NULL);
233 }
234
235 }
236
237 return ret;
238 }
239 }