// initialize.m
// Test basic +initialize behavior
// * +initialize before class method
// * superclass +initialize before subclass +initialize
// * subclass inheritance of superclass implementation
// * messaging during +initialize
#include "test.h"

int state = 0;

@interface Super0 { } @end
@implementation Super0
+(void)initialize {
    fail("objc_getClass() must not trigger +initialize");
}
@end

@interface Super {} @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 { } @end
@interface Sub2 : Super2 { } @end

@implementation Super2
+(id)class { return self; }
+(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 { } @end
@interface Sub3 : Super3 { } @end

@implementation Super3
+(id)class { return self; }
+(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

int main()
{
    // 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);

    succeed(__FILE__);

    return 0;
}
