1/*
2 * Copyright (c) 2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20//
21//  auto_tester.m
22//  Copyright (c) 2008-2011 Apple Inc. All rights reserved.
23//
24
25#import <Foundation/Foundation.h>
26#import <objc/objc-auto.h>
27#import "TestCase.h"
28#import "auto_tester.h"
29
30static BOOL logTestOutput = NO;
31
32void testLoop(NSMutableArray *testCases);
33
34void cleanup(NSMutableArray *testCases)
35{
36    void (^completionCallback)(void) = ^{ testLoop(testCases); };
37    dispatch_queue_t current = dispatch_get_current_queue();
38    auto_zone_collect_and_notify(objc_collectableZone(), AUTO_ZONE_COLLECT_EXHAUSTIVE_COLLECTION|AUTO_ZONE_COLLECT_LOCAL_COLLECTION, current, completionCallback);
39}
40
41void testLoop(NSMutableArray *testCases)
42{
43    __block BOOL allTestsComplete = YES;
44    __block int successCount = 0;
45    __block int failCount = 0;
46    __block int skipCount = 0;
47    dispatch_queue_t current = dispatch_get_current_queue();
48    void (^completionCallback)(void) = Block_copy(^{ cleanup(testCases); });
49
50    [testCases enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))^(id obj, NSUInteger idx, BOOL *stop){
51        TestCase *t = (TestCase *)obj;
52        switch ([t result]) {
53            case PENDING:
54            {
55                NSString *skipMessage = [t shouldSkip];
56                if (skipMessage) {
57                    skipCount++;
58                    [t setTestResult:SKIPPED message:skipMessage];
59                } else {
60                    *stop = YES;
61                    allTestsComplete = NO;
62                    [t setCompletionCallback:completionCallback];
63                    [t startTest];
64                }
65            }
66                break;
67            case PASSED:
68                successCount++;
69                break;
70            case FAILED:
71                failCount++;
72                break;
73            case SKIPPED:
74                skipCount++;
75                break;
76            default:
77                NSLog(@"unexpected test status in testLoop(): %@ = %d\n", [t className], [t result]);
78                exit(-1);
79                break;
80        }
81    }];
82    Block_release(completionCallback);
83
84    // report results
85    if (allTestsComplete) {
86        [testCases enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))^(id obj, NSUInteger idx, BOOL *stop){
87            TestCase *t = (TestCase *)obj;
88            NSLog(@"%@: %@", [t className], [t resultString]);
89            NSString *output = [t testOutput];
90            if (logTestOutput && output)
91                NSLog(@"Test output:\n%@", output);
92        }];
93        NSLog(@"%d test cases: %d passed, %d failed, %d skipped", [testCases count], successCount, failCount, skipCount);
94        exit(failCount);
95    }
96}
97
98int main(int argc, char *argv[])
99{
100    int repeat = 1;
101
102    NSMutableArray *testCases = [NSMutableArray array];
103    NSMutableArray *testClasses = [NSMutableArray array];
104    NSProcessInfo *pi = [NSProcessInfo processInfo];
105    NSMutableArray *args = [[pi arguments] mutableCopy];
106    [args removeObjectAtIndex:0];
107    while ([args count] > 0) {
108        int argCount = 0;
109        NSString *arg = [args objectAtIndex:0];
110        if ([arg isEqual:@"-logTestOutput"]) {
111            argCount++;
112            logTestOutput = YES;
113        }
114        if ([arg hasPrefix:@"-repeat="]) {
115            argCount++;
116            repeat = [[arg substringFromIndex:[@"-repeat=" length]] intValue];
117            if (repeat < 1)
118                repeat = 1;
119        }
120        Class c = NSClassFromString(arg);
121        if (c) {
122            [testClasses addObject:c];
123            argCount++;
124        }
125        [args removeObjectsInRange:NSMakeRange(0, argCount)];
126    }
127
128    // Simple test driver that just runs all the tests.
129    if ([testClasses count] == 0)
130        [testClasses addObjectsFromArray:[TestCase testClasses]];
131    [testClasses enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))^(id obj, NSUInteger idx, BOOL *stop){
132        for (int i=0; i<repeat; i++)
133            [testCases addObject:[[obj alloc] init]];
134    }];
135
136    dispatch_queue_t testQ = dispatch_queue_create("Test Runner", NULL);
137    dispatch_async(testQ, ^{testLoop(testCases);});
138
139    [[NSRunLoop mainRunLoop] run];
140    dispatch_main();
141    return 0;
142}
143