/* ObjectTesting - Include basic object tests for the GNUstep Testsuite Copyright (C) 2005 Free Software Foundation, Inc. Written by: Matt Rice? This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #import #import #import #import #import #import /* WARNING ... * * You should look in Testing.h for the standard Test Framework API * and its documentation (along with the README file). * * You should look in the example tests in the TestFramework subdirectory * of the gnustep-make package to see how to use the API. * * This file contains old code from the earlier days of the testsuite * and while a lot of it is useful stuff, it's not an example of how * best to use the current API. */ /* Quick test to check that we have the class we expect. */ #define TEST_FOR_CLASS(aClassName, aClass, TestDescription) \ pass([aClass isKindOfClass: NSClassFromString(aClassName)], TestDescription) /* Quick test to check for a non-empty string in the case where we don't * actually know what value we should be expecting. */ #define TEST_STRING(code, description) \ { \ NSString *_testString = code; \ pass(_testString != nil \ && [_testString isKindOfClass: [NSString class]] \ && [_testString length], description); \ } /* DEPRECATED * This is for backward compatibility, the original havng been replaced * by two clearer/simpler macros. * Please use PASS_EXCEPTION() for a test which is supposed to raise, * or PASS_RUNS() to test that code runs without raising an exception. */ #define TEST_EXCEPTION(code, exceptionName, shouldRaise, description) \ if (shouldRaise) { PASS_EXCEPTION(code, exceptionName, description) } \ else { PASS_RUNS(code, description) } /* DEPRECATED START_TEST/END_TEST can be used if the code being tested could raise and the exception should be considered a test failure. The exception is not reraised to allow subsequent tests to execute. The START_TEST macro takes an argument which will skip the test as Skipped if it evaluates to 0, allowing runtime control of whether the code block should be executed. */ #define START_TEST(supported) if ((supported)) { NS_DURING #define END_TEST(result, desc, args...) \ pass(result, desc, ## args); \ NS_HANDLER \ fprintf(stderr, "EXCEPTION: %s %s %s\n", \ [[localException name] UTF8String], \ [[localException reason] UTF8String], \ [[[localException userInfo] description] UTF8String]); \ pass (NO, desc, ## args); NS_ENDHANDLER } \ else unsupported (desc, ## args) /* Functions to test basic capabilities expected of most classes. */ static void test_NSMutableCopying(NSString *iClassName, NSString *mClassName, NSArray *objects) __attribute__ ((unused)); static void test_alloc(NSString *className) __attribute__ ((unused)); static void test_alloc_only(NSString *className) __attribute__ ((unused)); static void test_NSObject(NSString *className, NSArray *objects) __attribute__ ((unused)); static void test_NSCopying(NSString *iClassName, NSString *mClassName, NSArray *objects, BOOL mustRetain, BOOL mustCopy) __attribute__ ((unused)); static void test_NSCoding(NSArray *objects) __attribute__ ((unused)); static void test_keyed_NSCoding(NSArray *objects) __attribute__ ((unused)); /* perform basic allocation tests */ static void test_alloc(NSString *className) { Class theClass = NSClassFromString(className); id obj0 = nil; id obj1 = nil; const char *prefix = [[NSString stringWithFormat: @"Class '%@'", className] UTF8String]; NSZone *testZone = NSCreateZone(1024, 1024, 1); pass(theClass != Nil, "%s exists", prefix); obj0 = [theClass alloc]; pass(obj0 != nil, "%s has working alloc", prefix); pass([obj0 isKindOfClass: theClass], "%s alloc gives the correct class", prefix); obj0 = [[theClass alloc] init]; pass([obj0 isKindOfClass: theClass], "%s has working init", prefix); obj0 = [theClass new]; pass([obj0 isKindOfClass: theClass], "%s has working new", prefix); obj1 = [theClass allocWithZone: testZone]; pass([obj1 isKindOfClass: theClass],"%s has working allocWithZone",prefix); } /* perform basic allocation tests without initialisation*/ static void test_alloc_only(NSString *className) { Class theClass = NSClassFromString(className); id obj0 = nil; id obj1 = nil; const char *prefix = [[NSString stringWithFormat: @"Class '%@'", className] UTF8String]; NSZone *testZone = NSCreateZone(1024, 1024, 1); pass(theClass != Nil, "%s exists", prefix); obj0 = [theClass alloc]; pass(obj0 != nil, "%s has working alloc", prefix); pass([obj0 isKindOfClass: theClass], "%s alloc gives the correct class", prefix); PASS_EXCEPTION([obj0 description], NSInvalidArgumentException, "raises NSInvalidArgumentException in description") PASS_EXCEPTION(if([obj0 init]==nil)[NSException raise: NSInvalidArgumentException format: @""], NSInvalidArgumentException, "returns nil or raises NSInvalidArgumentException in init") PASS_EXCEPTION(if([theClass new]==nil)[NSException raise: NSInvalidArgumentException format: @""], NSInvalidArgumentException, "returns nil or raises NSInvalidArgumentException in new") obj1 = [theClass allocWithZone: testZone]; pass([obj1 isKindOfClass: theClass],"%s has working allocWithZone",prefix); } /* test for the NSObject protocol */ /* TODO move to ProtocolTesting.h? */ static void test_NSObject(NSString *className, NSArray *objects) { int i; Class theClass = Nil; theClass = NSClassFromString(className); pass(theClass != Nil, "%s is a known className", [className UTF8String]); for (i = 0; i < [objects count]; i++) { id theObj = [objects objectAtIndex: i]; id mySelf = nil; Class myClass = Nil; int count0; int count1; int count2; Class sup = Nil; const char *prefix; id r; prefix = [[NSString stringWithFormat: @"Object %i of class '%@'", i, className] UTF8String]; pass([theObj conformsToProtocol: @protocol(NSObject)], "%s conforms to NSObject", prefix); mySelf = [theObj self]; pass(mySelf == theObj, "%s can return self", prefix); myClass = [theObj class]; pass(myClass != Nil, "%s can return own class", prefix); pass([theObj isKindOfClass: theClass], "%s object %.160s is of correct class", prefix, [[theObj description] UTF8String]); pass(mySelf == myClass ? ![theObj isMemberOfClass: myClass] : [theObj isMemberOfClass: myClass], "%s isMemberOfClass works", prefix); sup = [theObj superclass]; pass(theClass == NSClassFromString(@"NSObject") ? sup == nil : (sup != nil && sup != myClass), "%s can return superclass", prefix); pass([theObj respondsToSelector: @selector(hash)], "%s responds to hash", prefix); pass([theObj isEqual: theObj], "%s isEqual: to self", prefix); pass([theObj respondsToSelector: @selector(self)], "%s respondsToSelector: ", prefix); [theObj isProxy]; r = [theObj retain]; pass(theObj == r, "%s handles retain", prefix); [theObj release]; [theObj retain]; [theObj autorelease]; count0 = [theObj retainCount]; [theObj retain]; count1 = [theObj retainCount]; [theObj release]; count2 = [theObj retainCount]; pass((count0 == count2), "%s has working retainCount", prefix); pass([[theObj description] isKindOfClass: [NSString class]], "%s has NSString description", prefix); pass([theObj performSelector: @selector(self)] == theObj, "%s handles performSelector", prefix); } } static void test_NSCoding(NSArray *objects) { int i; for (i = 0; i < [objects count]; i++) { id obj = [objects objectAtIndex: i]; const char *prefix; NSMutableData *data; NSArchiver *archiver; id decoded; START_SET(YES); pass([[[obj class] description] length], "I can extract a class name for object"); prefix = [[NSString stringWithFormat: @"Object %i of class '%s'", i, [NSStringFromClass([obj class]) UTF8String]] UTF8String]; pass([obj conformsToProtocol: @protocol(NSCoding)], "conforms to NSCoding protocol"); data = (NSMutableData *)[NSMutableData data]; archiver = [[NSArchiver alloc] initForWritingWithMutableData: data]; pass(archiver != nil, "I am able to set up an archiver"); data = nil; [archiver encodeRootObject: obj]; data = [archiver archiverData]; pass(data && [data length] > 0, "%s can be encoded", prefix); decoded = [NSUnarchiver unarchiveObjectWithData: data]; pass(decoded != nil, "can be decoded"); pass([decoded isEqual: obj], "decoded object equals the original"); END_SET("test_NSCoding object %u", i); } } static void test_keyed_NSCoding(NSArray *objects) { int i; for (i = 0; i < [objects count]; i++) { id obj = [objects objectAtIndex: i]; const char *prefix; NSData *data; id decoded; START_SET(YES); pass([[[obj class] description] length], "I can extract a class name for object"); prefix = [[NSString stringWithFormat: @"Object %i of class '%s'", i, [NSStringFromClass([obj class]) UTF8String]] UTF8String]; pass([obj conformsToProtocol: @protocol(NSCoding)], "conforms to NSCoding protocol"); data = [NSKeyedArchiver archivedDataWithRootObject: obj]; pass([data length] > 0, "%s can be encoded", prefix); decoded = [NSKeyedUnarchiver unarchiveObjectWithData: data]; pass (decoded != nil, "can be decoded"); PASS_EQUAL(decoded, obj, "decoded object equals the original"); END_SET("test_keyed_NSCoding object %u", i); } } static void test_NSCopying(NSString *iClassName, NSString *mClassName, NSArray *objects, BOOL mustRetain, BOOL mustCopy) { Class iClass = NSClassFromString(iClassName); Class mClass = NSClassFromString(mClassName); int i; NSZone *testZone = NSCreateZone(1024, 1024, 1); pass(iClass != Nil, "%s is a known class", [iClassName UTF8String]); pass(mClass != Nil, "%s is a known class", [mClassName UTF8String]); for (i = 0; i < [objects count]; i++) { BOOL immutable; NSString *theName; const char *prefix; id theCopy = nil; Class theClass = Nil; id theObj = [objects objectAtIndex: i]; START_SET(YES); if (iClass != mClass && [theObj isKindOfClass: mClass]) { immutable = NO; theName = iClassName; theClass = iClass; } else { immutable = YES; theName = mClassName; theClass = mClass; } prefix = [[NSString stringWithFormat: @"Object %i of class '%s'", i, [theName UTF8String]] UTF8String]; pass([theObj conformsToProtocol: @protocol(NSCopying)], "conforms to NSCopying"); theCopy = [theObj copy]; pass(theCopy != nil, "%s understands -copy", prefix); pass([theCopy isKindOfClass: iClass], "%s copy is of correct type", prefix); pass([theObj isEqual: theCopy], "%s original and copy are equal", prefix); if (immutable) { if (YES == mustRetain) { pass(theCopy == theObj, "%s is retained by copy with same zone", prefix); } else if (YES == mustCopy) { pass(theCopy != theObj, "%s is not retained by copy with same zone", prefix); } } if (theClass != iClass) { pass(![theCopy isKindOfClass: theClass], "%s result of copy is not immutable", prefix); } theCopy = [theObj copyWithZone: testZone]; pass(theCopy != nil, "%s understands -copyWithZone", prefix); pass([theCopy isKindOfClass: iClass], "%s zCopy has correct type", prefix); pass([theObj isEqual: theCopy], "%s copy and original are equal", prefix); if (immutable) { if (YES == mustRetain) { pass(theCopy == theObj, "%s is retained by copy with other zone", prefix); } else if (YES == mustCopy) { pass(theCopy != theObj, "%s is not retained by copy with other zone", prefix); } } if (theClass != iClass) pass(![theCopy isKindOfClass: theClass], "%s result of copyWithZone: is not immutable", prefix); END_SET("test_NSCopying object %u", i); } } static void test_NSMutableCopying(NSString *iClassName, NSString *mClassName, NSArray *objects) { int i; Class iClass = Nil; Class mClass = Nil; NSZone *testZone = NSCreateZone(1024, 1024, 1); iClass = NSClassFromString(iClassName); pass(iClass != Nil, "%s is a known class", [iClassName UTF8String]); mClass = NSClassFromString(mClassName); pass(mClass != Nil, "%s is a known class", [mClassName UTF8String]); for (i = 0; i < [objects count]; i++) { id theObj = [objects objectAtIndex: i]; NSString *theName = nil; const char *prefix; BOOL immutable; id theCopy = nil; Class theClass = Nil; START_SET(YES); if (iClass == mClass && [theObj isKindOfClass: mClass]) immutable = NO; else immutable = YES; if (immutable) { theName = iClassName; theClass = iClass; } else { theName = mClassName; theClass = mClass; } prefix = [[NSString stringWithFormat: @"Object %i of class '%s'", i, [theName UTF8String]] UTF8String]; pass([theObj conformsToProtocol: @protocol(NSMutableCopying)], "%s conforms to NSMutableCopying protocol", prefix); theCopy = [theObj mutableCopy]; pass(theCopy != nil, "%s understands -mutableCopy", prefix); pass([theCopy isKindOfClass: mClass], "%s mutable copy is of correct type", prefix); pass([theCopy isEqual: theObj], "%s copy equals original", prefix); pass(theCopy != theObj, "%s not retained by mutable copy in the same zone", [mClassName UTF8String]); theCopy = [theObj mutableCopyWithZone: testZone]; pass(theCopy != nil, "%s understands mutableCopyWithZone", [mClassName UTF8String]); pass(theCopy != theObj, "%s not retained by mutable copy in other zone", [mClassName UTF8String]); END_SET("test_NSMutableCopying object %u", i); } }