]> git.saurik.com Git - apple/objc4.git/blobdiff - test/initialize.m
objc4-756.2.tar.gz
[apple/objc4.git] / test / initialize.m
diff --git a/test/initialize.m b/test/initialize.m
new file mode 100644 (file)
index 0000000..d32b4df
--- /dev/null
@@ -0,0 +1,323 @@
+// TEST_CONFIG
+
+// initialize.m
+// Test basic +initialize behavior
+// * +initialize before class method
+// * superclass +initialize before subclass +initialize
+// * subclass inheritance of superclass implementation
+// * messaging during +initialize
+// * +initialize provoked by class_getMethodImplementation
+// * +initialize not provoked by objc_getClass
+#include "test.h"
+#include "testroot.i"
+
+int state = 0;
+
+@interface Super0 : TestRoot @end
+@implementation Super0
++(void)initialize {
+    fail("objc_getClass() must not trigger +initialize");
+}
+@end
+
+@interface Super : TestRoot @end
+@implementation Super 
++(void)initialize {
+    testprintf("in [Super initialize]\n");
+    testassert(state == 0);
+    state = 1;
+}
++(void)method { 
+    fail("[Super method] shouldn't be called");
+}
+@end
+
+@interface Sub : Super @end
+@implementation Sub
++(void)initialize { 
+    testprintf("in [Sub initialize]\n");
+    testassert(state == 1);
+    state = 2;
+}
++(void)method { 
+    testprintf("in [Sub method]\n");
+    testassert(state == 2);
+    state = 3;
+}
+@end
+
+
+@interface Super2 : TestRoot @end
+@interface Sub2 : Super2 @end
+
+@implementation Super2
++(void)initialize { 
+    if (self == objc_getClass("Sub2")) {
+        testprintf("in [Super2 initialize] of Sub2\n");
+        testassert(state == 1);
+        state = 2;
+    } else if (self == objc_getClass("Super2")) {
+        testprintf("in [Super2 initialize] of Super2\n");
+        testassert(state == 0);
+        state = 1;
+    } else {
+        fail("in [Super2 initialize] of unknown class");
+    }
+}
++(void)method { 
+    testprintf("in [Super2 method]\n");
+    testassert(state == 2);
+    state = 3;
+}
+@end
+
+@implementation Sub2
+// nothing here
+@end
+
+
+@interface Super3 : TestRoot @end
+@interface Sub3 : Super3 @end
+
+@implementation Super3
++(void)initialize { 
+    if (self == [Sub3 class]) {  // this message triggers [Sub3 initialize]
+        testprintf("in [Super3 initialize] of Sub3\n");
+        testassert(state == 0);
+        state = 1;
+    } else if (self == [Super3 class]) {
+        testprintf("in [Super3 initialize] of Super3\n");
+        testassert(state == 1);
+        state = 2;
+    } else {
+        fail("in [Super3 initialize] of unknown class");
+    }
+}
++(void)method { 
+    testprintf("in [Super3 method]\n");
+    testassert(state == 2);
+    state = 3;
+}
+@end
+
+@implementation Sub3
+// nothing here
+@end
+
+
+@interface Super4 : TestRoot @end
+@implementation Super4
+-(void)instanceMethod { 
+    testassert(state == 1);
+    state = 2;
+}
++(void)initialize {
+    testprintf("in [Super4 initialize]\n");
+    testassert(state == 0);
+    state = 1;
+    id x = [[self alloc] init];
+    [x instanceMethod];
+    RELEASE_VALUE(x);
+}
+@end
+
+
+@interface Super5 : TestRoot @end
+@implementation Super5
+-(void)instanceMethod { 
+}
++(void)classMethod {
+    testassert(state == 1);
+    state = 2;
+}
++(void)initialize {
+    testprintf("in [Super5 initialize]\n");
+    testassert(state == 0);
+    state = 1;
+    class_getMethodImplementation(self, @selector(instanceMethod));
+    // this is the "memoized" case for getNonMetaClass
+    class_getMethodImplementation(object_getClass(self), @selector(classMethod));
+    [self classMethod];
+}
+@end
+
+
+@interface Super6 : TestRoot @end
+@interface Sub6 : Super6 @end
+@implementation Super6
++(void)initialize {
+    static bool once;
+    bool wasOnce;
+    testprintf("in [Super6 initialize] (#%d)\n", 1+(int)once);
+    if (!once) {
+        once = true;
+        wasOnce = true;
+        testassert(state == 0);
+        state = 1;
+    } else {
+        wasOnce = false;
+        testassert(state == 2);
+        state = 3;
+    }
+    [Sub6 class];
+    if (wasOnce) {
+        testassert(state == 5);
+        state = 6;
+    } else {
+        testassert(state == 3);
+        state = 4;
+    }
+}
+@end
+@implementation Sub6
++(void)initialize {
+    testprintf("in [Sub6 initialize]\n");
+    testassert(state == 1);
+    state = 2;
+    [super initialize];
+    testassert(state == 4);
+    state = 5;
+}
+@end
+
+
+@interface Super7 : TestRoot @end
+@interface Sub7 : Super7 @end
+@implementation Super7
++(void)initialize {
+    static bool once;
+    bool wasOnce;
+    testprintf("in [Super7 initialize] (#%d)\n", 1+(int)once);
+    if (!once) {
+        once = true;
+        wasOnce = true;
+        testassert(state == 0);
+        state = 1;
+    } else {
+        wasOnce = false;
+        testassert(state == 2);
+        state = 3;
+    }
+    [Sub7 class];
+    if (wasOnce) {
+        testassert(state == 5);
+        state = 6;
+    } else {
+        testassert(state == 3);
+        state = 4;
+    }
+}
+@end
+@implementation Sub7
++(void)initialize {
+    testprintf("in [Sub7 initialize]\n");
+    testassert(state == 1);
+    state = 2;
+    [super initialize];
+    testassert(state == 4);
+    state = 5;
+}
+@end
+
+
+
+@interface SuperThrower : TestRoot @end
+@implementation SuperThrower
++(void)initialize {
+    testprintf("in [SuperThrower initialize]\n");
+    testassert(state == 0);
+    state = 10;
+    @throw AUTORELEASE([TestRoot new]);
+    fail("@throw didn't throw");
+}
+@end
+
+@interface SubThrower : SuperThrower @end
+@implementation SubThrower
++(void)initialize {
+    testprintf("in [SubThrower initialize]\n");
+    testassert(state == 0);
+    state = 20;
+}
+@end
+
+int main()
+{
+    Class cls;
+
+    // objc_getClass() must not +initialize anything
+    state = 0;
+    objc_getClass("Super0");
+    testassert(state == 0);
+
+    // initialize superclass, then subclass
+    state = 0;
+    [Sub method];
+    testassert(state == 3);
+
+    // check subclass's inheritance of superclass initialize
+    state = 0;
+    [Sub2 method];
+    testassert(state == 3);
+
+    // check subclass method called from superclass initialize
+    state = 0;
+    [Sub3 method];
+    testassert(state == 3);
+
+    // check class_getMethodImplementation (instance method)
+    state = 0;
+    cls = objc_getClass("Super4");
+    testassert(state == 0);
+    class_getMethodImplementation(cls, @selector(classMethod));
+    testassert(state == 2);
+
+    // check class_getMethodImplementation (class method)
+    // this is the "slow" case for getNonMetaClass
+    state = 0;
+    cls = objc_getClass("Super5");
+    testassert(state == 0);
+    class_getMethodImplementation(object_getClass(cls), @selector(instanceMethod));
+    testassert(state == 2);
+
+    // check +initialize cycles
+    // this is the "cls is a subclass" case for getNonMetaClass
+    state = 0;
+    [Super6 class];
+    testassert(state == 6);
+
+    // check +initialize cycles
+    // this is the "cls is a subclass" case for getNonMetaClass
+    state = 0;
+    [Sub7 class];
+    testassert(state == 6);
+
+    // exception from +initialize must be handled cleanly
+    PUSH_POOL {
+        alarm(3);
+        testonthread( ^{
+            @try {
+                state = 0;
+                [SuperThrower class];
+                fail("where's the beef^Wexception?");
+            } @catch (...) {
+                testassert(state == 10);
+                state = 11;
+            }
+            testassert(state == 11);
+        });
+        @try {
+            state = 0;
+            [SuperThrower class];
+            testassert(state == 0);
+            [SubThrower class];
+            testassert(state == 20);
+        } @catch (...) {
+            fail("+initialize called again after exception");
+        }
+    } POP_POOL;
+    
+    succeed(__FILE__);
+
+    return 0;
+}