tools-make/TestFramework/ObjectTesting.h

522 lines
17 KiB
C
Raw Normal View History

/* 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 <Testing.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSData.h>
#import <Foundation/NSArchiver.h>
#import <Foundation/NSKeyedArchiver.h>
/* This file contains macros of testing some basic protocol implementation
* common to almost all classes.
*
* The reason for using macros rather than functions is that it allows the
* preprocessor and compiler to generate messages containign the file name
* and line number of the location of problems in your own testcase code.
*
* Sometimes, with a complex testing process, you want the location of the
* problem within that process ... so to aid that we also have function
* equivalents of the macros.
*/
/* Macro to perform basic allocation tests
*/
#define TEST_ALLOC(CN) \
{ \
NSString *className = (CN); \
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); \
DESTROY(obj0); \
\
obj0 = [[theClass alloc] init]; \
PASS([obj0 isKindOfClass: theClass], "%s has working init", prefix); \
DESTROY(obj0); \
\
obj0 = [theClass new]; \
PASS([obj0 isKindOfClass: theClass], "%s has working new", prefix); \
DESTROY(obj0); \
\
obj1 = [theClass allocWithZone: testZone]; \
PASS([obj1 isKindOfClass: theClass],"%s has working allocWithZone",prefix); \
PASS([obj1 isKindOfClass: theClass], \
"%s allocWithZone: gives the correct class", prefix); \
DESTROY(obj1); \
NSRecycleZone(testZone); \
}
static void test_alloc(NSString *CN) __attribute__ ((unused));
static void test_alloc(NSString *CN)
{
TEST_ALLOC(CN);
}
/* Macro to perform basic allocation tests without initialisation
*/
#define TEST_ALLOC_ONLY(CN) \
{ \
NSString *className = (CN); \
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=[obj0 init])==nil)[NSException raise: NSInvalidArgumentException format: @""], \
NSInvalidArgumentException, \
"returns nil or raises NSInvalidArgumentException in init") \
\
PASS_EXCEPTION(if((obj0=[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); \
DESTROY(obj1); \
NSRecycleZone(testZone); \
}
static void test_alloc_only(NSString *CN) __attribute__ ((unused));
static void test_alloc_only(NSString *CN)
{
TEST_ALLOC_ONLY(CN);
}
/* Macro to test for the NSObject protocol
* Arguments are:
* CN The name of the class to be tested
* OJS An arraayof objects to be tested
*/
#define TEST_NSOBJECT(CN, OJS) \
{ \
NSString *className = (CN); \
NSArray *objects = (OJS); \
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 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]; \
\
count1 = [theObj retainCount]; \
[theObj retain]; \
[theObj release]; \
count2 = [theObj retainCount]; \
PASS((count1 == 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_NSObject(NSString *CN, NSArray *OJS) __attribute__ ((unused));
static void test_NSObject(NSString *CN, NSArray *OJS)
{
TEST_NSOBJECT(CN, OJS)
}
/* Archives each object in the array, then unarchives it and checks that
* the two are equal (using the PASS_EQUAL macro).
*/
#define TEST_NSCODING(OJS) \
{ \
NSArray *objects = (OJS); \
int i; \
for (i = 0; i < [objects count]; i++) \
{ \
char buf[100]; \
id obj = [objects objectAtIndex: i]; \
const char *prefix; \
NSMutableData *data; \
NSArchiver *archiver; \
id decoded; \
\
snprintf(buf, sizeof(buf), "test_NSCoding object %u", i); \
START_SET(buf) \
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_EQUAL(decoded, obj, "decoded object equals the original"); \
DESTROY(archiver); \
END_SET(buf) \
} \
}
static void test_NSCoding(NSArray *OJS) __attribute__ ((unused));
static void test_NSCoding(NSArray *OJS)
{
TEST_NSCODING(OJS);
}
/* Archives each object in the argument array,
* then unarchives it and checks that the two are
* equal using the PASS_EQUAL macro.
*/
#define TEST_KEYED_NSCODING(OJS) \
{ \
NSArray *objects = (OJS); \
int i; \
for (i = 0; i < [objects count]; i++) \
{ \
char buf[100]; \
id obj = [objects objectAtIndex: i]; \
const char *prefix; \
NSData *data; \
id decoded; \
\
snprintf(buf, sizeof(buf), "test_keyed_NSCoding object %u", i); \
START_SET(buf) \
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(buf) \
} \
}
static void test_keyed_NSCoding(NSArray *OJS) __attribute__ ((unused));
static void test_keyed_NSCoding(NSArray *OJS)
{
TEST_KEYED_NSCODING(OJS);
}
/* A macro for testing that objects conform to, and
* implement the NSCopying protocol.
* Macro arguments are:
* ICN An NSString object containing the name of the
* immutable class to be immutably copied.
* MCN An NSString object containing the name of the
* mutable class corresponding to the immutable
* class.
* OJS An NSArray object containg one or more objects
* to be tested.
* MRT A flag saying whether copies of an immutable
* instance in the same zone must be implemented
* by simply retaining the original.
* MCP A flag saying whether copies of an immutable
* instance must always be made as real copies
* rather than by retaining the original.
*/
#define TEST_NSCOPYING(ICN, MCN, OJS, MRT, MCP) \
{ \
NSString *iClassName = (ICN); \
NSString *mClassName = (MCN); \
NSArray *objects = (OJS); \
BOOL mustRetain = (MRT); \
BOOL mustCopy = (MCP); \
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++) \
{ \
char buf[100]; \
BOOL immutable; \
NSString *theName; \
const char *prefix; \
id theCopy = nil; \
Class theClass = Nil; \
id theObj = [objects objectAtIndex: i]; \
\
snprintf(buf, sizeof(buf), "test_NSCopying object %u", i); \
START_SET(buf) \
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_EQUAL(theCopy, theObj, \
"%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); \
} \
\
DESTROY(theCopy); \
theCopy = [theObj copyWithZone: testZone]; \
PASS(theCopy != nil, "%s understands -copyWithZone", prefix); \
PASS([theCopy isKindOfClass: iClass], \
"%s zCopy has correct type", prefix); \
PASS_EQUAL(theCopy, theObj, \
"%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); \
DESTROY(theCopy); \
END_SET(buf) \
} \
}
static void test_NSCopying(
NSString *ICN, NSString *MCN, NSArray *OJS, BOOL MRT, BOOL MCP)
__attribute__ ((unused));
static void test_NSCopying(
NSString *ICN, NSString *MCN, NSArray *OJS, BOOL MRT, BOOL MCP)
{
TEST_NSCOPYING(ICN, MCN, OJS, MRT, MCP);
}
/* A macro for testing that objects conform to, and
* implement the mutable copying protocol.
* Macro arguments are:
* ICN An NSString object containing the name of the
* immutable class to be mutably copied.
* MCN An NSString object containing the name of the
* mutable class to be produced as a result of
* the copy.
* OJS An NSArray object containg one or more objects
* to be tested.
*/
#define TEST_NSMUTABLECOPYING(ICN, MCN, OJS) \
{ \
NSString *iClassName = (ICN); \
NSString *mClassName = (MCN); \
NSArray *objects = (OJS); \
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++) \
{ \
char buf[100]; \
id theObj = [objects objectAtIndex: i]; \
NSString *theName = nil; \
const char *prefix; \
BOOL immutable; \
id theCopy = nil; \
\
snprintf(buf, sizeof(buf), "test_NSMutableCopying object %u", i); \
START_SET(buf); \
if (iClass == mClass && [theObj isKindOfClass: mClass]) \
immutable = NO; \
else \
immutable = YES; \
\
if (YES == immutable) \
{ \
theName = iClassName; \
} \
else \
{ \
theName = mClassName; \
} \
\
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_EQUAL(theCopy, theObj, \
"%s copy object equals the original", prefix); \
PASS(theCopy != theObj, \
"%s not retained by mutable copy in the same zone", \
[mClassName UTF8String]); \
DESTROY(theCopy); \
\
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]); \
DESTROY(theCopy); \
END_SET(buf) \
} \
}
static void test_NSMutableCopying(
NSString *ICN, NSString *MCN, NSArray *OJS) __attribute__ ((unused));
static void test_NSMutableCopying(NSString *ICN, NSString *MCN, NSArray *OJS)
{
TEST_NSMUTABLECOPYING(ICN, MCN, OJS);
}
/* DEPRECATED ... please use the START_SET/END_SET and PASS macros instead.
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 { fprintf(stderr, "Failed test: " desc, ## args); \
fprintf(stderr, "\n"); }
/* 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); \
}