Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: inquisitiveSoft/AJKSqliteDatabase
base: fd0c125058
...
head fork: inquisitiveSoft/AJKSqliteDatabase
compare: 29b8d9dd47
Checking mergeability… Don't worry, you can still create the pull request.
  • 2 commits
  • 85 files changed
  • 0 commit comments
  • 1 contributor
Commits on Oct 18, 2011
Harry Jordan Adding a - (BOOL)createTable:(NSString *)tableName withColumns:(NSDic…
…tionary *)columnsForTypes method to AJKSqliteDatabase
c69330b
Commits on Oct 24, 2011
Harry Jordan Added a -(BOOL)createTable:withColumns:error: method which returns tr…
…ue either if an there's an existing table with that name, or if a table is created. Added a AJKConvenientAdditions category to AJKSqliteDatabase.
29b8d9d
Showing with 7,380 additions and 59 deletions.
  1. +15 −18 Source/AJKResultSet.m
  2. +10 −0 Source/AJKSqliteDatabase+ConvenientAdditions.h
  3. +42 −0 Source/AJKSqliteDatabase+ConvenientAdditions.m
  4. +10 −1 Source/AJKSqliteDatabase.h
  5. +53 −17 Source/AJKSqliteDatabase.m
  6. +1 −1  Source/AJKSqliteStatement.h
  7. +2 −1  Source/AJKSqliteStatement.m
  8. +19 −9 Tests/AJKSqliteDatabaseTests.m
  9. +0 −1  Tests/GHUnit.framework/GHUnit
  10. BIN  Tests/GHUnit.framework/GHUnit
  11. +0 −1  Tests/GHUnit.framework/Headers
  12. +43 −0 Tests/GHUnit.framework/Headers/BWSplitView.h
  13. +139 −0 Tests/GHUnit.framework/Headers/GHAsyncTestCase.h
  14. +46 −0 Tests/GHUnit.framework/Headers/GHMockNSHTTPURLResponse.h
  15. +162 −0 Tests/GHUnit.framework/Headers/GHMockNSURLConnection.h
  16. +132 −0 Tests/GHUnit.framework/Headers/GHNSInvocation+Utils.h
  17. +133 −0 Tests/GHUnit.framework/Headers/GHNSInvocationProxy.h
  18. +59 −0 Tests/GHUnit.framework/Headers/GHNSLocale+Mock.h
  19. +100 −0 Tests/GHUnit.framework/Headers/GHNSObject+Invocation.h
  20. +40 −0 Tests/GHUnit.framework/Headers/GHTest+JUnitXML.h
  21. +182 −0 Tests/GHUnit.framework/Headers/GHTest.h
  22. +23 −0 Tests/GHUnit.framework/Headers/GHTestApp.h
  23. +157 −0 Tests/GHUnit.framework/Headers/GHTestCase.h
  24. +38 −0 Tests/GHUnit.framework/Headers/GHTestGroup+JUnitXML.h
  25. +151 −0 Tests/GHUnit.framework/Headers/GHTestGroup.h
  26. +1,010 −0 Tests/GHUnit.framework/Headers/GHTestMacros.h
  27. +42 −0 Tests/GHUnit.framework/Headers/GHTestOperation.h
  28. +26 −0 Tests/GHUnit.framework/Headers/GHTestOutlineViewModel.h
  29. +159 −0 Tests/GHUnit.framework/Headers/GHTestRunner.h
  30. +114 −0 Tests/GHUnit.framework/Headers/GHTestSuite.h
  31. +117 −0 Tests/GHUnit.framework/Headers/GHTestViewController.h
  32. +163 −0 Tests/GHUnit.framework/Headers/GHTestViewModel.h
  33. +45 −0 Tests/GHUnit.framework/Headers/GHTestWindowController.h
  34. +144 −0 Tests/GHUnit.framework/Headers/GHTesting.h
  35. +36 −0 Tests/GHUnit.framework/Headers/GHUNSObject+Swizzle.h
  36. +51 −0 Tests/GHUnit.framework/Headers/GHUnit.h
  37. +105 −0 Tests/GHUnit.framework/Headers/GTMStackTrace.h
  38. +88 −0 Tests/GHUnit.framework/Headers/NSException+GHTestFailureExceptions.h
  39. +67 −0 Tests/GHUnit.framework/Headers/NSValue+GHValueFormatter.h
  40. +0 −1  Tests/GHUnit.framework/Resources
  41. BIN  Tests/GHUnit.framework/Resources/English.lproj/InfoPlist.strings
  42. BIN  Tests/GHUnit.framework/Resources/GHTestApp.nib
  43. BIN  Tests/GHUnit.framework/Resources/GHTestView.nib
  44. BIN  Tests/GHUnit.framework/Resources/GHTestWindow.nib
  45. BIN  Tests/GHUnit.framework/Resources/GradientSplitViewDimpleBitmap.tif
  46. BIN  Tests/GHUnit.framework/Resources/GradientSplitViewDimpleVector.pdf
  47. +38 −0 Tests/GHUnit.framework/Resources/Info.plist
  48. +0 −1  Tests/GHUnit.framework/Versions/Current
  49. BIN  Tests/GHUnit.framework/Versions/Current/GHUnit
  50. +43 −0 Tests/GHUnit.framework/Versions/Current/Headers/BWSplitView.h
  51. +139 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHAsyncTestCase.h
  52. +46 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHMockNSHTTPURLResponse.h
  53. +162 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHMockNSURLConnection.h
  54. +132 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHNSInvocation+Utils.h
  55. +133 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHNSInvocationProxy.h
  56. +59 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHNSLocale+Mock.h
  57. +100 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHNSObject+Invocation.h
  58. +40 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTest+JUnitXML.h
  59. +182 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTest.h
  60. +23 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestApp.h
  61. +157 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestCase.h
  62. +38 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestGroup+JUnitXML.h
  63. +151 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestGroup.h
  64. +1,010 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestMacros.h
  65. +42 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestOperation.h
  66. +26 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestOutlineViewModel.h
  67. +159 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestRunner.h
  68. +114 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestSuite.h
  69. +117 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestViewController.h
  70. +163 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestViewModel.h
  71. +45 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTestWindowController.h
  72. +144 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHTesting.h
  73. +36 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHUNSObject+Swizzle.h
  74. +51 −0 Tests/GHUnit.framework/Versions/Current/Headers/GHUnit.h
  75. +105 −0 Tests/GHUnit.framework/Versions/Current/Headers/GTMStackTrace.h
  76. +88 −0 Tests/GHUnit.framework/Versions/Current/Headers/NSException+GHTestFailureExceptions.h
  77. +67 −0 Tests/GHUnit.framework/Versions/Current/Headers/NSValue+GHValueFormatter.h
  78. BIN  Tests/GHUnit.framework/Versions/Current/Resources/English.lproj/InfoPlist.strings
  79. BIN  Tests/GHUnit.framework/Versions/Current/Resources/GHTestApp.nib
  80. BIN  Tests/GHUnit.framework/Versions/Current/Resources/GHTestView.nib
  81. BIN  Tests/GHUnit.framework/Versions/Current/Resources/GHTestWindow.nib
  82. BIN  Tests/GHUnit.framework/Versions/Current/Resources/GradientSplitViewDimpleBitmap.tif
  83. BIN  Tests/GHUnit.framework/Versions/Current/Resources/GradientSplitViewDimpleVector.pdf
  84. +38 −0 Tests/GHUnit.framework/Versions/Current/Resources/Info.plist
  85. +8 −8 Tests/main.m
