1 // restest.cpp
2
3 #include <algobase.h>
4 #include <stdio.h>
5 #include <string.h>
6
7 #include <Entry.h>
8 #include <File.h>
9 #include <String.h>
10
11 #include "Exception.h"
12 #include "OffsetFile.h"
13 #include "ResourceFile.h"
14 #include "Warnings.h"
15
16 const char kUsage[] = {
17 "Usage: %s <options> <filenames>\n"
18 "options:\n"
19 " -h, --help print this help\n"
20 " -l, --list list each file's resources (short version)\n"
21 " -L, --list-long list each file's resources (long version)\n"
22 " -s, --summary print a summary\n"
23 " -w, --write-test write the file resources (in memory only) and\n"
24 " compare the data with the file's\n"
25 };
26
27 const status_t USAGE_ERROR = B_ERRORS_END + 1;
28 const status_t USAGE_HELP = B_ERRORS_END + 2;
29
30 enum listing_level {
31 NO_LISTING,
32 SHORT_LISTING,
33 LONG_LISTING,
34 };
35
36 struct TestOptions {
37 listing_level listing;
38 bool write_test;
39 bool summary;
40 };
41
42 struct TestResult {
TestResultTestResult43 TestResult(const char* filename)
44 : filename(filename), warnings(), exception(NULL)
45 {
46 }
47
~TestResultTestResult48 ~TestResult()
49 {
50 delete exception;
51 }
52
53 BString filename;
54 Warnings warnings;
55 Exception* exception;
56 };
57
58 // print_indented
59 void
print_indented(const char * str,uint32 chars,bool indentFirst=true)60 print_indented(const char* str, uint32 chars, bool indentFirst = true)
61 {
62 const uint32 MAX_CHARS_PER_LINE = 75;
63 int32 charsLeft = strlen(str);
64 if (chars < MAX_CHARS_PER_LINE) {
65 for (int32 line = 0; charsLeft > 0; line++) {
66 if (line != 0 || indentFirst) {
67 for (int32 i = 0; (uint32)i < chars; i++)
68 printf(" ");
69 }
70 int32 bytesLeftOnLine = MAX_CHARS_PER_LINE - chars;
71 int32 printChars = min(bytesLeftOnLine, charsLeft);
72 printf("%.*s\n", (int)printChars, str);
73 str += printChars;
74 charsLeft -= printChars;
75 // skip spaces
76 while (*str == ' ') {
77 str++;
78 charsLeft--;
79 }
80 }
81 }
82 }
83
84 // print_indented
85 void
print_indented(const char * indentStr,const char * str)86 print_indented(const char* indentStr, const char* str)
87 {
88 uint32 chars = strlen(indentStr);
89 printf(indentStr);
90 print_indented(str, chars, false);
91 }
92
93 // parse_arguments
94 void
parse_arguments(int argc,const char * const * argv,BList & files,TestOptions & options)95 parse_arguments(int argc, const char* const* argv, BList& files,
96 TestOptions& options)
97 {
98 // default options
99 options.listing = NO_LISTING;
100 options.write_test = false;
101 options.summary = false;
102 // parse arguments
103 for (int32 i = 1; i < argc; i++) {
104 const char* arg = argv[i];
105 int32 len = strlen(arg);
106 if (len == 0)
107 throw Exception(USAGE_ERROR, "Illegal argument: `'.");
108 if (arg[0] == '-') {
109 if (len < 2)
110 throw Exception(USAGE_ERROR, "Illegal argument: `-'.");
111 if (arg[1] == '-') {
112 const char* option = arg + 2;
113 // help
114 if (!strcmp(option, "help")) {
115 throw Exception(USAGE_HELP);
116 // list
117 } else if (!strcmp(option, "list")) {
118 if (options.listing == NO_LISTING)
119 options.listing = SHORT_LISTING;
120 // list-long
121 } else if (!strcmp(option, "list-long")) {
122 options.listing = LONG_LISTING;
123 // summary
124 } else if (!strcmp(option, "summary")) {
125 options.summary = true;
126 // write-test
127 } else if (!strcmp(option, "write-test")) {
128 options.write_test = true;
129 // error
130 } else {
131 throw Exception(USAGE_ERROR, BString("Illegal option: `")
132 << arg << "'.");
133 }
134 } else {
135 for (int32 i = 1; i < len; i++) {
136 char option = arg[i];
137 switch (option) {
138 // help
139 case 'h':
140 throw Exception(USAGE_HELP);
141 break;
142 // list
143 case 'l':
144 if (options.listing == NO_LISTING)
145 options.listing = SHORT_LISTING;
146 break;
147 // list long
148 case 'L':
149 options.listing = LONG_LISTING;
150 break;
151 // summary
152 case 's':
153 options.summary = true;
154 break;
155 // write test
156 case 'w':
157 options.write_test = true;
158 break;
159 // error
160 default:
161 throw Exception(USAGE_ERROR,
162 BString("Illegal option: `")
163 << arg << "'.");
164 break;
165 }
166 }
167 }
168 } else
169 files.AddItem(const_cast<char*>(arg));
170 }
171 }
172
173 // test_file
174 void
test_file(const char * filename,const TestOptions & options,TestResult & testResult)175 test_file(const char* filename, const TestOptions& options,
176 TestResult& testResult)
177 {
178 Warnings::SetCurrentWarnings(&testResult.warnings);
179 ResourceFile resFile;
180 try {
181 // check if the file exists
182 BEntry entry(filename, true);
183 status_t error = entry.InitCheck();
184 if (error != B_OK)
185 throw Exception(error);
186 if (!entry.Exists() || !entry.IsFile())
187 throw Exception("Entry doesn't exist or is no regular file.");
188 entry.Unset();
189 // open the file
190 BFile file(filename, B_READ_ONLY);
191 error = file.InitCheck();
192 if (error != B_OK)
193 throw Exception(error, "Failed to open file.");
194 // do the actual test
195 resFile.Init(file);
196 if (options.write_test)
197 resFile.WriteTest();
198 } catch (Exception exception) {
199 testResult.exception = new Exception(exception);
200 }
201 Warnings::SetCurrentWarnings(NULL);
202 // print warnings and error
203 if (options.listing != NO_LISTING
204 || testResult.warnings.CountWarnings() > 0 || testResult.exception) {
205 printf("\nFile `%s':\n", filename);
206 }
207 // warnings
208 if (testResult.warnings.CountWarnings() > 0) {
209 for (int32 i = 0;
210 const char* warning = testResult.warnings.WarningAt(i);
211 i++) {
212 print_indented(" Warning: ", warning);
213 }
214 }
215 // error
216 if (testResult.exception) {
217 status_t error = testResult.exception->GetError();
218 const char* description = testResult.exception->GetDescription();
219 if (strlen(description) > 0) {
220 print_indented(" Error: ", description);
221 if (error != B_OK)
222 print_indented(" ", strerror(error));
223 } else if (error != B_OK)
224 print_indented(" Error: ", strerror(error));
225 }
226 // list resources
227 if (resFile.InitCheck() == B_OK) {
228 switch (options.listing) {
229 case NO_LISTING:
230 break;
231 case SHORT_LISTING:
232 resFile.PrintToStream(false);
233 break;
234 case LONG_LISTING:
235 resFile.PrintToStream(true);
236 break;
237 }
238 }
239 }
240
241 // test_files
242 void
test_files(BList & files,TestOptions & options)243 test_files(BList& files, TestOptions& options)
244 {
245 BList testResults;
246 int32 successTestCount = 0;
247 int32 warningTestCount = 0;
248 int32 failedTestCount = 0;
249 for (int32 i = 0;
250 const char* filename = (const char*)files.ItemAt(i);
251 i++) {
252 TestResult* testResult = new TestResult(filename);
253 testResults.AddItem(testResult);
254 test_file(filename, options, *testResult);
255 if (testResult->exception)
256 failedTestCount++;
257 else if (testResult->warnings.CountWarnings() > 0)
258 warningTestCount++;
259 else
260 successTestCount++;
261 }
262 // print summary
263 if (options.summary) {
264 printf("\nSummary:\n");
265 printf( "=======\n");
266 // successful tests
267 if (successTestCount > 0) {
268 if (successTestCount == 1)
269 printf("one successful test\n");
270 else
271 printf("%ld successful tests\n", successTestCount);
272 }
273 // tests with warnings
274 if (warningTestCount > 0) {
275 if (warningTestCount == 1)
276 printf("one test with warnings:\n");
277 else
278 printf("%ld tests with warnings:\n", warningTestCount);
279 for (int32 i = 0;
280 TestResult* testResult = (TestResult*)testResults.ItemAt(i);
281 i++) {
282 if (!testResult->exception
283 && testResult->warnings.CountWarnings() > 0) {
284 printf(" `%s'\n", testResult->filename.String());
285 }
286 }
287 }
288 // failed tests
289 if (failedTestCount > 0) {
290 if (failedTestCount == 1)
291 printf("one test failed:\n");
292 else
293 printf("%ld tests failed:\n", failedTestCount);
294 for (int32 i = 0;
295 TestResult* testResult = (TestResult*)testResults.ItemAt(i);
296 i++) {
297 if (testResult->exception)
298 printf(" `%s'\n", testResult->filename.String());
299 }
300 }
301 }
302 // cleanup
303 for (int32 i = 0;
304 TestResult* testResult = (TestResult*)testResults.ItemAt(i);
305 i++) {
306 delete testResult;
307 }
308 }
309
310 // main
311 int
main(int argc,const char * const * argv)312 main(int argc, const char* const* argv)
313 {
314 int returnValue = 0;
315 const char* cmdName = argv[0];
316 TestOptions options;
317 BList files;
318 try {
319 // parse arguments
320 parse_arguments(argc, argv, files, options);
321 if (files.CountItems() == 0)
322 throw Exception(USAGE_ERROR, "No files given.");
323 // test the files
324 test_files(files, options);
325 } catch (Exception exception) {
326 status_t error = exception.GetError();
327 const char* description = exception.GetDescription();
328 switch (error) {
329 case B_OK:
330 if (strlen(description) > 0)
331 fprintf(stderr, "%s\n", description);
332 returnValue = 1;
333 break;
334 case USAGE_ERROR:
335 if (strlen(description) > 0)
336 fprintf(stderr, "%s\n", description);
337 fprintf(stderr, kUsage, cmdName);
338 returnValue = 1;
339 break;
340 case USAGE_HELP:
341 printf(kUsage, cmdName);
342 break;
343 default:
344 fprintf(stderr, " error: %s\n", strerror(error));
345 returnValue = 1;
346 break;
347 }
348 }
349 return returnValue;
350 }
351
352