toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 //
michael@0 2 // GTMSenTestCase.m
michael@0 3 //
michael@0 4 // Copyright 2007-2008 Google Inc.
michael@0 5 //
michael@0 6 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
michael@0 7 // use this file except in compliance with the License. You may obtain a copy
michael@0 8 // of the License at
michael@0 9 //
michael@0 10 // http://www.apache.org/licenses/LICENSE-2.0
michael@0 11 //
michael@0 12 // Unless required by applicable law or agreed to in writing, software
michael@0 13 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
michael@0 14 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
michael@0 15 // License for the specific language governing permissions and limitations under
michael@0 16 // the License.
michael@0 17 //
michael@0 18
michael@0 19 #import "GTMSenTestCase.h"
michael@0 20
michael@0 21 #import <unistd.h>
michael@0 22 #if GTM_IPHONE_SIMULATOR
michael@0 23 #import <objc/message.h>
michael@0 24 #endif
michael@0 25
michael@0 26 #import "GTMObjC2Runtime.h"
michael@0 27 #import "GTMUnitTestDevLog.h"
michael@0 28
michael@0 29 #if !GTM_IPHONE_SDK
michael@0 30 #import "GTMGarbageCollection.h"
michael@0 31 #endif // !GTM_IPHONE_SDK
michael@0 32
michael@0 33 #if GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
michael@0 34 #import <stdarg.h>
michael@0 35
michael@0 36 @interface NSException (GTMSenTestPrivateAdditions)
michael@0 37 + (NSException *)failureInFile:(NSString *)filename
michael@0 38 atLine:(int)lineNumber
michael@0 39 reason:(NSString *)reason;
michael@0 40 @end
michael@0 41
michael@0 42 @implementation NSException (GTMSenTestPrivateAdditions)
michael@0 43 + (NSException *)failureInFile:(NSString *)filename
michael@0 44 atLine:(int)lineNumber
michael@0 45 reason:(NSString *)reason {
michael@0 46 NSDictionary *userInfo =
michael@0 47 [NSDictionary dictionaryWithObjectsAndKeys:
michael@0 48 [NSNumber numberWithInteger:lineNumber], SenTestLineNumberKey,
michael@0 49 filename, SenTestFilenameKey,
michael@0 50 nil];
michael@0 51
michael@0 52 return [self exceptionWithName:SenTestFailureException
michael@0 53 reason:reason
michael@0 54 userInfo:userInfo];
michael@0 55 }
michael@0 56 @end
michael@0 57
michael@0 58 @implementation NSException (GTMSenTestAdditions)
michael@0 59
michael@0 60 + (NSException *)failureInFile:(NSString *)filename
michael@0 61 atLine:(int)lineNumber
michael@0 62 withDescription:(NSString *)formatString, ... {
michael@0 63
michael@0 64 NSString *testDescription = @"";
michael@0 65 if (formatString) {
michael@0 66 va_list vl;
michael@0 67 va_start(vl, formatString);
michael@0 68 testDescription =
michael@0 69 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
michael@0 70 va_end(vl);
michael@0 71 }
michael@0 72
michael@0 73 NSString *reason = testDescription;
michael@0 74
michael@0 75 return [self failureInFile:filename atLine:lineNumber reason:reason];
michael@0 76 }
michael@0 77
michael@0 78 + (NSException *)failureInCondition:(NSString *)condition
michael@0 79 isTrue:(BOOL)isTrue
michael@0 80 inFile:(NSString *)filename
michael@0 81 atLine:(int)lineNumber
michael@0 82 withDescription:(NSString *)formatString, ... {
michael@0 83
michael@0 84 NSString *testDescription = @"";
michael@0 85 if (formatString) {
michael@0 86 va_list vl;
michael@0 87 va_start(vl, formatString);
michael@0 88 testDescription =
michael@0 89 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
michael@0 90 va_end(vl);
michael@0 91 }
michael@0 92
michael@0 93 NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@",
michael@0 94 condition, isTrue ? "false" : "true", testDescription];
michael@0 95
michael@0 96 return [self failureInFile:filename atLine:lineNumber reason:reason];
michael@0 97 }
michael@0 98
michael@0 99 + (NSException *)failureInEqualityBetweenObject:(id)left
michael@0 100 andObject:(id)right
michael@0 101 inFile:(NSString *)filename
michael@0 102 atLine:(int)lineNumber
michael@0 103 withDescription:(NSString *)formatString, ... {
michael@0 104
michael@0 105 NSString *testDescription = @"";
michael@0 106 if (formatString) {
michael@0 107 va_list vl;
michael@0 108 va_start(vl, formatString);
michael@0 109 testDescription =
michael@0 110 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
michael@0 111 va_end(vl);
michael@0 112 }
michael@0 113
michael@0 114 NSString *reason =
michael@0 115 [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@",
michael@0 116 [left description], [right description], testDescription];
michael@0 117
michael@0 118 return [self failureInFile:filename atLine:lineNumber reason:reason];
michael@0 119 }
michael@0 120
michael@0 121 + (NSException *)failureInEqualityBetweenValue:(NSValue *)left
michael@0 122 andValue:(NSValue *)right
michael@0 123 withAccuracy:(NSValue *)accuracy
michael@0 124 inFile:(NSString *)filename
michael@0 125 atLine:(int)lineNumber
michael@0 126 withDescription:(NSString *)formatString, ... {
michael@0 127
michael@0 128 NSString *testDescription = @"";
michael@0 129 if (formatString) {
michael@0 130 va_list vl;
michael@0 131 va_start(vl, formatString);
michael@0 132 testDescription =
michael@0 133 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
michael@0 134 va_end(vl);
michael@0 135 }
michael@0 136
michael@0 137 NSString *reason;
michael@0 138 if (accuracy) {
michael@0 139 reason =
michael@0 140 [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@",
michael@0 141 left, right, testDescription];
michael@0 142 } else {
michael@0 143 reason =
michael@0 144 [NSString stringWithFormat:@"'%@' should be equal to '%@' +/-'%@'. %@",
michael@0 145 left, right, accuracy, testDescription];
michael@0 146 }
michael@0 147
michael@0 148 return [self failureInFile:filename atLine:lineNumber reason:reason];
michael@0 149 }
michael@0 150
michael@0 151 + (NSException *)failureInRaise:(NSString *)expression
michael@0 152 inFile:(NSString *)filename
michael@0 153 atLine:(int)lineNumber
michael@0 154 withDescription:(NSString *)formatString, ... {
michael@0 155
michael@0 156 NSString *testDescription = @"";
michael@0 157 if (formatString) {
michael@0 158 va_list vl;
michael@0 159 va_start(vl, formatString);
michael@0 160 testDescription =
michael@0 161 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
michael@0 162 va_end(vl);
michael@0 163 }
michael@0 164
michael@0 165 NSString *reason = [NSString stringWithFormat:@"'%@' should raise. %@",
michael@0 166 expression, testDescription];
michael@0 167
michael@0 168 return [self failureInFile:filename atLine:lineNumber reason:reason];
michael@0 169 }
michael@0 170
michael@0 171 + (NSException *)failureInRaise:(NSString *)expression
michael@0 172 exception:(NSException *)exception
michael@0 173 inFile:(NSString *)filename
michael@0 174 atLine:(int)lineNumber
michael@0 175 withDescription:(NSString *)formatString, ... {
michael@0 176
michael@0 177 NSString *testDescription = @"";
michael@0 178 if (formatString) {
michael@0 179 va_list vl;
michael@0 180 va_start(vl, formatString);
michael@0 181 testDescription =
michael@0 182 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
michael@0 183 va_end(vl);
michael@0 184 }
michael@0 185
michael@0 186 NSString *reason;
michael@0 187 if ([[exception name] isEqualToString:SenTestFailureException]) {
michael@0 188 // it's our exception, assume it has the right description on it.
michael@0 189 reason = [exception reason];
michael@0 190 } else {
michael@0 191 // not one of our exception, use the exceptions reason and our description
michael@0 192 reason = [NSString stringWithFormat:@"'%@' raised '%@'. %@",
michael@0 193 expression, [exception reason], testDescription];
michael@0 194 }
michael@0 195
michael@0 196 return [self failureInFile:filename atLine:lineNumber reason:reason];
michael@0 197 }
michael@0 198
michael@0 199 @end
michael@0 200
michael@0 201 NSString *STComposeString(NSString *formatString, ...) {
michael@0 202 NSString *reason = @"";
michael@0 203 if (formatString) {
michael@0 204 va_list vl;
michael@0 205 va_start(vl, formatString);
michael@0 206 reason =
michael@0 207 [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease];
michael@0 208 va_end(vl);
michael@0 209 }
michael@0 210 return reason;
michael@0 211 }
michael@0 212
michael@0 213 NSString *const SenTestFailureException = @"SenTestFailureException";
michael@0 214 NSString *const SenTestFilenameKey = @"SenTestFilenameKey";
michael@0 215 NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey";
michael@0 216
michael@0 217 @interface SenTestCase (SenTestCasePrivate)
michael@0 218 // our method of logging errors
michael@0 219 + (void)printException:(NSException *)exception fromTestName:(NSString *)name;
michael@0 220 @end
michael@0 221
michael@0 222 @implementation SenTestCase
michael@0 223 + (id)testCaseWithInvocation:(NSInvocation *)anInvocation {
michael@0 224 return [[[self alloc] initWithInvocation:anInvocation] autorelease];
michael@0 225 }
michael@0 226
michael@0 227 - (id)initWithInvocation:(NSInvocation *)anInvocation {
michael@0 228 if ((self = [super init])) {
michael@0 229 invocation_ = [anInvocation retain];
michael@0 230 }
michael@0 231 return self;
michael@0 232 }
michael@0 233
michael@0 234 - (void)dealloc {
michael@0 235 [invocation_ release];
michael@0 236 [super dealloc];
michael@0 237 }
michael@0 238
michael@0 239 - (void)failWithException:(NSException*)exception {
michael@0 240 [exception raise];
michael@0 241 }
michael@0 242
michael@0 243 - (void)setUp {
michael@0 244 }
michael@0 245
michael@0 246 - (void)performTest {
michael@0 247 @try {
michael@0 248 [self invokeTest];
michael@0 249 } @catch (NSException *exception) {
michael@0 250 [[self class] printException:exception
michael@0 251 fromTestName:NSStringFromSelector([self selector])];
michael@0 252 [exception raise];
michael@0 253 }
michael@0 254 }
michael@0 255
michael@0 256 - (NSInvocation *)invocation {
michael@0 257 return invocation_;
michael@0 258 }
michael@0 259
michael@0 260 - (SEL)selector {
michael@0 261 return [invocation_ selector];
michael@0 262 }
michael@0 263
michael@0 264 + (void)printException:(NSException *)exception fromTestName:(NSString *)name {
michael@0 265 NSDictionary *userInfo = [exception userInfo];
michael@0 266 NSString *filename = [userInfo objectForKey:SenTestFilenameKey];
michael@0 267 NSNumber *lineNumber = [userInfo objectForKey:SenTestLineNumberKey];
michael@0 268 NSString *className = NSStringFromClass([self class]);
michael@0 269 if ([filename length] == 0) {
michael@0 270 filename = @"Unknown.m";
michael@0 271 }
michael@0 272 fprintf(stderr, "%s:%ld: error: -[%s %s] : %s\n",
michael@0 273 [filename UTF8String],
michael@0 274 (long)[lineNumber integerValue],
michael@0 275 [className UTF8String],
michael@0 276 [name UTF8String],
michael@0 277 [[exception reason] UTF8String]);
michael@0 278 fflush(stderr);
michael@0 279 }
michael@0 280
michael@0 281 - (void)invokeTest {
michael@0 282 NSException *e = nil;
michael@0 283 @try {
michael@0 284 // Wrap things in autorelease pools because they may
michael@0 285 // have an STMacro in their dealloc which may get called
michael@0 286 // when the pool is cleaned up
michael@0 287 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
michael@0 288 // We don't log exceptions here, instead we let the person that called
michael@0 289 // this log the exception. This ensures they are only logged once but the
michael@0 290 // outer layers get the exceptions to report counts, etc.
michael@0 291 @try {
michael@0 292 [self setUp];
michael@0 293 @try {
michael@0 294 NSInvocation *invocation = [self invocation];
michael@0 295 #if GTM_IPHONE_SIMULATOR
michael@0 296 // We don't call [invocation invokeWithTarget:self]; because of
michael@0 297 // Radar 8081169: NSInvalidArgumentException can't be caught
michael@0 298 // It turns out that on iOS4 (and 3.2) exceptions thrown inside an
michael@0 299 // [invocation invoke] on the simulator cannot be caught.
michael@0 300 // http://openradar.appspot.com/8081169
michael@0 301 objc_msgSend(self, [invocation selector]);
michael@0 302 #else
michael@0 303 [invocation invokeWithTarget:self];
michael@0 304 #endif
michael@0 305 } @catch (NSException *exception) {
michael@0 306 e = [exception retain];
michael@0 307 }
michael@0 308 [self tearDown];
michael@0 309 } @catch (NSException *exception) {
michael@0 310 e = [exception retain];
michael@0 311 }
michael@0 312 [pool release];
michael@0 313 } @catch (NSException *exception) {
michael@0 314 e = [exception retain];
michael@0 315 }
michael@0 316 if (e) {
michael@0 317 [e autorelease];
michael@0 318 [e raise];
michael@0 319 }
michael@0 320 }
michael@0 321
michael@0 322 - (void)tearDown {
michael@0 323 }
michael@0 324
michael@0 325 - (NSString *)description {
michael@0 326 // This matches the description OCUnit would return to you
michael@0 327 return [NSString stringWithFormat:@"-[%@ %@]", [self class],
michael@0 328 NSStringFromSelector([self selector])];
michael@0 329 }
michael@0 330
michael@0 331 // Used for sorting methods below
michael@0 332 static int MethodSort(id a, id b, void *context) {
michael@0 333 NSInvocation *invocationA = a;
michael@0 334 NSInvocation *invocationB = b;
michael@0 335 const char *nameA = sel_getName([invocationA selector]);
michael@0 336 const char *nameB = sel_getName([invocationB selector]);
michael@0 337 return strcmp(nameA, nameB);
michael@0 338 }
michael@0 339
michael@0 340
michael@0 341 + (NSArray *)testInvocations {
michael@0 342 NSMutableArray *invocations = nil;
michael@0 343 // Need to walk all the way up the parent classes collecting methods (in case
michael@0 344 // a test is a subclass of another test).
michael@0 345 Class senTestCaseClass = [SenTestCase class];
michael@0 346 for (Class currentClass = self;
michael@0 347 currentClass && (currentClass != senTestCaseClass);
michael@0 348 currentClass = class_getSuperclass(currentClass)) {
michael@0 349 unsigned int methodCount;
michael@0 350 Method *methods = class_copyMethodList(currentClass, &methodCount);
michael@0 351 if (methods) {
michael@0 352 // This handles disposing of methods for us even if an exception should fly.
michael@0 353 [NSData dataWithBytesNoCopy:methods
michael@0 354 length:sizeof(Method) * methodCount];
michael@0 355 if (!invocations) {
michael@0 356 invocations = [NSMutableArray arrayWithCapacity:methodCount];
michael@0 357 }
michael@0 358 for (size_t i = 0; i < methodCount; ++i) {
michael@0 359 Method currMethod = methods[i];
michael@0 360 SEL sel = method_getName(currMethod);
michael@0 361 char *returnType = NULL;
michael@0 362 const char *name = sel_getName(sel);
michael@0 363 // If it starts with test, takes 2 args (target and sel) and returns
michael@0 364 // void run it.
michael@0 365 if (strstr(name, "test") == name) {
michael@0 366 returnType = method_copyReturnType(currMethod);
michael@0 367 if (returnType) {
michael@0 368 // This handles disposing of returnType for us even if an
michael@0 369 // exception should fly. Length +1 for the terminator, not that
michael@0 370 // the length really matters here, as we never reference inside
michael@0 371 // the data block.
michael@0 372 [NSData dataWithBytesNoCopy:returnType
michael@0 373 length:strlen(returnType) + 1];
michael@0 374 }
michael@0 375 }
michael@0 376 // TODO: If a test class is a subclass of another, and they reuse the
michael@0 377 // same selector name (ie-subclass overrides it), this current loop
michael@0 378 // and test here will cause cause it to get invoked twice. To fix this
michael@0 379 // the selector would have to be checked against all the ones already
michael@0 380 // added, so it only gets done once.
michael@0 381 if (returnType // True if name starts with "test"
michael@0 382 && strcmp(returnType, @encode(void)) == 0
michael@0 383 && method_getNumberOfArguments(currMethod) == 2) {
michael@0 384 NSMethodSignature *sig = [self instanceMethodSignatureForSelector:sel];
michael@0 385 NSInvocation *invocation
michael@0 386 = [NSInvocation invocationWithMethodSignature:sig];
michael@0 387 [invocation setSelector:sel];
michael@0 388 [invocations addObject:invocation];
michael@0 389 }
michael@0 390 }
michael@0 391 }
michael@0 392 }
michael@0 393 // Match SenTestKit and run everything in alphbetical order.
michael@0 394 [invocations sortUsingFunction:MethodSort context:nil];
michael@0 395 return invocations;
michael@0 396 }
michael@0 397
michael@0 398 @end
michael@0 399
michael@0 400 #endif // GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
michael@0 401
michael@0 402 @implementation GTMTestCase : SenTestCase
michael@0 403 - (void)invokeTest {
michael@0 404 NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
michael@0 405 Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog");
michael@0 406 if (devLogClass) {
michael@0 407 [devLogClass performSelector:@selector(enableTracking)];
michael@0 408 [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
michael@0 409
michael@0 410 }
michael@0 411 [super invokeTest];
michael@0 412 if (devLogClass) {
michael@0 413 [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)];
michael@0 414 [devLogClass performSelector:@selector(disableTracking)];
michael@0 415 }
michael@0 416 [localPool drain];
michael@0 417 }
michael@0 418
michael@0 419 + (BOOL)isAbstractTestCase {
michael@0 420 NSString *name = NSStringFromClass(self);
michael@0 421 return [name rangeOfString:@"AbstractTest"].location != NSNotFound;
michael@0 422 }
michael@0 423
michael@0 424 + (NSArray *)testInvocations {
michael@0 425 NSArray *invocations = nil;
michael@0 426 if (![self isAbstractTestCase]) {
michael@0 427 invocations = [super testInvocations];
michael@0 428 }
michael@0 429 return invocations;
michael@0 430 }
michael@0 431
michael@0 432 @end
michael@0 433
michael@0 434 // Leak detection
michael@0 435 #if !GTM_IPHONE_DEVICE && !GTM_SUPPRESS_RUN_LEAKS_HOOK
michael@0 436 // Don't want to get leaks on the iPhone Device as the device doesn't
michael@0 437 // have 'leaks'. The simulator does though.
michael@0 438
michael@0 439 // COV_NF_START
michael@0 440 // We don't have leak checking on by default, so this won't be hit.
michael@0 441 static void _GTMRunLeaks(void) {
michael@0 442 // This is an atexit handler. It runs leaks for us to check if we are
michael@0 443 // leaking anything in our tests.
michael@0 444 const char* cExclusionsEnv = getenv("GTM_LEAKS_SYMBOLS_TO_IGNORE");
michael@0 445 NSMutableString *exclusions = [NSMutableString string];
michael@0 446 if (cExclusionsEnv) {
michael@0 447 NSString *exclusionsEnv = [NSString stringWithUTF8String:cExclusionsEnv];
michael@0 448 NSArray *exclusionsArray = [exclusionsEnv componentsSeparatedByString:@","];
michael@0 449 NSString *exclusion;
michael@0 450 NSCharacterSet *wcSet = [NSCharacterSet whitespaceCharacterSet];
michael@0 451 GTM_FOREACH_OBJECT(exclusion, exclusionsArray) {
michael@0 452 exclusion = [exclusion stringByTrimmingCharactersInSet:wcSet];
michael@0 453 [exclusions appendFormat:@"-exclude \"%@\" ", exclusion];
michael@0 454 }
michael@0 455 }
michael@0 456 // Clearing out DYLD_ROOT_PATH because iPhone Simulator framework libraries
michael@0 457 // are different from regular OS X libraries and leaks will fail to run
michael@0 458 // because of missing symbols. Also capturing the output of leaks and then
michael@0 459 // pipe rather than a direct pipe, because otherwise if leaks failed,
michael@0 460 // the system() call will still be successful. Bug:
michael@0 461 // http://code.google.com/p/google-toolbox-for-mac/issues/detail?id=56
michael@0 462 NSString *string
michael@0 463 = [NSString stringWithFormat:
michael@0 464 @"LeakOut=`DYLD_ROOT_PATH='' /usr/bin/leaks %@%d` &&"
michael@0 465 @"echo \"$LeakOut\"|/usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'",
michael@0 466 exclusions, getpid()];
michael@0 467 int ret = system([string UTF8String]);
michael@0 468 if (ret) {
michael@0 469 fprintf(stderr,
michael@0 470 "%s:%d: Error: Unable to run leaks. 'system' returned: %d\n",
michael@0 471 __FILE__, __LINE__, ret);
michael@0 472 fflush(stderr);
michael@0 473 }
michael@0 474 }
michael@0 475 // COV_NF_END
michael@0 476
michael@0 477 static __attribute__((constructor)) void _GTMInstallLeaks(void) {
michael@0 478 BOOL checkLeaks = YES;
michael@0 479 #if !GTM_IPHONE_SDK
michael@0 480 checkLeaks = GTMIsGarbageCollectionEnabled() ? NO : YES;
michael@0 481 #endif // !GTM_IPHONE_SDK
michael@0 482 if (checkLeaks) {
michael@0 483 checkLeaks = getenv("GTM_ENABLE_LEAKS") ? YES : NO;
michael@0 484 if (checkLeaks) {
michael@0 485 // COV_NF_START
michael@0 486 // We don't have leak checking on by default, so this won't be hit.
michael@0 487 fprintf(stderr, "Leak Checking Enabled\n");
michael@0 488 fflush(stderr);
michael@0 489 int ret = atexit(&_GTMRunLeaks);
michael@0 490 // To avoid unused variable warning when _GTMDevAssert is stripped.
michael@0 491 (void)ret;
michael@0 492 _GTMDevAssert(ret == 0,
michael@0 493 @"Unable to install _GTMRunLeaks as an atexit handler (%d)",
michael@0 494 errno);
michael@0 495 // COV_NF_END
michael@0 496 }
michael@0 497 }
michael@0 498 }
michael@0 499
michael@0 500 #endif // !GTM_IPHONE_DEVICE && !GTM_SUPPRESS_RUN_LEAKS_HOOK

mercurial