View
33 Source/AJKResultSet.m
@@ -55,9 +55,7 @@ - (void)readColumnNames
[columnNamesForIndexes setObject:columnIndexValue forKey:columnName];
}
}
-
- NSLog(@"columnNamesForIndexes: %@", columnNamesForIndexes);
-
+
// Assign immutable copies
self.columnNames = [columnNames copy];
self.columnNamesForIndexes = [columnNamesForIndexes copy];
@@ -102,7 +100,6 @@ - (NSArray *)resultsWithError:(NSError **)outError
- (NSDictionary *)allValuesForCurrentRowWithError:(NSError **)outError
{
NSDictionary *columnNamesForIndexes = [self columnNamesForIndexes];
- NSLog(@"columnNamesForIndexes: %@", columnNamesForIndexes);
int numberOfColumns = (int)[columnNamesForIndexes count];
if(numberOfColumns <= 0) {
@@ -220,9 +217,9 @@ - (int)indexOfColumn:(NSString *)columnName
if(columnNumber)
return [columnNumber intValue];
}
-
- NSLog(@"Couldn't find a column named '%@'.", [columnName lowercaseString]);
- return -1;
+
+ NSLog(@"Couldn't find a column named '%@'.", [columnName lowercaseString]);
+ return -1;
}
@@ -264,7 +261,7 @@ - (BOOL)columnAtIndexIsNull:(int)columnIndex {
- (BOOL)boolForColumn:(NSString *)columnName {
- return ([self int32ForColumn:columnName] > 0);
+ return ([self int32ForColumn:columnName] > 0);
}
@@ -277,15 +274,15 @@ - (NSInteger)integerForColumn:(NSString *)columnName {
- (int32_t)int32ForColumn:(NSString *)columnName {
- return [self int32ForColumnAtIndex:[self indexOfColumn:columnName]];
+ return [self int32ForColumnAtIndex:[self indexOfColumn:columnName]];
}
- (int64_t)int64ForColumn:(NSString *)columnName {
- return [self int64ForColumnAtIndex:[self indexOfColumn:columnName]];
+ return [self int64ForColumnAtIndex:[self indexOfColumn:columnName]];
}
- (double)doubleForColumn:(NSString *)columnName {
- return [self doubleForColumnAtIndex:[self indexOfColumn:columnName]];
+ return [self doubleForColumnAtIndex:[self indexOfColumn:columnName]];
}
@@ -293,7 +290,7 @@ - (double)doubleForColumn:(NSString *)columnName {
#pragma mark Accessing object values
- (NSString *)stringForColumn:(NSString *)columnName {
- return [self stringForColumnAtIndex:[self indexOfColumn:columnName]];
+ return [self stringForColumnAtIndex:[self indexOfColumn:columnName]];
}
@@ -311,12 +308,12 @@ - (NSDate *)dateForColumn:(NSString *)columnName {
}
- (NSData *)dataForColumn:(NSString *)columnName {
- return [self dataForColumnAtIndex:[self indexOfColumn:columnName]];
+ return [self dataForColumnAtIndex:[self indexOfColumn:columnName]];
}
- (id)objectForColumn:(NSString *)columnName {
- return [self objectForColumnAtIndex:[self indexOfColumn:columnName]];
+ return [self objectForColumnAtIndex:[self indexOfColumn:columnName]];
}
@@ -375,16 +372,16 @@ - (NSString *)stringForColumnAtIndex:(int)columnIndex
if(cString)
result = [NSString stringWithUTF8String:cString];
- });
-
- return result;
+ });
+
+ return result;
}
- (NSData *)dataForColumnAtIndex:(int)columnIndex {
if((columnIndex < 0) || [self columnAtIndexIsNull:columnIndex])
return nil;
-
+
__block NSMutableData *data = nil;
dispatch_sync_avoiding_deadlocks([AJKSqliteDatabase queue], ^{
int dataSize = sqlite3_column_bytes([[self statement] statementHandle], columnIndex);
View
10 Source/AJKSqliteDatabase+ConvenientAdditions.h
@@ -0,0 +1,10 @@
+#import "AJKSqliteDatabase.h"
+
+
+@interface AJKSqliteDatabase (AJKConvenientAdditions)
+
+- (NSInteger)largestIntegerForColumn:(NSString *)columnName inTable:(NSString *)tableName;
+- (double)largestDoubleForColumn:(NSString *)columnName inTable:(NSString *)tableName;
+
+
+@end
View
42 Source/AJKSqliteDatabase+ConvenientAdditions.m
@@ -0,0 +1,42 @@
+#import "AJKSqliteDatabase+ConvenientAdditions.h"
+#import "AJKResultSet.h"
+
+
+@implementation AJKSqliteDatabase (AJKConvenientAdditions)
+
+
+- (NSInteger)largestIntegerForColumn:(NSString *)columnName inTable:(NSString *)tableName
+{
+ NSError *error = nil;
+ NSString *query = [NSString stringWithFormat:@"SELECT MAX(%@) FROM %@", columnName, tableName];
+ AJKResultSet *results = [self executeQuery:query withArguments:nil error:&error];
+
+ NSInteger integerResult = 0;
+ if([results nextRow]) {
+ // Currently just assuming that the result is actually an integer
+ integerResult = [results integerForColumn:columnName];
+ } else if(error)
+ NSLog(@"Couldn't retrieve the maximum integer value for the '%@' column: %@", columnName, error);
+
+ return integerResult;
+}
+
+
+- (double)doubleForColumn:(NSString *)columnName inTable:(NSString *)tableName
+{
+ NSError *error = nil;
+ NSString *query = [NSString stringWithFormat:@"SELECT MAX(%@) FROM %@", columnName, tableName];
+ AJKResultSet *results = [self executeQuery:query withArguments:nil error:&error];
+
+ double doubleResult = 0;
+ if([results nextRow]) {
+ // Currently just assuming that the result is actually an integer
+ doubleResult = [results doubleForColumn:columnName];
+ } else if(error)
+ NSLog(@"Couldn't retrieve the maximum double value for the '%@' column in the '%@' table: %@", columnName, tableName, error);
+
+ return doubleResult;
+}
+
+
+@end
View
11 Source/AJKSqliteDatabase.h
@@ -25,13 +25,16 @@ extern NSString *const AJKSqliteDatabaseError;
- (AJKResultSet *)executeQuery:(NSString *)query;
- (AJKResultSet *)executeQuery:(NSString *)query withArguments:(NSArray *)arguments error:(NSError **)outError;
+// Manage tables
+- (BOOL)createTable:(NSString *)tableName withColumns:(NSDictionary *)columnsForTypes error:(NSError **)outError;
+
//
- (BOOL)rollback;
- (BOOL)commit;
- (BOOL)beginTransaction;
- (BOOL)beginDeferredTransaction;
-// querying the status of the database
+// Querying the status of the database
- (int64_t)identifierOfLastInsert;
- (int)numberOfChanges;
- (BOOL)hasEncounteredError;
@@ -41,6 +44,12 @@ extern NSString *const AJKSqliteDatabaseError;
// Manage cached objects
- (void)removeUnusedCachedStatements;
+
+// Treat these methods as largely private, you should only be used to creat more efficient execute... style methods
+- (AJKSqliteStatement *)statementForQuery:(NSString *)query error:(NSError **)outError;
+- (void)bindObject:(id)objectToBind toColumn:(int)columnIndex inStatement:(sqlite3_stmt *)statement;
+- (void)addResultSet:(AJKResultSet *)resultSet;
+
// Treat these methods as private, only AJKSqliteStatement and AJKResultSet should call these methods
- (void)decrementUsageOfStatement:(AJKSqliteStatement *)statement;
- (void)finishedUsingResultSet:(AJKResultSet *)resultSet;
View
70 Source/AJKSqliteDatabase.m
@@ -13,12 +13,6 @@ @interface AJKSqliteDatabase () {
NSMutableDictionary *cachedStatements_, *activeResultsSets_;
}
-- (AJKSqliteStatement *)statementForquery:(NSString *)query error:(NSError **)outError;
-- (void)bindObject:(id)objectToBind toColumn:(int)columnIndex inStatement:(sqlite3_stmt *)statement;
-
-// Managing result sets
-- (void)addResultSet:(AJKResultSet *)resultSet;
-
@end
@@ -83,7 +77,7 @@ - (BOOL)executeUpdate:(NSString *)query;
- (BOOL)executeUpdate:(NSString *)query withArguments:(NSArray *)arguments error:(NSError **)outError
{
- AJKSqliteStatement *statement = [self statementForquery:query error:outError];
+ AJKSqliteStatement *statement = [self statementForQuery:query error:outError];
if(!statement)
return NO;
@@ -143,7 +137,7 @@ - (BOOL)executeUpdate:(NSString *)query withArguments:(NSArray *)arguments error
return TRUE;
if(outError != NULL) {
- NSString *errorDescription = [NSString stringWithFormat:@"Encountered an error while calling sqlite3_step() foe the '%@' query,: %d %@", query, result, [self lastErrorMessage]];
+ NSString *errorDescription = [NSString stringWithFormat:@"Encountered an error while calling sqlite3_step() for the '%@' query,: %d %@", query, result, [self lastErrorMessage]];
*outError = [NSError errorWithDomain:AJKSqliteDatabaseError code:result userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
errorDescription, NSLocalizedDescriptionKey,
nil]];
@@ -161,7 +155,7 @@ - (AJKResultSet *)executeQuery:(NSString *)query
- (AJKResultSet *)executeQuery:(NSString *)query withArguments:(NSArray *)arguments error:(NSError **)outError
{
- AJKSqliteStatement *statement = [self statementForquery:query error:outError];
+ AJKSqliteStatement *statement = [self statementForQuery:query error:outError];
if(!statement)
return nil;
@@ -249,12 +243,10 @@ - (void)bindObject:(id)objectToBind toColumn:(int)columnIndex inStatement:(sqlit
}
-
-
#pragma mark -
#pragma mark Manage statements
-- (AJKSqliteStatement *)statementForquery:(NSString *)query error:(NSError **)outError {
+- (AJKSqliteStatement *)statementForQuery:(NSString *)query error:(NSError **)outError {
// Look for an existing statement matching the query
__block AJKSqliteStatement *statement = nil;
@synchronized(self) {
@@ -287,7 +279,7 @@ - (AJKSqliteStatement *)statementForquery:(NSString *)query error:(NSError **)ou
if(result == SQLITE_OK) {
// AJKSqliteStatement will call sqlite3_finalize() with the statement handle when it's dealloced
- statement = [[AJKSqliteStatement alloc] initForquery:query withHandle:statementHandle];
+ statement = [[AJKSqliteStatement alloc] initForQuery:query withHandle:statementHandle];
[statement incrementUseCount];
@synchronized(self) {
@@ -370,7 +362,51 @@ - (void)finishedUsingResultSet:(AJKResultSet *)resultSet
#pragma mark -
-#pragma mark querying the status of the database
+#pragma mark Manage tables
+
+- (BOOL)createTable:(NSString *)tableName withColumns:(NSDictionary *)columnsForTypes error:(NSError **)outError
+{
+ if(!tableName || ([tableName length] <= 0)) {
+ if(outError != NULL) {
+ NSDictionary *errorDictionary = [NSDictionary dictionaryWithObject:NSLocalizedString(@"Tried to create a table without a name", @"Sqlite related error") forKey:NSLocalizedDescriptionKey];
+ *outError = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:errorDictionary];
+ }
+
+ return FALSE;
+ }
+
+ // Search in sqlite_master table if table exists
+ AJKResultSet *resultSet = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?" withArguments:[NSArray arrayWithObject:[tableName lowercaseString]] error:nil];
+ BOOL foundTable = [resultSet nextRow];
+ [resultSet close];
+
+
+ // Create the table if an existing one couldn't be found
+ if(!foundTable) {
+ NSMutableString *columns = [[NSMutableString alloc] init];
+ __block BOOL firstColumn = TRUE;
+ [columnsForTypes enumerateKeysAndObjectsUsingBlock:^ (id columnName, id type, BOOL *stop) {
+ if(firstColumn)
+ [columns appendFormat:@"%@ %@", columnName, type];
+ else
+ [columns appendFormat:@", %@ %@", columnName, type];
+
+ firstColumn = FALSE;
+ }];
+
+ return [self executeUpdate:[NSString stringWithFormat:@"create table %@ (%@)", tableName, columns] withArguments:nil error:outError];
+ }
+
+
+ // Check that the table contains the expected columns ??
+
+
+ return TRUE;
+}
+
+
+#pragma mark -
+#pragma mark Querying the status of the database
- (int64_t)identifierOfLastInsert {
@@ -424,7 +460,7 @@ - (NSString *)lastErrorMessage {
#pragma mark Help methods
-- (BOOL)validateSQL:(NSString *)sqlquery error:(NSError **)outError
+- (BOOL)validateSQL:(NSString *)sqlQuery error:(NSError **)outError
{
int numberOfAttemptsToTry = [self numberOfAttemptsToTry];
__block int result = 0;
@@ -434,7 +470,7 @@ - (BOOL)validateSQL:(NSString *)sqlquery error:(NSError **)outError
sqlite3_stmt *statementHandle = NULL;
do {
- result = sqlite3_prepare_v2(database, [sqlquery UTF8String], -1, &statementHandle, 0);
+ result = sqlite3_prepare_v2(database, [sqlQuery UTF8String], -1, &statementHandle, 0);
if(result == SQLITE_BUSY || result == SQLITE_LOCKED) {
usleep(20);
@@ -450,7 +486,7 @@ - (BOOL)validateSQL:(NSString *)sqlquery error:(NSError **)outError
if(result == SQLITE_OK)
return TRUE;
else if(result == SQLITE_BUSY || result == SQLITE_LOCKED)
- NSLog(@"Couldn't validate the '%@' query because the '%@' database was busy", sqlquery, [[self databaseURL] path]);
+ NSLog(@"Couldn't validate the '%@' query because the '%@' database was busy", sqlQuery, [[self databaseURL] path]);
if(outError != NULL) {
NSDictionary *errorDictionary = [NSDictionary dictionaryWithObject:[self lastErrorMessage] forKey:NSLocalizedDescriptionKey];
View
2  Source/AJKSqliteStatement.h
@@ -8,7 +8,7 @@
@property (copy, readonly) NSString *query;
@property (assign, readonly) sqlite3_stmt *statementHandle;
-- (id)initForquery:(NSString *)query withHandle:(sqlite3_stmt *)statementHandle;
+- (id)initForQuery:(NSString *)query withHandle:(sqlite3_stmt *)statementHandle;
- (void)reset;
- (int32_t)useCount;
View
3  Source/AJKSqliteStatement.m
@@ -2,6 +2,7 @@
#import "AJKSqliteDatabase.h"
#import "AJKBlockFunctions.h"
+#import "libkern/OSAtomic.h"
@interface AJKSqliteStatement () {
@@ -23,7 +24,7 @@ @implementation AJKSqliteStatement
@synthesize query = query_, statementHandle = statementHandle_;
-- (id)initForquery:(NSString *)query withHandle:(sqlite3_stmt *)statementHandle
+- (id)initForQuery:(NSString *)query withHandle:(sqlite3_stmt *)statementHandle
{
self = [super init];
View
28 Tests/AJKSqliteDatabaseTests.m
@@ -56,7 +56,7 @@ - (void)testBadUpdate {
- (void)testCreateTable {
NSError *error = nil;
- BOOL success = [database executeUpdate:@"create table trees (name text, description text, awesomeness integer, height double, age double)" withArguments:nil error:&error];
+ BOOL success = [database executeUpdate:@"CREATE TABLE trees (name text, description text, awesomeness integer, height double, age double)" withArguments:nil error:&error];
if(!success) {
GHFail(@"Couldn't create table: %@", error);
}
@@ -64,8 +64,8 @@ - (void)testCreateTable {
- (void)testRecreateTableWithIdenticalName {
NSError *error = nil;
- BOOL success = [database executeUpdate:@"create table trees (name text, seed text)" withArguments:nil error:&error];
- GHAssertFalse(success, @"Shouldn't be able to create table: %@", error);
+ BOOL success = [database executeUpdate:@"CREATE TABLE trees (name text, seed text)" withArguments:nil error:&error];
+ GHAssertFalse(success, @"Shouldn't be able to create a table with an identical name: %@", error);
GHTestLog(@"%@", error);
}
@@ -84,16 +84,16 @@ - (void)testBasicInsertingAndSelecting {
nil];
GHTestLog(@"Input: %@\n\n", inputArguments);
- BOOL result = [database executeUpdate:@"insert into trees (name, description, awesomeness, height, age) values (?, ?, ?, ?, ?)" withArguments:inputArguments error:&error];
+ BOOL result = [database executeUpdate:@"INSERT INTO trees (name, description, awesomeness, height, age) VALUES (?, ?, ?, ?, ?)" withArguments:inputArguments error:&error];
GHAssertTrue(result, @"Failed to insert a row into the trees table");
error = nil;
- AJKResultSet *resultSet = [database executeQuery:@"select * from trees" withArguments:nil error:&error];
- GHAssertTrue((BOOL)resultSet, @"select * from trees, failed, %@", error);
+ AJKResultSet *resultSet = [database executeQuery:@"SELECT * FROM trees" withArguments:nil error:&error];
+ GHAssertTrue((BOOL)resultSet, @"SELECT * FROM trees, failed, %@", error);
error = nil;
NSArray *results = [resultSet resultsWithError:&error];
- GHAssertTrue([results count] == 1, @"select * from trees, should return one result rather than '%d', %@", [results count], error);
+ GHAssertTrue([results count] == 1, @"SELECT * FROM trees, should return one result rather than '%d', %@", [results count], error);
GHTestLog(@"Output:\n%@\n\n", results);
NSString *resultName = [resultSet stringForColumn:@"name"];
@@ -107,7 +107,18 @@ - (void)testBasicInsertingAndSelecting {
}
-
+- (void)testCreatingAnotherTable {
+ NSError *error = nil;
+ BOOL success = [database createTable:@"measurements" withColumns:[NSDictionary dictionaryWithObjectsAndKeys:
+ @"integer", @"identifier",
+ @"double", @"timestamp",
+ @"double", @"x",
+ @"double", @"y",
+ @"double", @"z",
+ nil] error:&error];
+
+ GHAssertTrue(success, @"Couldn't create a table using the -createTable:withColumns: method: %@", error);
+}
- (void)testRemoveTemporaryDatabase {
@@ -118,5 +129,4 @@ - (void)testRemoveTemporaryDatabase {
}
-
@end
View
1  Tests/GHUnit.framework/GHUnit
View
BIN  Tests/GHUnit.framework/GHUnit
Binary file not shown
View
1  Tests/GHUnit.framework/Headers
View
43 Tests/GHUnit.framework/Headers/BWSplitView.h
@@ -0,0 +1,43 @@
+//
+// BWSplitView.h
+// BWToolkit
+//
+// Created by Brandon Walkin (www.brandonwalkin.com) and Fraser Kuyvenhoven.
+// All code is provided under the New BSD license.
+//
+
+#import <Cocoa/Cocoa.h>
+
+@interface BWSplitView : NSSplitView {
+ NSColor *color;
+ BOOL colorIsEnabled, checkboxIsEnabled, dividerCanCollapse, collapsibleSubviewCollapsed;
+ id secondaryDelegate;
+ NSMutableDictionary *minValues, *maxValues, *minUnits, *maxUnits;
+ NSMutableDictionary *resizableSubviewPreferredProportion, *nonresizableSubviewPreferredSize;
+ NSArray *stateForLastPreferredCalculations;
+ int collapsiblePopupSelection;
+ float uncollapsedSize;
+
+ // Collapse button
+ NSButton *toggleCollapseButton;
+ BOOL isAnimating;
+}
+
+@property (retain) NSMutableDictionary *minValues, *maxValues, *minUnits, *maxUnits;
+@property (retain) NSMutableDictionary *resizableSubviewPreferredProportion, *nonresizableSubviewPreferredSize;
+@property (retain) NSArray *stateForLastPreferredCalculations;
+@property (retain) NSButton *toggleCollapseButton;
+@property BOOL collapsibleSubviewCollapsed;
+@property int collapsiblePopupSelection;
+@property BOOL dividerCanCollapse;
+
+// The split view divider color
+@property (copy) NSColor *color;
+
+// Flag for whether a custom divider color is enabled. If not, the standard divider color is used.
+@property BOOL colorIsEnabled;
+
+// Call this method to collapse or expand a subview configured as collapsible in the IB inspector.
+- (IBAction)toggleCollapse:(id)sender;
+
+@end
View
139 Tests/GHUnit.framework/Headers/GHAsyncTestCase.h
@@ -0,0 +1,139 @@
+//
+// GHAsyncTestCase.h
+// GHUnit
+//
+// Created by Gabriel Handford on 4/8/09.
+// Copyright 2009. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "GHTestCase.h"
+
+// Some default statuses to use; Or define and use your own
+enum {
+ kGHUnitWaitStatusUnknown = 0,
+ kGHUnitWaitStatusSuccess,
+ kGHUnitWaitStatusFailure,
+ kGHUnitWaitStatusCancelled
+};
+
+/*!
+ Asynchronous test case with wait and notify.
+
+ If notify occurs before wait has started (if it was a synchronous call), this test
+ case will still work.
+
+ Be sure to call prepare before the asynchronous method (otherwise an exception will raise).
+
+ @code
+ - (void)testSuccess {
+ [self prepare];
+
+ // Do asynchronous task here
+ [self performSelector:@selector(_succeed) withObject:nil afterDelay:0.1];
+
+ [self waitForStatus:kGHUnitWaitStatusSuccess timeout:1.0];
+ }
+
+ - (void)_succeed {
+ // Notice the forSelector points to the test above. This is so that
+ // stray notifies don't error or falsely succeed other tests.
+ // To ignore the check, forSelector can be NULL.
+ [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testSuccess)];
+ }
+ @endcode
+ */
+@interface GHAsyncTestCase : GHTestCase {
+
+ NSInteger waitForStatus_;
+ NSInteger notifiedStatus_;
+
+ BOOL prepared_; // Whether prepared was called before waitForStatus:timeout:
+ NSRecursiveLock *lock_; // Lock to synchronize on
+ SEL waitSelector_; // The selector we are waiting on
+
+ NSArray *_runLoopModes;
+}
+
+/*!
+ Run loop modes to run while waiting;
+ Defaults to NSDefaultRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode
+ */
+@property (retain, nonatomic) NSArray *runLoopModes;
+
+/*!
+ Prepare before calling the asynchronous method.
+ */
+- (void)prepare;
+
+/*!
+ Prepare and specify the selector we will use in notify.
+
+ @param selector
+ */
+- (void)prepare:(SEL)selector;
+
+/*!
+ Wait for notification of status or timeout.
+
+ Be sure to prepare before calling your asynchronous method.
+ For example,
+
+ @code
+ - (void)testFoo {
+ [self prepare];
+ // Do asynchronous task here
+ [self waitForStatus:kGHUnitWaitStatusSuccess timeout:1.0];
+ }
+ @endcode
+
+ @param status kGHUnitWaitStatusSuccess, kGHUnitWaitStatusFailure or custom status
+ @param timeout Timeout in seconds
+ */
+- (void)waitForStatus:(NSInteger)status timeout:(NSTimeInterval)timeout;
+
+// Deprecated
+- (void)waitFor:(NSInteger)status timeout:(NSTimeInterval)timeout;
+
+/*!
+ Wait for timeout to occur.
+ Fails if we did _NOT_ timeout.
+ @param timeout
+ */
+- (void)waitForTimeout:(NSTimeInterval)timeout;
+
+/*!
+ Notify waiting of status for test selector.
+ @param status Status, for example, kGHUnitWaitStatusSuccess
+ @param selector If not NULL, then will verify this selector is where we are waiting.
+ This prevents stray asynchronous callbacks to fail a later test
+ */
+- (void)notify:(NSInteger)status forSelector:(SEL)selector;
+
+/*!
+ Notify waiting of status for any selector.
+ @param status Status, for example, kGHUnitWaitStatusSuccess
+ */
+- (void)notify:(NSInteger)status;
+
+@end
View
46 Tests/GHUnit.framework/Headers/GHMockNSHTTPURLResponse.h
@@ -0,0 +1,46 @@
+//
+// GHMockNSHTTPURLResponse.h
+// GHUnit
+//
+// Created by Gabriel Handford on 4/9/09.
+// Copyright 2009. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+/*!
+ NSHTTPURLResponse for use with mocking.
+ Allows us to manually set the status code and headers in the response.
+ */
+@interface GHMockNSHTTPURLResponse : NSHTTPURLResponse {
+ NSInteger statusCode_;
+ NSDictionary *headers_;
+}
+
+- (id)initWithStatusCode:(NSInteger)statusCode headers:(NSDictionary *)headers;
+
+- (void)setStatusCode:(NSInteger)code;
+- (void)setHeaders:(NSDictionary *)headers;
+
+@end
View
162 Tests/GHUnit.framework/Headers/GHMockNSURLConnection.h
@@ -0,0 +1,162 @@
+//
+// GHMockNSURLConnection.h
+// GHUnit
+//
+// Created by Gabriel Handford on 4/9/09.
+// Copyright 2009. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+extern NSString *const GHMockNSURLConnectionException;
+
+/*!
+ NSURLConnection for mocking.
+
+ Use with GHAsyncTestCase to mock out connections.
+
+ @code
+
+ @interface GHNSURLConnectionMockTest : GHAsyncTestCase {}
+ @end
+
+ @implementation GHNSURLConnectionMockTest
+
+ - (void)testMock {
+ [self prepare];
+ GHMockNSURLConnection *connection = [[GHMockNSURLConnection alloc] initWithRequest:nil delegate:self];
+ [connection receiveHTTPResponseWithStatusCode:204 headers:testHeaders_ afterDelay:0.1];
+ [connection receiveData:testData_ afterDelay:0.2];
+ [connection finishAfterDelay:0.3];
+ [self waitForStatus:kGHUnitWaitStatusSuccess timeout:1.0];
+ }
+
+ - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
+ GHAssertEquals([(NSHTTPURLResponse *)response statusCode], 204, nil);
+ GHAssertEqualObjects([(NSHTTPURLResponse *)response allHeaderFields], testHeaders_, nil);
+ }
+
+ - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ GHAssertEqualObjects(data, testData_, nil);
+ }
+
+ - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+ [self notify:kGHUnitWaitStatusSuccess forSelector:@selector(testMock)];
+ }
+ @end
+
+ @endcode
+ */
+@interface GHMockNSURLConnection : NSObject {
+ NSURLRequest *request_;
+ id delegate_; // weak
+
+ BOOL cancelled_;
+ BOOL started_;
+}
+
+@property (readonly, nonatomic, getter=isStarted) BOOL started;
+@property (readonly, nonatomic, getter=isCancelled) BOOL cancelled;
+
+// Mocked version of NSURLConnection#initWithRequest:delegate:
+- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate;
+
+// Mocked version of NSURLConnection#initWithRequest:delegate:startImmediately:
+- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately;
+
+// Mocked version of NSURLConnection#scheduleInRunLoop:forMode: (NOOP)
+- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
+
+// Mocked version of NSURLConnection#start (NOOP)
+- (void)start;
+
+/*!
+ Send generic response to delegate after delay.
+ (For asynchronous requests)
+ @param delay Delay in seconds (if < 0, there is no delay)
+ */
+- (void)receiveResponse:(NSURLResponse *)response afterDelay:(NSTimeInterval)delay;
+
+/*!
+ Send HTTP response to delegate with status code, headers, after delay.
+ This is only the HTTP response (and not data or finished).
+ (For asynchronous requests)
+ @param statusCode HTTP status code
+ @param headers Headers
+ @param delay Delay in seconds (if < 0, there is no delay)
+ */
+- (void)receiveHTTPResponseWithStatusCode:(int)statusCode headers:(NSDictionary *)headers afterDelay:(NSTimeInterval)delay;
+
+/*!
+ Send data to connection delegate after delay.
+ @param data Data to send
+ @param delay Delay in seconds
+ */
+- (void)receiveData:(NSData *)data afterDelay:(NSTimeInterval)delay;
+
+- (void)receiveData:(NSData *)data statusCode:(NSInteger)statusCode MIMEType:(NSString *)MIMEType afterDelay:(NSTimeInterval)delay;
+
+/*!
+ Send data (from file in bundle resource) to connection delegate after delay.
+ (For asynchronous requests)
+ @param path Path to file
+ @param delay Delay in seconds
+ */
+- (void)receiveDataFromPath:(NSString *)path afterDelay:(NSTimeInterval)delay;
+
+/*!
+ Calls connectionDidFinish: delegate after delay.
+ (For asynchronous requests)
+ @param delay Delay in seconds (if < 0, there is no delay)
+ */
+- (void)finishAfterDelay:(NSTimeInterval)delay;
+
+/*!
+ Sends mock response, sends data, and then calls finish.
+ (For asynchronous requests)
+ @param path Path to load data from. File should be available in Test target (bundle)
+ @param statusCode Status code for response
+ @param MIMEType Content type for response header
+ @param afterDelay Delay before responding (if < 0, there is no delay)
+ */
+- (void)receiveFromPath:(NSString *)path statusCode:(NSInteger)statusCode MIMEType:(NSString *)MIMEType afterDelay:(NSTimeInterval)delay;
+
+/*!
+ Sends mock response, sends data, and then calls finish.
+ (For asynchronous requests)
+ @param data Data to load. File should be available in Test target (bundle)
+ @param statusCode Status code for response
+ @param MIMEType Content type for response header
+ @param afterDelay Delay before responding (if < 0, there is no delay)
+ */
+- (void)receiveData:(NSData *)data statusCode:(NSInteger)statusCode MIMEType:(NSString *)MIMEType afterDelay:(NSTimeInterval)delay;
+
+/*!
+ Calls connection:didFailWithError: on delegate after specified delay.
+ @param error The error to pass to the delegate.
+ @param delay Delay before responding (if < 0, there is no delay)
+ */
+- (void)failWithError:(NSError *)error afterDelay:(NSTimeInterval)delay;
+
+@end
View
132 Tests/GHUnit.framework/Headers/GHNSInvocation+Utils.h
@@ -0,0 +1,132 @@
+//
+// GHNSInvocation+Utils.h
+// GHKit
+//
+// Created by Gabriel Handford on 1/17/09.
+// Copyright 2009. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+//
+// Creates arguments NSArray from var args, with first object named 'object'
+// - (void)methodName:(id)arg1 withObjects:object, ...
+//
+#define GHConvertVarArgs(object) \
+NSMutableArray *arguments = [NSMutableArray array]; \
+do { \
+id arg; \
+va_list args; \
+if (object) { \
+[arguments addObject:object]; \
+va_start(args, object); \
+while ((arg = va_arg(args, id))) \
+[arguments addObject:arg]; \
+va_end(args); \
+} \
+} while(0);
+
+@interface NSInvocation (GHUtils_GHUNIT)
+
+/*!
+ Invoke on main thread.
+ @param waitUntilDone Whether to join on the call
+ */
+- (void)ghu_invokeOnMainThread:(BOOL)waitUntilDone;
+
+/*!
+ Invoke target selector with multiple arguments.
+ @param target Invocation target
+ @param selector Method
+ @param withObjects (Variable) Arguments list
+ */
++ (id)ghu_invokeWithTarget:(id)target selector:(SEL)selector withObjects:object, ...;
+
+/*!
+ Invoke target selector with multiple arguments.
+ @param target Invocation target
+ @param selector Method
+ @param arguments Arguments list
+ */
++ (id)ghu_invokeWithTarget:(id)target selector:(SEL)selector arguments:(NSArray *)arguments;
+
+/*!
+ Invoke target selector with multiple arguments.
+ @param target Invocation target
+ @param selector Method
+ @param afterDelay Time interval for delay (in seconds)
+ @param arguments Arguments list
+ */
++ (id)ghu_invokeWithTarget:(id)target selector:(SEL)selector afterDelay:(NSTimeInterval)delay arguments:(NSArray *)arguments;
+
+/*!
+ Invoke target selector on main thread with multiple arguments.
+ Use [NSNull null] for nil arguments.
+ @param target Target
+ @param selector Action
+ @param waitUntilDone Whether to wait for call to finish
+ @param withObjects Nil terminated list of (object) arguments; Use [NSNull null] for nil arguments
+ */
++ (void)ghu_invokeTargetOnMainThread:(id)target selector:(SEL)selector waitUntilDone:(BOOL)waitUntilDone withObjects:object, ...;
+
+/*!
+ Invoke target selector on main thread with multiple arguments.
+ @param target Target
+ @param selector Action
+ @param waitUntilDone Whether to wait for call to finish
+ @param arguments Arguments list
+ */
++ (void)ghu_invokeTargetOnMainThread:(id)target selector:(SEL)selector waitUntilDone:(BOOL)waitUntilDone arguments:(NSArray *)arguments;
+
+/*!
+Invoke target selector on main thread with multiple arguments.
+ @param target Target
+ @param selector Action
+ @param waitUntilDone Whether to wait for call to finish
+ @param afterDelay Time interval for delay (in seconds)
+ @param arguments Arguments list
+ */
++ (void)ghu_invokeTargetOnMainThread:(id)target selector:(SEL)selector waitUntilDone:(BOOL)waitUntilDone afterDelay:(NSTimeInterval)delay arguments:(NSArray *)arguments;
+
+/*!
+ Create invocation with variable arguments.
+ Use [NSNull null] for nil arguments.
+ @param target Invocation target
+ @param selector Method
+ @param hasReturnValue Will be set to YES, if there is a return value
+ @param withObjects (Variable) Arguments list
+ */
++ (NSInvocation *)ghu_invocationWithTarget:(id)target selector:(SEL)selector hasReturnValue:(BOOL *)hasReturnValue withObjects:object, ...;
+
+/*!
+ Create invocation with variable arguments.
+ Use [NSNull null] for nil arguments.
+ @param target Invocation target
+ @param selector Method
+ @param hasReturnValue Will be set to YES, if there is a return value
+ @param arguments Arguments array
+ */
++ (NSInvocation *)ghu_invocationWithTarget:target selector:(SEL)selector hasReturnValue:(BOOL *)hasReturnValue arguments:(NSArray *)arguments;
+
+@end
View
133 Tests/GHUnit.framework/Headers/GHNSInvocationProxy.h
@@ -0,0 +1,133 @@
+//
+// GHNSInvocationProxy_GHUNIT.h
+// GHKit
+//
+// Modified by Gabriel Handford on 5/9/09.
+// This class is based on DDInvocationGrabber.
+//
+
+/*
+ * Copyright (c) 2007-2009 Dave Dribin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+/*
+ * This class is based on CInvocationGrabber:
+ *
+ * Copyright (c) 2007, Toxic Software
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the Toxic Software nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*!
+ Proxy that allows invocation on a separate thread, or with a delay.
+
+ Use with the GHNSObject+Invocation category:
+
+ @code
+ NSMutableArray *array = ...;
+ // Adds object to array after 5 seconds
+ [[array ghu_proxyAfterDelay:5.0] addObject:@"test"];
+
+
+ NSThread *thread = ...
+ // Remove all objects from another thread
+ [[array ghu_proxyOnThread:thread waitUntilDone:NO] removeAllObjects];
+ @endcode
+
+ Create invocation proxy for a NSMutableArray.
+
+ @code
+ NSMutableArray *array = ...;
+ NSThread *thread = ...
+
+ GHNSInvocationProxy_GHUNIT *arrayProxy = [GHNSInvocationProxy_GHUNIT invocation];
+ arrayProxy.target = array;
+ arrayProxy.thread = thread;
+ arrayProxy.waitUntilDone = NO;
+
+ // Performs method on thread and doesn't wait for return
+ [arrayProxy addObject:@"test"];
+ @endcode
+ */
+@interface GHNSInvocationProxy_GHUNIT : NSProxy {
+
+ id target_;
+
+ NSThread *thread_;
+ BOOL waitUntilDone_;
+ NSTimeInterval delay_;
+
+ // If debuging time to set
+ NSTimeInterval *time_;
+
+ NSInvocation *invocation_;
+}
+
+@property (retain, nonatomic) id target;
+@property (retain, nonatomic) NSInvocation *invocation;
+@property (retain, nonatomic) NSThread *thread;
+@property (assign, nonatomic) BOOL waitUntilDone;
+@property (assign, nonatomic) NSTimeInterval delay;
+@property (assign, nonatomic) NSTimeInterval *time;
+
+/*!
+ Create autoreleased empty invocation proxy.
+ @result Invocation proxy
+ */
++ (id)invocation;
+
+/*!
+ Create invocation proxy with target.
+ @param target
+ @result Invocation proxy
+ */
+- (id)prepareWithInvocationTarget:(id)target;
+
+@end
View
59 Tests/GHUnit.framework/Headers/GHNSLocale+Mock.h
@@ -0,0 +1,59 @@
+//
+// GHNSLocale+Mock.h
+// GHUnit
+//
+// Created by Gabriel Handford on 4/13/09.
+// Copyright 2009. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+/*!
+ Category for overriding the current locale at runtime.
+
+ @code
+ #import "GHNSLocale+Mock.h"
+ // This aliases the currentLocale method and with the specified locale identifier
+ [NSLocale gh_setLocaleIdentifier:@"en_GB"];
+
+ [[NSLocale currentLocale] localeIdentifier] == "en_GB"
+ @endcode
+ */
+@interface NSLocale (GHMock)
+
++ (void)gh_setLocaleIdentifier:(NSString *)localeIdentifier;
+
+/*!
+ Aliases to currentLocale with locale set from gh_setLocaleIdentifier.
+ If not set, defaults to NSLocale with identifier en_US.
+ */
++ (NSLocale *)gh_currentLocale;
+
++ (void)gh_setPreferredLanguages:(NSArray *)preferredLanguages;
+
+/*!
+ Aliases to preferredLanguages set from gh_setPreferredLanguages.
+ If not set, defaults to [@"en"].
+ */
++ (NSArray *)gh_preferredLanguages;
+
+@end
View
100 Tests/GHUnit.framework/Headers/GHNSObject+Invocation.h
@@ -0,0 +1,100 @@
+//
+// GHNSObject+Invocation.h
+// GHKit
+//
+// Created by Gabriel Handford on 1/18/09.
+// Copyright 2009. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "GHNSInvocation+Utils.h"
+#import "GHNSInvocationProxy.h"
+
+/*!
+ Adds performSelector methods that take a nil-terminated variable argument list,
+ for when you need to pass more arguments to performSelector.
+ */
+@interface NSObject (GHInvocation_GHUNIT)
+
+/*!
+ Perform selector if responds.
+ @param selector
+ @result nil if we don't respond to the selector, otherwise the selector result
+ */
+- (id)ghu_performIfRespondsToSelector:(SEL)selector;
+
+/*!
+ Perform selector if responds with multiple arguments.
+ @param selector
+ @param withObjects nil terminated variable argument list
+ @result nil if we don't respond to the selector, otherwise the selector result
+ */
+- (id)ghu_performIfRespondsToSelector:(SEL)selector withObjects:object, ...;
+
+/*!
+ Invoke selector with arguments.
+ @param selector
+ @param withObjects nil terminated variable argument list
+ */
+- (id)ghu_performSelector:(SEL)selector withObjects:object, ...;
+
+- (id)ghu_performSelector:(SEL)selector afterDelay:(NSTimeInterval)delay withObjects:object, ...;
+
+/*!
+ Invoke selector with arguments on main thread.
+ Does not wait until selector is finished.
+ @param selector
+ @param withObjects nil terminated variable argument list
+ */
+- (void)ghu_performSelectorOnMainThread:(SEL)selector withObjects:object, ...;
+
+/*!
+ Invoke selector with arguments on main thread.
+ @param selector
+ @param waitUntilDone Whether to join on selector and wait for it to finish.
+ @param withObjects nil terminated variable argument list
+ */
+- (void)ghu_performSelectorOnMainThread:(SEL)selector waitUntilDone:(BOOL)waitUntilDone withObjects:object, ...;
+
+
+- (void)ghu_performSelector:(SEL)selector onMainThread:(BOOL)onMainThread waitUntilDone:(BOOL)waitUntilDone withObjects:object, ...;
+
+- (void)ghu_performSelector:(SEL)selector onMainThread:(BOOL)onMainThread waitUntilDone:(BOOL)waitUntilDone arguments:(NSArray *)arguments;
+
+- (void)ghu_performSelector:(SEL)selector onMainThread:(BOOL)onMainThread waitUntilDone:(BOOL)waitUntilDone
+ afterDelay:(NSTimeInterval)delay arguments:(NSArray *)arguments;
+
+
+// Invocation proxies
+
+- (id)ghu_proxyOnMainThread;
+- (id)ghu_proxyOnMainThread:(BOOL)waitUntilDone;
+- (id)ghu_proxyOnThread:(NSThread *)thread;
+- (id)ghu_proxyOnThread:(NSThread *)thread waitUntilDone:(BOOL)waitUntilDone;
+- (id)ghu_proxyAfterDelay:(NSTimeInterval)delay;
+
+// Debug proxies
+- (id)ghu_timedProxy:(NSTimeInterval *)time;
+- (id)ghu_debugProxy:(NSTimeInterval *)time proxy:(GHNSInvocationProxy_GHUNIT **)proxy;
+
+@end
View
40 Tests/GHUnit.framework/Headers/GHTest+JUnitXML.h
@@ -0,0 +1,40 @@
+//
+// GHTest+JUnitXML.h
+// GHUnit
+//
+// Created by Gabriel Handford on 6/4/10.
+// Copyright 2010. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "GHTest.h"
+
+@interface GHTest (JUnitXML)
+
+/*!
+ Return test results in JUnit XML format for external parsing use
+ (such as a Continuous Integration system like Hudson)
+ */
+- (NSString *)JUnitXML;
+
+@end
View
182 Tests/GHUnit.framework/Headers/GHTest.h
@@ -0,0 +1,182 @@
+//
+// GHTest.h
+// GHKit
+//
+// Created by Gabriel Handford on 1/18/09.
+// Copyright 2009. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+/*!
+ Test status.
+ */
+typedef enum {
+ GHTestStatusNone = 0,
+ GHTestStatusRunning, // Test is running
+ GHTestStatusCancelling, // Test is being cancelled
+ GHTestStatusCancelled, // Test was cancelled
+ GHTestStatusSucceeded, // Test finished and succeeded
+ GHTestStatusErrored, // Test finished and errored
+} GHTestStatus;
+
+enum {
+ GHTestOptionReraiseExceptions = 1 << 0, // Allows exceptions to be raised (so you can trigger the debugger)
+ GHTestOptionForceSetUpTearDownClass = 1 << 1, // Runs setUpClass/tearDownClass for this (each) test; Used when re-running a single test in a group
+};
+typedef NSInteger GHTestOptions;
+
+/*!
+ Generate string from GHTestStatus
+ @param status
+ */
+extern NSString* NSStringFromGHTestStatus(GHTestStatus status);
+
+/*!
+ Check if test is running (or trying to cancel).
+ */
+extern BOOL GHTestStatusIsRunning(GHTestStatus status);
+
+/*!
+ Check if test has succeeded, errored or cancelled.
+ */
+extern BOOL GHTestStatusEnded(GHTestStatus status);
+
+/*!
+ Test stats.
+ */
+typedef struct {
+ NSInteger succeedCount; // Number of succeeded tests
+ NSInteger failureCount; // Number of failed tests
+ NSInteger cancelCount; // Number of aborted tests
+ NSInteger testCount; // Total number of tests
+} GHTestStats;
+
+/*!
+ Create GHTestStats.
+ */
+extern GHTestStats GHTestStatsMake(NSInteger succeedCount, NSInteger failureCount, NSInteger cancelCount, NSInteger testCount);
+
+extern const GHTestStats GHTestStatsEmpty;
+
+extern NSString *NSStringFromGHTestStats(GHTestStats stats);
+
+@protocol GHTestDelegate;
+
+/*!
+ The base interface for a runnable test.
+ A runnable with a unique identifier, display name, stats, timer, delegate, log and error handling.
+ */
+@protocol GHTest <NSObject, NSCoding, NSCopying>
+
+- (void)run:(GHTestOptions)options;
+
+@property (readonly, nonatomic) NSString *identifier; // Unique identifier for test
+@property (readonly, nonatomic) NSString *name;
+@property (assign, nonatomic) NSTimeInterval interval;
+@property (assign, nonatomic) GHTestStatus status;
+@property (readonly, nonatomic) GHTestStats stats;
+@property (retain, nonatomic) NSException *exception;
+@property (assign, nonatomic, getter=isDisabled) BOOL disabled;
+@property (assign, nonatomic, getter=isHidden) BOOL hidden;
+@property (assign, nonatomic) id<GHTestDelegate> delegate; // weak
+
+- (NSArray *)log;
+
+- (void)reset;
+- (void)cancel;
+
+- (NSInteger)disabledCount;
+
+@end
+
+/*!
+ Test delegate for notification when a test starts and ends.
+ */
+@protocol GHTestDelegate <NSObject>
+- (void)testDidStart:(id<GHTest>)test source:(id<GHTest>)source;
+- (void)testDidUpdate:(id<GHTest>)test source:(id<GHTest>)source;
+- (void)testDidEnd:(id<GHTest>)test source:(id<GHTest>)source;
+- (void)test:(id<GHTest>)test didLog:(NSString *)message source:(id<GHTest>)source;
+@end
+
+/*!
+ Delegate which is notified of log messages from inside GHTestCase.
+ */
+@protocol GHTestCaseLogWriter <NSObject>
+- (void)log:(NSString *)message testCase:(id)testCase;
+@end
+
+/*!
+ Default test implementation with a target/selector pair.
+ - Tests a target and selector
+ - Notifies a test delegate
+ - Keeps track of status, running time and failures
+ - Stores any test specific logging
+ */
+@interface GHTest : NSObject <GHTest, GHTestCaseLogWriter> {
+
+ NSObject<GHTestDelegate> *delegate_; // weak
+
+ id target_;
+ SEL selector_;
+
+ NSString *identifier_;
+ NSString *name_;
+ GHTestStatus status_;
+ NSTimeInterval interval_;
+ BOOL disabled_;
+ BOOL hidden_;
+ NSException *exception_; // If failed
+
+ NSMutableArray *log_;
+
+}
+
+@property (readonly, nonatomic) id target;
+@property (readonly, nonatomic) SEL selector;
+@property (readonly, nonatomic) NSArray *log;
+
+/*!
+ Create test with identifier, name.
+ @param identifier Unique identifier
+ @param name Name
+ */
+- (id)initWithIdentifier:(NSString *)identifier name:(NSString *)name;
+
+/*!
+ Create test with target/selector.
+ @param target Target (usually a test case)
+ @param selector Selector (usually a test method)
+ */
+- (id)initWithTarget:(id)target selector:(SEL)selector;
+
+/*!
+ Create autoreleased test with target/selector.
+ @param target Target (usually a test case)
+ @param selector Selector (usually a test method)
+ */
++ (id)testWithTarget:(id)target selector:(SEL)selector;
++ (id)testWithTarget:(id)target methodName:(NSString *)methodName;
+
+@end
+
View
23 Tests/GHUnit.framework/Headers/GHTestApp.h
@@ -0,0 +1,23 @@
+//
+// GHTestApp.h
+// GHUnit
+//
+// Created by Gabriel Handford on 1/20/09.
+// Copyright 2009. All rights reserved.
+//
+
+#import "GHTestWindowController.h"
+
+@interface GHTestApp : NSObject {
+ NSMutableArray *topLevelObjects_;
+
+ GHTestWindowController *windowController_;
+
+ GHTestSuite *suite_;
+}
+
+- (id)initWithSuite:(GHTestSuite *)suite;
+
+- (void)runTests;
+
+@end
View
157 Tests/GHUnit.framework/Headers/GHTestCase.h
@@ -0,0 +1,157 @@
+//
+// GHTestCase.h
+// GHUnit
+//
+// Created by Gabriel Handford on 1/21/09.
+// Copyright 2009. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+//
+// Portions of this file fall under the following license, marked with:
+// GTM_BEGIN : GTM_END
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "GHTestMacros.h"
+#import "GHTest.h"
+
+// Log to your test case logger.
+// For example, GHTestLog(@"Some debug info, %@", obj)
+#define GHTestLog(...) [self log:[NSString stringWithFormat:__VA_ARGS__, nil]]
+
+/*!
+ The base class for a test case.
+
+ @code
+ @interface MyTest : GHTestCase {}
+ @end
+
+ @implementation MyTest
+
+ // Run before each test method
+ - (void)setUp { }
+
+ // Run after each test method
+ - (void)tearDown { }
+
+ // Run before the tests are run for this class
+ - (void)setUpClass { }
+
+ // Run before the tests are run for this class
+ - (void)tearDownClass { }
+
+ // Tests are prefixed by 'test' and contain no arguments and no return value
+ - (void)testA {
+ GHTestLog(@"Log with a test with the GHTestLog(...) for test specific logging.");
+ }
+
+ // Another test; Tests are run in lexical order
+ - (void)testB { }
+
+ // Override any exceptions; By default exceptions are raised, causing a test failure
+ - (void)failWithException:(NSException *)exception { }
+
+ @end
+ @endcode
+
+ */
+@interface GHTestCase : NSObject {
+ id<GHTestCaseLogWriter> logWriter_; // weak
+
+ SEL currentSelector_;
+}
+
+//! The current test selector
+@property (assign, nonatomic) SEL currentSelector;
+@property (assign, nonatomic) id<GHTestCaseLogWriter> logWriter;
+
+// GTM_BEGIN
+//! Run before each test method
+- (void)setUp;
+
+//! Run after each test method
+- (void)tearDown;
+
+/*!
+ By default exceptions are raised, causing a test failure
+ @brief Override any exceptions
+ @param exception Exception that was raised by test
+ */
+- (void)failWithException:(NSException*)exception;
+// GTM_END
+
+//! Run before the tests (once per test case)
+- (void)setUpClass;
+
+//! Run after the tests (once per test case)
+- (void)tearDownClass;
+
+/*!
+ Whether to run the tests on a separate thread. Override this method in your
+ test case to override the default.
+ Default is NO, tests are run on a separate thread by default.
+ @result If YES runs on the main thread
+ */
+- (BOOL)shouldRunOnMainThread;
+
+//! Any special handling of exceptions after they are thrown; By default logs stack trace to standard out.
+- (void)handleException:(NSException *)exception;
+
+/*!
+ Log a message, which notifies the log delegate.
+ This is not meant to be used directly, see GHTestLog(...) macro.
+ @param message
+ */
+- (void)log:(NSString *)message;
+
+
+// Additions, to avoid needing to use macros directly
+
+/*!
+ Raise an exception with accompanying error message
+ @param errorMessage
+ */
+
+- (void)raise:(NSString *)errorMessage;
+
+/*!
+ Assert that the value passed in to boolean is true, with an error message to display if it isn't
+ @param boolean, errorMessage
+ */
+- (void)assert:(BOOL)boolean error:(NSString *)errorMessage;
+
+@end
View
38 Tests/GHUnit.framework/Headers/GHTestGroup+JUnitXML.h
@@ -0,0 +1,38 @@
+//
+// GHTestGroup+JUnitXML.h
+// GHUnit
+//
+// Created by Gabriel Handford on 6/4/10.
+// Copyright 2010. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "GHTestGroup.h"
+
+@interface GHTestGroup (JUnitXML)
+
+- (NSString *)JUnitXML;
+
+- (BOOL)writeJUnitXMLAtPath:(NSString *)documentsPath error:(NSError **)error;
+
+@end
View
151 Tests/GHUnit.framework/Headers/GHTestGroup.h
@@ -0,0 +1,151 @@
+//
+// GHTestGroup.h
+//
+// Created by Gabriel Handford on 1/16/09.
+// Copyright 2009. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "GHTest.h"
+#import "GHTestCase.h"
+
+/*!
+ @brief Interface for a group of tests.
+
+ This group conforms to the GHTest protocol as well (see Composite pattern).
+ */
+@protocol GHTestGroup <GHTest>
+- (NSString *)name;
+- (id<GHTestGroup>)parent;
+- (NSArray *)children;
+@end
+
+/*!
+ @brief A collection of tests (or test groups).
+
+ A test group is a collection of id<GHTest>, that may represent a set of test case methods.
+
+ For example, if you had the following GHTestCase.
+
+ @code
+ @interface FooTest : GHTestCase {}
+ - (void)testFoo;
+ - (void)testBar;
+ @end
+ @endcode
+
+ The GHTestGroup would consist of and array of GHTest, [FooTest#testFoo and FooTest#testBar],
+ each test being a target and selector pair.
+
+ A test group may also consist of a group of groups (since GHTestGroup conforms to GHTest),
+ and this might represent a GHTestSuite.
+ */
+@interface GHTestGroup : NSObject <GHTestDelegate, GHTestGroup> {
+
+ NSObject<GHTestDelegate> *delegate_; // weak
+ id<GHTestGroup> parent_; // weak
+
+ NSMutableArray */*of id<GHTest>*/children_;
+
+ NSString *name_; // The name of the test group (usually the class name of the test case
+ NSTimeInterval interval_; // Total time of child tests
+ GHTestStatus status_; // Current status of the group (current status of running or completed child tests)
+ GHTestStats stats_; // Current stats for the group (aggregate of child test stats)
+
+ BOOL didSetUpClass_;
+
+ GHTestOptions options_;
+
+ // Set if test is created from initWithTestCase:delegate:
+ // Allows use to perform setUpClass and tearDownClass (once per test case run)
+ id testCase_;
+
+ NSException *exception_; // If exception happens in group setUpClass/tearDownClass
+}
+
+@property (readonly, nonatomic) NSArray */*of id<GHTest>*/children;
+@property (assign, nonatomic) id<GHTestGroup> parent;
+@property (readonly, nonatomic) id testCase;
+@property (assign, nonatomic) GHTestOptions options;
+
+/*!
+ Create an empty test group.
+ @param name The name of the test group
+ @param delegate Delegate, notifies of test start and end
+ @result New test group
+ */
+- (id)initWithName:(NSString *)name delegate:(id<GHTestDelegate>)delegate;
+
+/*!
+ Create test group from a test case.
+ @param testCase Test case, could be a subclass of SenTestCase or GHTestCase
+ @param delegate Delegate, notifies of test start and end
+ @result New test group
+ */
+- (id)initWithTestCase:(id)testCase delegate:(id<GHTestDelegate>)delegate;
+
+/*!
+ Create test group from a single test.
+ @param testCase
+ @param selector Test to run
+ @param delegate
+ */
+- (id)initWithTestCase:(id)testCase selector:(SEL)selector delegate:(id<GHTestDelegate>)delegate;
+
+/*!
+ Create test group from a test case.
+ @param testCase Test case, could be a subclass of SenTestCase or GHTestCase
+ @param delegate Delegate, notifies of test start and end
+ @result New test group
+ */
++ (GHTestGroup *)testGroupFromTestCase:(id)testCase delegate:(id<GHTestDelegate>)delegate;
+
+/*!
+ Add a test case (or test group) to this test group.
+ @param testCase Test case, could be a subclass of SenTestCase or GHTestCase
+ */
+- (void)addTestCase:(id)testCase;
+
+- (void)addTestGroup:(GHTestGroup *)testGroup;
+
+- (void)addTests:(NSArray */*of id<GHTest>*/)tests;
+
+- (void)addTest:(id<GHTest>)test;
+
+- (BOOL)shouldRunOnMainThread;
+
+/*!
+ Get list of failed tests.
+ @result Failed tests
+ */
+- (NSArray */*of id<GHTest>*/)failedTests;
+
+/*!
+ Run in operation queue.
+ Tests from the group are added and will block until they have completed.
+ @param operationQueue If nil, then runs as is
+ @param options Options
+ */
+- (void)runInOperationQueue:(NSOperationQueue *)operationQueue options:(GHTestOptions)options;
+
+@end
View
1,010 Tests/GHUnit.framework/Headers/GHTestMacros.h
@@ -0,0 +1,1010 @@
+//
+// GHTestMacros.h
+//
+// Created by Gabriel Handford on 1/17/09.
+// Copyright 2009. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+//
+// Portions of this file fall under the following license, marked with
+// SENTE_BEGIN - SENTE_END
+//
+// Copyright (c) 1997-2005, Sen:te (Sente SA). All rights reserved.
+//
+// Use of this source code is governed by the following license:
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// (1) Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// (2) Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL Sente SA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Note: this license is equivalent to the FreeBSD license.
+//
+// This notice may not be removed from this file.
+
+//
+// Portions of this file fall under the following license, marked with:
+// GTM_BEGIN : GTM_END
+//
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+//
+
+#import "NSException+GHTestFailureExceptions.h"
+#import "NSValue+GHValueFormatter.h"
+
+// GTM_BEGIN
+
+extern NSString *const GHTestFilenameKey;
+extern NSString *const GHTestLineNumberKey;
+extern NSString *const GHTestFailureException;
+
+#if defined(__cplusplus)
+extern "C"
+#endif
+NSString *GHComposeString(NSString *, ...);
+
+// Generates a failure when a1 != noErr
+// Args:
+// a1: should be either an OSErr or an OSStatus
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define GHAssertNoErr(a1, description, ...) \
+do { \
+@try {\
+OSStatus a1value = (a1); \
+if (a1value != noErr) { \
+NSString *_expression = [NSString stringWithFormat:@"Expected noErr, got %ld for (%s)", a1value, #a1]; \
+if (description) { \
+_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(description, ##__VA_ARGS__)]; \
+} \
+[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:_expression]]; \
+} \
+}\
+@catch (id anException) {\
+[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat:@"(%s) == noErr fails", #a1] \
+exception:anException \
+inFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \
+}\
+} while(0)
+
+// Generates a failure when a1 != a2
+// Args:
+// a1: received value. Should be either an OSErr or an OSStatus
+// a2: expected value. Should be either an OSErr or an OSStatus
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define GHAssertErr(a1, a2, description, ...) \
+do { \
+@try {\
+OSStatus a1value = (a1); \
+OSStatus a2value = (a2); \
+if (a1value != a2value) { \
+NSString *_expression = [NSString stringWithFormat:@"Expected %s(%ld) but got %ld for (%s)", #a2, a2value, a1value, #a1]; \
+if (description) { \
+_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(description, ##__VA_ARGS__)]; \
+} \
+[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:_expression]]; \
+} \
+}\
+@catch (id anException) {\
+[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat:@"(%s) == (%s) fails", #a1, #a2] \
+exception:anException \
+inFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \
+}\
+} while(0)
+
+
+// Generates a failure when a1 is NULL
+// Args:
+// a1: should be a pointer (use GHAssertNotNil for an object)
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define GHAssertNotNULL(a1, description, ...) \
+do { \
+@try {\
+const void* a1value = (a1); \
+if (a1value == NULL) { \
+NSString *_expression = [NSString stringWithFormat:@"(%s) != NULL", #a1]; \
+if (description) { \
+_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(description, ##__VA_ARGS__)]; \
+} \
+[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:_expression]]; \
+} \
+}\
+@catch (id anException) {\
+[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat:@"(%s) != NULL fails", #a1] \
+exception:anException \
+inFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \
+}\
+} while(0)
+
+// Generates a failure when a1 is not NULL
+// Args:
+// a1: should be a pointer (use GHAssertNil for an object)
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define GHAssertNULL(a1, description, ...) \
+do { \
+@try {\
+const void* a1value = (a1); \
+if (a1value != NULL) { \
+NSString *_expression = [NSString stringWithFormat:@"(%s) == NULL", #a1]; \
+if (description) { \
+_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(description, ##__VA_ARGS__)]; \
+} \
+[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:_expression]]; \
+} \
+}\
+@catch (id anException) {\
+[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat:@"(%s) == NULL fails", #a1] \
+exception:anException \
+inFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \
+}\
+} while(0)
+
+// Generates a failure when a1 is equal to a2. This test is for C scalars,
+// structs and unions.
+// Args:
+// a1: argument 1
+// a2: argument 2
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define GHAssertNotEquals(a1, a2, description, ...) \
+do { \
+@try {\
+if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:[@"Type mismatch -- " stringByAppendingString:GHComposeString(description, ##__VA_ARGS__)]]]; \
+} else { \
+__typeof__(a1) a1value = (a1); \
+__typeof__(a2) a2value = (a2); \
+NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \
+NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \
+if ([a1encoded isEqualToValue:a2encoded]) { \
+NSString *_expression = [NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2]; \
+if (description) { \
+_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(description, ##__VA_ARGS__)]; \
+} \
+[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:_expression]]; \
+} \
+} \
+} \
+@catch (id anException) {\
+[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \
+exception:anException \
+inFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:GHComposeString(description, ##__VA_ARGS__)]]; \
+}\
+} while(0)
+
+// Generates a failure when a1 is equal to a2. This test is for objects.
+// Args:
+// a1: argument 1. object.
+// a2: argument 2. object.
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define GHAssertNotEqualObjects(a1, a2, desc, ...) \
+do { \
+@try {\
+id a1value = (a1); \
+id a2value = (a2); \
+if ( (@encode(__typeof__(a1value)) == @encode(id)) && \
+(@encode(__typeof__(a2value)) == @encode(id)) && \
+![(id)a1value isEqual:(id)a2value] ) continue; \
+NSString *_expression = [NSString stringWithFormat:@"%s('%@') != %s('%@')", #a1, [a1 description], #a2, [a2 description]]; \
+if (desc) { \
+_expression = [NSString stringWithFormat:@"%@: %@", _expression, GHComposeString(desc, ##__VA_ARGS__)]; \
+} \
+[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:_expression]]; \
+}\
+@catch (id anException) {\
+[self failWithException:[NSException ghu_failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \
+exception:anException \
+inFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:GHComposeString(desc, ##__VA_ARGS__)]]; \
+}\
+} while(0)
+
+// Generates a failure when a1 is not 'op' to a2. This test is for C scalars.
+// Args:
+// a1: argument 1
+// a2: argument 2
+// op: operation
+// description: A format string as in the printf() function. Can be nil or
+// an empty string but must be present.
+// ...: A variable number of arguments to the format string. Can be absent.
+#define GHAssertOperation(a1, a2, op, description, ...) \
+do { \
+@try {\
+if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \
+[self failWithException:[NSException ghu_failureInFile:[NSString stringWithUTF8String:__FILE__] \
+atLine:__LINE__ \
+withDescription:[@"Type mismatch -- " stringByAppendingString:GHComposeString(description, ##__VA_ARGS__)]]]; \
+} else { \