1/* 2copyright 2004 Alexander Malmberg <alexander@malmberg.org> 3*/ 4 5#include "Testing.h" 6 7#include <stdio.h> 8 9#include <Foundation/NSArchiver.h> 10#include <Foundation/NSArray.h> 11#include <Foundation/NSAttributedString.h> 12#include <Foundation/NSAutoreleasePool.h> 13#include <Foundation/NSByteOrder.h> 14#include <Foundation/NSCharacterSet.h> 15#include <Foundation/NSData.h> 16#include <Foundation/NSDate.h> 17#include <Foundation/NSDateFormatter.h> 18#include <Foundation/NSDictionary.h> 19#include <Foundation/NSException.h> 20#include <Foundation/NSFormatter.h> 21#include <Foundation/NSGeometry.h> 22#include <Foundation/NSNotification.h> 23#include <Foundation/NSNull.h> 24#include <Foundation/NSObject.h> 25#include <Foundation/NSSet.h> 26#include <Foundation/NSString.h> 27#include <Foundation/NSURL.h> 28#include <Foundation/NSValue.h> 29 30@interface NSObject (Equality) 31- (BOOL) testEquality: (id)other; 32@end 33 34@interface NSObject (DecodingTests) 35+ (NSObject*) createTestInstance; 36+ (BOOL) verifyTestInstance: (NSObject *)instance 37 ofVersion: (int)version; 38- (BOOL) testEquality; 39@end 40 41@implementation NSObject (DecodingTests) 42+ (NSObject *) createTestInstance 43{ 44 if (self == [NSException class]) 45 { 46 return [[NSException alloc] initWithName: @"Test" 47 reason: @"Testing" 48 userInfo: nil]; 49 } 50 else 51 { 52 return [[self alloc] init]; 53 } 54} 55 56+ (BOOL) verifyTestInstance: (NSObject *)instance 57 ofVersion: (int)version 58{ 59 id o; 60 61 if (instance == nil) return NO; 62 o = [self createTestInstance]; 63 if (YES == [o respondsToSelector: @selector(testEquality:)]) 64 return [o testEquality: instance]; 65 if (NO == [instance testEquality]) return YES; 66 return [o isEqual: instance]; 67} 68 69- (BOOL) testEquality 70{ 71 static IMP impNSObject = 0; 72 /* By default, assume that every class that overrides NSObject's 73 isEqual: implementation can compare archived instances. 74 subclasses for which this doesn't hold can simply override this 75 method in a category and return a constant YES/NO. */ 76 77 if (!impNSObject) 78 { 79 impNSObject = [NSObject instanceMethodForSelector:@selector(isEqual:)]; 80 } 81 return [self methodForSelector:@selector(isEqual:)] == impNSObject 82 ? NO : YES; 83} 84@end 85 86@implementation NSCharacterSet (DecodingTests) 87+ (NSObject *) createTestInstance 88{ 89 return [[self characterSetWithCharactersInString: @"qwertzuiop"] retain]; 90} 91@end 92 93@implementation NSValue (DecodingTests) 94+ (NSObject *) createTestInstance 95{ 96 return [[self valueWithSize: NSMakeSize(1.1, 1.2)] retain]; 97} 98- (BOOL) testEquality: (id)other 99{ 100 if (strcmp([self objCType], @encode(NSSize)) == 0) 101 { 102 NSSize mSize = [self sizeValue]; 103 NSSize oSize = [other sizeValue]; 104 105 if (EQ(mSize.height, oSize.height) && EQ(mSize.width, oSize.width)) 106 return YES; 107 return NO; 108 } 109 return [self isEqual: other]; 110} 111@end 112 113@implementation NSNumber (DecodingTests) 114+ (NSObject *) createTestInstance 115{ 116 return [[self numberWithInt: 1] retain]; 117} 118@end 119 120@implementation NSData (DecodingTests) 121+ (NSObject *) createTestInstance 122{ 123 NSString *source = @"We need constant data"; 124 NSData *data = [source dataUsingEncoding: NSUnicodeStringEncoding]; 125 126 if (NSHostByteOrder() == NS_BigEndian) 127 { 128 NSMutableData *m = [data mutableCopy]; 129 uint8_t *p = (uint8_t*)[m mutableBytes]; 130 uint8_t *e = p + [m length]; 131 132 while (p < e) 133 { 134 uint8_t tmp = p[0]; 135 136 p[0] = p[1]; 137 p[1] = tmp; 138 p += 2; 139 } 140 return m; 141 } 142 else 143 { 144 return [data retain]; 145 } 146} 147@end 148 149@implementation NSDate (DecodingTests) 150+ (NSObject *) createTestInstance 151{ 152 return [[NSDate dateWithTimeIntervalSince1970: 4294967296.0] retain]; 153} 154@end 155 156@implementation NSURL (DecodingTests) 157+ (NSObject *) createTestInstance 158{ 159 return [[self alloc] initWithString: @"http://www.gnustep.org/"]; 160} 161@end 162 163 164/* 165If set, we write out new .data files for the current versions for classes 166that don't have them. 167*/ 168BOOL update; 169 170void test(Class class) 171{ 172 NS_DURING 173 { 174 /* 175 In order to catch decoders that don't consume all the data that they 176 should, we decode/encode an array that includes the object and a string. 177 We verify that the string was correctly decoded, although any errors will 178 likely be caught by crashes in the unarchiver. 179 */ 180 NSString *sentinel = @"quux!"; 181 182 int v = [class version]; 183 NSObject *instance; 184 NSArray *decodedInstance; 185 NSData *d; 186 NSString *filename; 187 188 instance = [class createTestInstance]; 189 190 d = [NSArchiver archivedDataWithRootObject: 191 [NSArray arrayWithObjects: instance, sentinel, nil]]; 192 decodedInstance = [NSUnarchiver unarchiveObjectWithData: d]; 193 194 NSCAssert([sentinel isEqual: [decodedInstance objectAtIndex: 1]], 195 NSInternalInconsistencyException); 196 197 PASS([class verifyTestInstance: [decodedInstance objectAtIndex: 0] 198 ofVersion: v], "decoding current version of class %s", POBJECT(class)); 199 200 for (; v >= 0; v--) 201 { 202 int w; 203 204 for (w = 0; w < 2; w++) 205 { 206 const char *width; 207 208 if (0 == w) 209 { 210 if (YES == update && 4 != sizeof(void*)) 211 { 212 continue; // Can't write a 32bit update. 213 } 214 width = "32bit"; 215 } 216 else 217 { 218 if (YES == update && 8 != sizeof(void*)) 219 { 220 continue; // Can't write a 64bit update. 221 } 222 width = "64bit"; 223 } 224 225 filename = [NSString stringWithFormat: @"%@.%i.%s", 226 class, v, width]; 227 d = [NSData dataWithContentsOfFile: filename]; 228 if (!d) 229 { 230 if (v == [class version]) 231 { 232 if (!update) 233 PASS(0, "%s has %s reference data for current version", 234 POBJECT(class), width) 235 else 236 [NSArchiver archiveRootObject: 237 [NSArray arrayWithObjects: instance, sentinel, nil] 238 toFile: filename]; 239 } 240 continue; 241 } 242 243 decodedInstance = [NSUnarchiver unarchiveObjectWithData: d]; 244 NSCAssert([sentinel isEqual: [decodedInstance objectAtIndex: 1]], 245 NSInternalInconsistencyException); 246 PASS([class verifyTestInstance: [decodedInstance objectAtIndex: 0] 247 ofVersion: v], "decoding %s version %i of class %s", 248 width, v, POBJECT(class)); 249 } 250 } 251 } 252 NS_HANDLER 253 { 254 PASS(0, "decoding class %s: %s", 255 POBJECT(class), POBJECT(localException)); 256 } 257 NS_ENDHANDLER 258} 259 260int main(int argc, char **argv) 261{ 262 NSAutoreleasePool *arp = [NSAutoreleasePool new]; 263 264 update = argc == 2 && !strcmp(argv[1], "--update"); 265 266#define T(c) test([c class]); 267 T(NSArray) 268 T(NSAttributedString) 269 T(NSCharacterSet) 270 T(NSData) 271 T(NSMutableData) 272 T(NSDate) 273 T(NSDateFormatter) 274 T(NSDictionary) 275 T(NSException) 276 T(NSNotification) 277 T(NSNull) 278 T(NSObject) 279 T(NSSet) 280 T(NSString) 281 T(NSURL) 282 T(NSValue) 283 T(NSNumber) 284 285 [arp release]; arp = nil; 286 287 return 0; 288} 289 290