BPicture: Fix archive constructor.
[haiku.git] / src / tools / restest / restest.cpp
blob32c92bb999659efa559fb05bd24926c33977d557
1 // restest.cpp
3 #include <algobase.h>
4 #include <stdio.h>
5 #include <string.h>
7 #include <Entry.h>
8 #include <File.h>
9 #include <String.h>
11 #include "Exception.h"
12 #include "OffsetFile.h"
13 #include "ResourceFile.h"
14 #include "Warnings.h"
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"
27 const status_t USAGE_ERROR = B_ERRORS_END + 1;
28 const status_t USAGE_HELP = B_ERRORS_END + 2;
30 enum listing_level {
31 NO_LISTING,
32 SHORT_LISTING,
33 LONG_LISTING,
36 struct TestOptions {
37 listing_level listing;
38 bool write_test;
39 bool summary;
42 struct TestResult {
43 TestResult(const char* filename)
44 : filename(filename), warnings(), exception(NULL)
48 ~TestResult()
50 delete exception;
53 BString filename;
54 Warnings warnings;
55 Exception* exception;
58 // print_indented
59 void
60 print_indented(const char* str, uint32 chars, bool indentFirst = true)
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(" ");
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--;
84 // print_indented
85 void
86 print_indented(const char* indentStr, const char* str)
88 uint32 chars = strlen(indentStr);
89 printf(indentStr);
90 print_indented(str, chars, false);
93 // parse_arguments
94 void
95 parse_arguments(int argc, const char* const* argv, BList& files,
96 TestOptions& options)
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 << "'.");
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;
168 } else
169 files.AddItem(const_cast<char*>(arg));
173 // test_file
174 void
175 test_file(const char* filename, const TestOptions& options,
176 TestResult& testResult)
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);
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);
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);
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));
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;
241 // test_files
242 void
243 test_files(BList& files, TestOptions& options)
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++;
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);
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());
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());
302 // cleanup
303 for (int32 i = 0;
304 TestResult* testResult = (TestResult*)testResults.ItemAt(i);
305 i++) {
306 delete testResult;
310 // main
312 main(int argc, const char* const* argv)
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;
349 return returnValue;