]> git.wincent.com - WOTest.git/blob - WOTestRunner/WOTestRunner.m
Code clean-up for garbage collection
[WOTest.git] / WOTestRunner / WOTestRunner.m
1 //
2 //  WOTestRunner.m
3 //  WOTest
4 //
5 //  Created by Wincent Colaiuta on 12 March 2005.
6 //
7 //  Copyright 2004-2007 Wincent Colaiuta.
8 //  This program is free software: you can redistribute it and/or modify
9 //  it under the terms of the GNU General Public License as published by
10 //  the Free Software Foundation, either version 3 of the License, or
11 //  (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 //
21
22 // main header
23 #import "WOTestRunner.h"
24
25 // system headers
26 #import <Foundation/Foundation.h>
27 #import <objc/objc-runtime.h>
28 #import <getopt.h>
29
30 // framework headers
31 #import "WOTest.h"
32
33 // make what(1) produce meaningful output
34 #import "WOTestRunner_Version.h"
35
36 #pragma mark -
37 #pragma mark Implementation
38
39 int main(int argc, const char *argv[])
40 {
41     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
42     int exitCode = EXIT_SUCCESS;
43
44     // an example of using the framework without linking to it
45     WO_TEST_LOAD_FRAMEWORK;
46     if (!WO_TEST_FRAMEWORK_IS_LOADED)
47     {
48         exitCode = EXIT_FAILURE;
49         fprintf(stderr, "error: unable to load WOTest framework\n");
50         goto cleanup;
51     }
52
53     // parse commandline arguments
54     int verbose = 0;
55     NSMutableArray *testClasses     = [NSMutableArray array];
56     NSMutableArray *excludeClasses  = [NSMutableArray array];
57     NSMutableArray *testBundles     = [NSMutableArray array];
58     NSMutableArray *excludeBundles  = [NSMutableArray array];
59
60     extern char *optarg;
61     extern int  optind;
62     int         ch;
63     static struct option longopts[] = {
64         { "help",           no_argument,        NULL,   'h' },
65         { "verbose",        no_argument,        NULL,   'v' },
66         { "version",        no_argument,        NULL,   'V' },
67         { "test-class",     required_argument,  NULL,   't' },
68         { "exclude-class",  required_argument,  NULL,   'e' },
69         { "test-bundle",    required_argument,  NULL,   'b' },
70         { "exclude-bundle", required_argument,  NULL,   'x' }
71     };
72     while ((ch = getopt_long(argc, (char * const *)argv, "hvVt:e:b:", longopts, NULL)) != -1)
73     {
74         switch (ch)
75         {
76             case 'h':   // show help
77                 showUsage(argv[0]);
78                 goto cleanup;
79                 break;
80             case 'v':   // be verbose
81                 verbose++;
82                 break;
83             case 'V':   // show version information
84                 showVersion();
85                 goto cleanup;
86                 break;
87             case 't': // test this class
88                 [testClasses addObject:[NSString stringWithUTF8String:optarg]];
89                 break;
90             case 'e': // exclude this class
91                 [excludeClasses addObject:[NSString stringWithUTF8String:optarg]];
92                 break;
93             case 'b': // test this bundle (loading into memory if necessary)
94                 [testBundles addObject:[NSString stringWithUTF8String:optarg]];
95                 break;
96             case 'x': // exclude this bundle
97                 [excludeBundles addObject:[NSString stringWithUTF8String:optarg]];
98                 break;
99             default:
100                 showUsage(argv[0]);
101                 exitCode = EXIT_FAILURE;
102                 goto cleanup;
103         }
104     }
105
106     // TODO: automatically modify DYLD_FRAMEWORK_PATH based on passed-in bundles, restore to previous setting on exit
107     // basic algorithm:
108     // - save DYLD_FRAMEWORK_PATH
109     // - for each absolute bundle path, is bundle path already in DYLD_FRAMEWORK_PATH? if not add it
110     // - at end, restore DYLD_FRAMEWORK_PATH
111     // more sophisticated algorithm:
112     // - inspect mach-o headers to see which libraries it wants to load; if they're non-standard locations, add them
113
114     /*
115
116      Example error when trying to run WOCommon tests without first setting DYLD_FRAMEWORK_PATH
117
118      2006-10-23 01:19:51.727 WOTestRunner[892] *** -[NSBundle load]: Error loading code /Users/wincent/trabajo/build/Debug/WOCommon.bundle/Contents/MacOS/WOCommon for bundle /Users/wincent/trabajo/build/Debug/WOCommon.bundle, error code 4 (link edit error code 4, error number 0 (Library not loaded: @executable_path/../Frameworks/WOTest.framework/Versions/A/WOTest
119      warning: could not load bundle /Users/wincent/trabajo/build/Debug/WOCommon.bundle
120
121     of course, may be more complicated than all that. would be nice to handle case where WOTestRunner, WOTest.framework and the bundle being tested were all in completely unrelated locations
122
123     */
124
125     if ([testBundles count] > 0) // test only these bundles
126     {
127         for (NSString *bundlePath in testBundles)
128         {
129             bundlePath = [bundlePath WOTest_stringByConvertingToAbsolutePath];
130             NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
131             if (bundle && [bundle load])
132             {
133                 if ([testClasses count] > 0) // test only these classes
134                 {
135                     for (NSString *class in testClasses)
136                         [WO_TEST_SHARED_INSTANCE runTestsForClassName:class];
137                 }
138                 else // test all classes
139                 {
140                     NSArray *classes = [WO_TEST_SHARED_INSTANCE testableClassesFrom:bundle];
141                     for (NSString *class in classes)
142                     {
143                         if ([excludeClasses containsObject:class]) continue;
144                         [WO_TEST_SHARED_INSTANCE runTestsForClassName:class];
145                     }
146                 }
147             }
148             else
149                 fprintf(stderr, "warning: could not load bundle %s\n", [bundlePath UTF8String]);
150         }
151     }
152     else // test all bundles
153     {
154         if ([testClasses count] > 0) // test only these classes
155         {
156             for (NSString *class in testClasses)
157                 [WO_TEST_SHARED_INSTANCE runTestsForClassName:class];
158         }
159         else // test all classes
160         {
161             for (NSString *class in [WO_TEST_SHARED_INSTANCE testableClasses])
162             {
163                 if ([excludeClasses containsObject:class]) continue;
164                 [WO_TEST_SHARED_INSTANCE runTestsForClassName:class];
165             }
166         }
167     }
168
169     [WO_TEST_SHARED_INSTANCE printTestResultsSummary];
170     if (![WO_TEST_SHARED_INSTANCE testsWereSuccessful])
171         exitCode = EXIT_FAILURE;
172
173 cleanup:
174     [pool drain];
175     return exitCode;
176 }
177
178 void showUsage(const char *name)
179 {
180     fprintf
181     (stdout,
182      //------------------------------- 80 columns ----------------------------------->|
183      "WOTestRunner, part of the WOTest framework <http://test.wincent.com/>.\n"
184      "\n"
185      "%s.\n"
186      "This program is free software: you can redistribute it and/or modify\n"
187      "it under the terms of the GNU General Public License as published by\n"
188      "the Free Software Foundation, either version 3 of the License, or\n"
189      "(at your option) any later version.\n"
190      "\n"
191      "This program is distributed in the hope that it will be useful,\n"
192      "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
193      "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
194      "GNU General Public License for more details.\n"
195      "\n"
196      "You should have received a copy of the GNU General Public License\n"
197      "along with this program.  If not, see <http://www.gnu.org/licenses/>.\n"
198      "\n"
199      "Usage: %s [option]...\n"
200      "-t, --test-class=CLASS         test only CLASS\n"
201      "-e, --exclude-class=CLASS      test all but CLASS\n"
202      "-b, --test-bundle=BUNDLE       test only BUNDLE, loading if necessary\n"
203      "-x, --exclude-bundle=BUNDLE    test all but BUNDLE\n"
204      "-v, --verbose                  verbose output (repeat for more verbosity)\n"
205      "-V, --version                  show version information\n"
206      "-h, --help                     show this usage information\n",
207      WO_RCSID_STRING(copyright), name);
208 }
209
210 void showVersion(void)
211 {
212     fprintf
213     (stdout,
214      //------------------------------- 80 columns ----------------------------------->|
215      "WOTestRunner, part of the WOTest framework <http://test.wincent.com/>.\n"
216      "\n"
217      "%s.\n"
218      "This program is free software: you can redistribute it and/or modify\n"
219      "it under the terms of the GNU General Public License as published by\n"
220      "the Free Software Foundation, either version 3 of the License, or\n"
221      "(at your option) any later version.\n"
222      "\n"
223      "This program is distributed in the hope that it will be useful,\n"
224      "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
225      "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
226      "GNU General Public License for more details.\n"
227      "\n"
228      "You should have received a copy of the GNU General Public License\n"
229      "along with this program.  If not, see <http://www.gnu.org/licenses/>.\n"
230      "\n"
231      "%s\n", WO_RCSID_STRING(copyright), WO_RCSID_STRING(version));
232 }