aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2009-09-16 17:04:27 +0000
committerGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2009-09-16 17:04:27 +0000
commit080b28284751ae178608f45268765f6e0ae94287 (patch)
tree615c076d5a79621921eac431f5857f6e2d73c8c0
parent9ebf8b15cfb105864e97a0e8b005de8e6e5d5c42 (diff)
[Author: dmaclach]
Add support for more complex unit test class hierarchies than what is available out of the box in SenTestCase. I currently have the release notes checked out as part of a separate change, and will update them accordingly for this as well. R=thomasvl,mrossetti DELTA=80 (79 added, 0 deleted, 1 changed)
-rw-r--r--Foundation/GTMObjC2Runtime.h1
-rw-r--r--Foundation/GTMObjC2Runtime.m4
-rw-r--r--Foundation/GTMObjC2RuntimeTest.m9
-rw-r--r--UnitTesting/GTMSenTestCase.h23
-rw-r--r--UnitTesting/GTMSenTestCase.m31
-rw-r--r--UnitTesting/GTMUnitTestingTest.m13
6 files changed, 80 insertions, 1 deletions
diff --git a/Foundation/GTMObjC2Runtime.h b/Foundation/GTMObjC2Runtime.h
index 4f569a5..9f5afa0 100644
--- a/Foundation/GTMObjC2Runtime.h
+++ b/Foundation/GTMObjC2Runtime.h
@@ -55,6 +55,7 @@
OBJC_EXPORT Class object_getClass(id obj);
OBJC_EXPORT const char *class_getName(Class cls);
OBJC_EXPORT BOOL class_conformsToProtocol(Class cls, Protocol *protocol);
+OBJC_EXPORT BOOL class_respondsToSelector(Class cls, SEL sel);
OBJC_EXPORT Class class_getSuperclass(Class cls);
OBJC_EXPORT Method *class_copyMethodList(Class cls, unsigned int *outCount);
OBJC_EXPORT SEL method_getName(Method m);
diff --git a/Foundation/GTMObjC2Runtime.m b/Foundation/GTMObjC2Runtime.m
index 9835654..2eb5dc3 100644
--- a/Foundation/GTMObjC2Runtime.m
+++ b/Foundation/GTMObjC2Runtime.m
@@ -58,6 +58,10 @@ Class class_getSuperclass(Class cls) {
return cls->super_class;
}
+BOOL class_respondsToSelector(Class cls, SEL sel) {
+ return class_getInstanceMethod(cls, sel) != nil;
+}
+
Method *class_copyMethodList(Class cls, unsigned int *outCount) {
if (!cls) return NULL;
diff --git a/Foundation/GTMObjC2RuntimeTest.m b/Foundation/GTMObjC2RuntimeTest.m
index 7344a0f..7a88f23 100644
--- a/Foundation/GTMObjC2RuntimeTest.m
+++ b/Foundation/GTMObjC2RuntimeTest.m
@@ -160,6 +160,15 @@ AT_REQUIRED
nil);
}
+- (void)test_class_respondsToSelector {
+ // Nil Checks
+ STAssertFalse(class_respondsToSelector(cls_, @selector(setUp)), nil);
+ STAssertFalse(class_respondsToSelector(cls_, nil), nil);
+
+ // Standard use check
+ STAssertTrue(class_respondsToSelector(cls_, @selector(kwyjibo)), nil);
+}
+
- (void)test_class_getSuperclass {
// Nil Checks
STAssertNil(class_getSuperclass(nil), nil);
diff --git a/UnitTesting/GTMSenTestCase.h b/UnitTesting/GTMSenTestCase.h
index 9837b2b..58c0594 100644
--- a/UnitTesting/GTMSenTestCase.h
+++ b/UnitTesting/GTMSenTestCase.h
@@ -1008,4 +1008,27 @@ GTM_EXTERN NSString *const SenTestLineNumberKey;
// to set up our logging system correctly to verify logging calls.
// See GTMUnitTestDevLog.h for details
@interface GTMTestCase : SenTestCase
+
+// Returns YES if this is an abstract testCase class as opposed to a concrete
+// testCase class that you want tests run against. SenTestCase is not designed
+// out of the box to handle an abstract class hierarchy descending from it with
+// some concrete subclasses. In some cases we want all the "concrete"
+// subclasses of an abstract subclass of SenTestCase to run a test, but we don't
+// want that test to be run against an instance of an abstract subclass itself.
+// By returning "YES" here, the tests defined by this class won't be run against
+// an instance of this class. As an example class hierarchy:
+//
+// FooExtensionTestCase
+// GTMTestCase <- ExtensionTestCase <
+// BarExtensionTestCase
+//
+// So FooExtensionTestCase and BarExtensionTestCase inherit from
+// ExtensionTestCase (and probably FooExtension and BarExtension inherit from a
+// class named Extension). We want the tests in ExtensionTestCase to be run as
+// part of FooExtensionTestCase and BarExtensionTestCase, but we don't want them
+// run against ExtensionTestCase. The default implementation of
+// isAbstractTestCase returns NO if self (being a class) has no subclasses and
+// YES otherwise.
++ (BOOL)isAbstractTestCase;
+
@end
diff --git a/UnitTesting/GTMSenTestCase.m b/UnitTesting/GTMSenTestCase.m
index 99b9db0..ec5ef62 100644
--- a/UnitTesting/GTMSenTestCase.m
+++ b/UnitTesting/GTMSenTestCase.m
@@ -18,6 +18,7 @@
#import "GTMSenTestCase.h"
#import <unistd.h>
+#import "GTMObjC2Runtime.h"
#if !GTM_IPHONE_SDK
#import "GTMGarbageCollection.h"
@@ -305,6 +306,36 @@ NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
[devLogClass performSelector:@selector(disableTracking)];
}
}
+
++ (BOOL)isAbstractTestCase {
+ int numClasses = objc_getClassList(NULL, 0);
+ BOOL isAbstract = NO;
+ if (numClasses > 0) {
+ Class *classes = malloc(sizeof(Class) * numClasses);
+ numClasses = objc_getClassList(classes, numClasses);
+ for (int i = 0; i < numClasses && !isAbstract; ++i) {
+ Class cls = classes[i];
+ if (class_respondsToSelector(cls, @selector(superclass))) {
+ Class superClass = [cls superclass];
+ if ([self isEqualTo:superClass]) {
+ isAbstract = YES;
+ }
+ }
+ }
+ free(classes);
+ }
+ return isAbstract;
+}
+
+
++ (NSArray *)testInvocations {
+ NSArray *invocations = nil;
+ if (![self isAbstractTestCase]) {
+ invocations = [super testInvocations];
+ }
+ return invocations;
+}
+
@end
// Leak detection
diff --git a/UnitTesting/GTMUnitTestingTest.m b/UnitTesting/GTMUnitTestingTest.m
index 72e6880..cd00b3e 100644
--- a/UnitTesting/GTMUnitTestingTest.m
+++ b/UnitTesting/GTMUnitTestingTest.m
@@ -23,7 +23,10 @@
NSString *const kGTMWindowNibName = @"GTMUnitTestingTest";
NSString *const kGTMWindowSaveFileName = @"GTMUnitTestingWindow";
-@interface GTMUnitTestingTest : GTMTestCase {
+@interface GTMAbstractUnitTestingTest : GTMTestCase
+@end
+
+@interface GTMUnitTestingTest : GTMAbstractUnitTestingTest {
int expectedFailureCount_;
}
@end
@@ -44,6 +47,14 @@ NSString *const kGTMWindowSaveFileName = @"GTMUnitTestingWindow";
@interface GTMUnitTestingProxyTest : NSProxy
@end
+@implementation GTMAbstractUnitTestingTest
+- (void)testAbstractUnitTest {
+ static int testCount = 0;
+ testCount += 1;
+ STAssertEquals(testCount, 1, @"testAbstractUnitTest should only fire once");
+}
+@end
+
@implementation GTMUnitTestingTest
// Brings up the window defined in the nib and takes a snapshot of it.