Permalink
Browse files

OCUnit support

  • Loading branch information...
1 parent 2ad4520 commit b1acc183443e887af1f2dba7a0277af9d94ae484 @cppforlife cppforlife committed Oct 9, 2011
View
36 Cedar.xcodeproj/project.pbxproj
@@ -32,6 +32,16 @@
96A07F0B13F276B10021974D /* FocusedSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A07F0A13F276B10021974D /* FocusedSpec.m */; };
96A07F0F13F27F2F0021974D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A07F0E13F27F2F0021974D /* main.m */; };
96A07F1113F283E40021974D /* FocusedSpec2.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A07F1013F283E40021974D /* FocusedSpec2.m */; };
+ 960118BC1434867E00825FFF /* CDROTestIPhoneRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 960118BB1434867E00825FFF /* CDROTestIPhoneRunner.m */; };
+ 968BA92F143485F800EA40B3 /* CDROTestIPhoneRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = 968BA92E143485F800EA40B3 /* CDROTestIPhoneRunner.h */; };
+ 96EA1CA8142C6425001A78E0 /* CDROTestReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 96EA1CA6142C6425001A78E0 /* CDROTestReporter.m */; };
+ 96EA1CA9142C6425001A78E0 /* CDROTestReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 96EA1CA6142C6425001A78E0 /* CDROTestReporter.m */; };
+ 96EA1CAA142C6425001A78E0 /* CDROTestRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 96EA1CA7142C6425001A78E0 /* CDROTestRunner.m */; };
+ 96EA1CAE142C6449001A78E0 /* CDROTestReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 96EA1CAC142C6449001A78E0 /* CDROTestReporter.h */; };
+ 96EA1CAF142C6449001A78E0 /* CDROTestReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 96EA1CAC142C6449001A78E0 /* CDROTestReporter.h */; };
+ 96EA1CB0142C6449001A78E0 /* CDROTestRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = 96EA1CAD142C6449001A78E0 /* CDROTestRunner.h */; };
+ 96EA1CBA142C6560001A78E0 /* CDRSpecFailureSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 96EA1CB9142C6560001A78E0 /* CDRSpecFailureSpec.mm */; };
+ 96EA1CBB142C6560001A78E0 /* CDRSpecFailureSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 96EA1CB9142C6560001A78E0 /* CDRSpecFailureSpec.mm */; };
AE0AF56513E9C0E300029396 /* CedarMatchers.h in Headers */ = {isa = PBXBuildFile; fileRef = AE0AF55E13E9C0E300029396 /* CedarMatchers.h */; settings = {ATTRIBUTES = (Public, ); }; };
AE0AF56C13E9C0FB00029396 /* CedarMatchers.h in Copy headers to framework */ = {isa = PBXBuildFile; fileRef = AE0AF55E13E9C0E300029396 /* CedarMatchers.h */; };
AE0AF57D13E9C5B200029396 /* BeInstanceOf.mm in Sources */ = {isa = PBXBuildFile; fileRef = AE0AF57B13E9C5B200029396 /* BeInstanceOf.mm */; };
@@ -284,6 +294,13 @@
96A07F0A13F276B10021974D /* FocusedSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FocusedSpec.m; path = Focused/FocusedSpec.m; sourceTree = "<group>"; };
96A07F0E13F27F2F0021974D /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Focused/main.m; sourceTree = "<group>"; };
96A07F1013F283E40021974D /* FocusedSpec2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FocusedSpec2.m; path = Focused/FocusedSpec2.m; sourceTree = "<group>"; };
+ 960118BB1434867E00825FFF /* CDROTestIPhoneRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDROTestIPhoneRunner.m; sourceTree = "<group>"; };
+ 968BA92E143485F800EA40B3 /* CDROTestIPhoneRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDROTestIPhoneRunner.h; sourceTree = "<group>"; };
+ 96EA1CA6142C6425001A78E0 /* CDROTestReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDROTestReporter.m; sourceTree = "<group>"; };
+ 96EA1CA7142C6425001A78E0 /* CDROTestRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDROTestRunner.m; sourceTree = "<group>"; };
+ 96EA1CAC142C6449001A78E0 /* CDROTestReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDROTestReporter.h; sourceTree = "<group>"; };
+ 96EA1CAD142C6449001A78E0 /* CDROTestRunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDROTestRunner.h; sourceTree = "<group>"; };
+ 96EA1CB9142C6560001A78E0 /* CDRSpecFailureSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CDRSpecFailureSpec.mm; sourceTree = "<group>"; };
AE0AF55E13E9C0E300029396 /* CedarMatchers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CedarMatchers.h; sourceTree = "<group>"; };
AE0AF57913E9C16D00029396 /* MatcherTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MatcherTemplate.h; sourceTree = "<group>"; };
AE0AF57B13E9C5B200029396 /* BeInstanceOf.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BeInstanceOf.mm; sourceTree = "<group>"; };
@@ -526,6 +543,8 @@
AEEE1FC411DC27B800029872 /* CDRDefaultReporter.m */,
AEC9DEEB12C2CC7E0039512D /* CDRColorizedReporter.m */,
42064469139B44F600C85605 /* CDRTeamCityReporter.m */,
+ 96EA1CA6142C6425001A78E0 /* CDROTestReporter.m */,
+ 96EA1CA7142C6425001A78E0 /* CDROTestRunner.m */,
);
path = Source;
sourceTree = "<group>";
@@ -549,6 +568,8 @@
AEEE1FCB11DC27B800029872 /* CDRDefaultReporter.h */,
AEC9DEEA12C2CC7E0039512D /* CDRColorizedReporter.h */,
42064465139B44EC00C85605 /* CDRTeamCityReporter.h */,
+ 96EA1CAC142C6449001A78E0 /* CDROTestReporter.h */,
+ 96EA1CAD142C6449001A78E0 /* CDROTestRunner.h */,
);
path = Headers;
sourceTree = "<group>";
@@ -562,6 +583,7 @@
AEEE1FD811DC27B800029872 /* CDRSpecStatusCell.h */,
AEEE1FD911DC27B800029872 /* CDRSpecStatusViewController.h */,
AEEE1FDA11DC27B800029872 /* CedarApplicationDelegate.h */,
+ 968BA92E143485F800EA40B3 /* CDROTestIPhoneRunner.h */,
);
path = iPhone;
sourceTree = "<group>";
@@ -575,6 +597,7 @@
AEEE1FE011DC27B800029872 /* CDRSpecStatusCell.m */,
AEEE1FE111DC27B800029872 /* CDRSpecStatusViewController.m */,
AEEE1FE211DC27B800029872 /* CedarApplicationDelegate.m */,
+ 960118BB1434867E00825FFF /* CDROTestIPhoneRunner.m */,
);
path = iPhone;
sourceTree = "<group>";
@@ -596,6 +619,8 @@
96A07F0D13F27ED70021974D /* Focused */,
AEEE1FEB11DC27B800029872 /* iPhone */,
AE8C879F1362068A006C9305 /* Matchers */,
+ 96EA1CB9142C6560001A78E0 /* CDRSpecFailureSpec.mm */,
+ AEEE1FE811DC27B800029872 /* CDRExampleGroupSpec.mm */,
AEEE1FE911DC27B800029872 /* CDRExampleSpec.mm */,
AEEE1FE811DC27B800029872 /* CDRExampleGroupSpec.mm */,
AEEE1FF011DC27B800029872 /* GlobalBeforeEachSpec.mm */,
@@ -705,6 +730,8 @@
AE0AF58513E9E87E00029396 /* ActualValue.h in Headers */,
AEF72F7813EC730700786282 /* CedarComparators.h in Headers */,
AEF72F7B13EC734000786282 /* CedarStringifiers.h in Headers */,
+ 96EA1CAE142C6449001A78E0 /* CDROTestReporter.h in Headers */,
+ 96EA1CB0142C6449001A78E0 /* CDROTestRunner.h in Headers */,
AEF7301113ECC25000786282 /* BeEmpty.h in Headers */,
AEF7303A13ECCB7B00786282 /* StringifiersBase.h in Headers */,
AEF7303B13ECCB7B00786282 /* StringifiersContainer.h in Headers */,
@@ -727,6 +754,8 @@
files = (
AEC9DEEF12C2CC7E0039512D /* CDRColorizedReporter.h in Headers */,
42064467139B44EC00C85605 /* CDRTeamCityReporter.h in Headers */,
+ 96EA1CAF142C6449001A78E0 /* CDROTestReporter.h in Headers */,
+ 968BA92F143485F800EA40B3 /* CDROTestIPhoneRunner.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -837,6 +866,7 @@
Japanese,
French,
German,
+ en,
);
mainGroup = AEEE1FA411DC26EA00029872;
productRefGroup = AEEE1FB711DC271300029872 /* Products */;
@@ -961,6 +991,8 @@
AE8C881413626FE7006C9305 /* CDRSpecFailure.m in Sources */,
4206446A139B44F600C85605 /* CDRTeamCityReporter.m in Sources */,
AE0AF57D13E9C5B200029396 /* BeInstanceOf.mm in Sources */,
+ 96EA1CA8142C6425001A78E0 /* CDROTestReporter.m in Sources */,
+ 96EA1CAA142C6425001A78E0 /* CDROTestRunner.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -975,6 +1007,7 @@
AEEE21C311DC290400029872 /* SpecSpec.mm in Sources */,
AEEE21C411DC290400029872 /* SpecSpec2.m in Sources */,
AE8C87AE136245BB006C9305 /* ExpectFailureWithMessage.m in Sources */,
+ 96EA1CBA142C6560001A78E0 /* CDRSpecFailureSpec.mm in Sources */,
AEF7301B13ECC4AE00786282 /* BeCloseToSpec.mm in Sources */,
AEF7301D13ECC4AE00786282 /* BeInstanceOfSpec.mm in Sources */,
AEF7301F13ECC4AE00786282 /* BeNilSpec.mm in Sources */,
@@ -1012,6 +1045,8 @@
AE8C881513626FE7006C9305 /* CDRSpecFailure.m in Sources */,
4206446B139B44F600C85605 /* CDRTeamCityReporter.m in Sources */,
AE0AF57E13E9C5B200029396 /* BeInstanceOf.mm in Sources */,
+ 96EA1CA9142C6425001A78E0 /* CDROTestReporter.m in Sources */,
+ 960118BC1434867E00825FFF /* CDROTestIPhoneRunner.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1028,6 +1063,7 @@
AEEE228711DC2D5800029872 /* main.m in Sources */,
AEEE228811DC2D5800029872 /* SlowSpec.m in Sources */,
AE8C87AF136245BD006C9305 /* ExpectFailureWithMessage.m in Sources */,
+ 96EA1CBB142C6560001A78E0 /* CDRSpecFailureSpec.mm in Sources */,
AEF7301C13ECC4AE00786282 /* BeCloseToSpec.mm in Sources */,
AEF7301E13ECC4AE00786282 /* BeInstanceOfSpec.mm in Sources */,
AEF7302013ECC4AE00786282 /* BeNilSpec.mm in Sources */,
View
102 Rakefile
@@ -1,10 +1,18 @@
PROJECT_NAME = "Cedar"
-CONFIGURATION = "Release"
+APP_NAME = "OCUnitApplicationTests"
+CONFIGURATION = "Debug"
+
+OCUNIT_SPECS_TARGET_NAME = "OCUnitSpecs"
+OCUNIT_UI_SPECS_TARGET_NAME = "OCUnitApplicationTestsTests"
+
SPECS_TARGET_NAME = "Specs"
UI_SPECS_TARGET_NAME = "iPhoneSpecs"
-SDK_DIR = "/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk"
+
+SDK_VERSION = "4.3"
+SDK_DIR = "/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator#{SDK_VERSION}.sdk"
BUILD_DIR = File.join(File.dirname(__FILE__), "build")
+
def build_dir(effective_platform_name)
File.join(BUILD_DIR, CONFIGURATION + effective_platform_name)
end
@@ -15,6 +23,20 @@ def system_or_exit(cmd, stdout = nil)
system(cmd) or raise "******** Build failed ********"
end
+def with_env_vars(env_vars)
+ old_values = {}
+ env_vars.each do |key,new_value|
+ old_values[key] = ENV[key]
+ ENV[key] = new_value
+ end
+
+ yield
+
+ env_vars.each_key do |key|
+ ENV[key] = old_values[key]
+ end
+end
+
def output_file(target)
output_dir = if ENV['IS_CI_BOX']
ENV['CC_BUILD_ARTIFACTS']
@@ -28,21 +50,21 @@ def output_file(target)
output_file
end
-task :default => [:trim_whitespace, :specs, :focused_specs, :uispecs]
-task :cruise do
- Rake::Task[:clean].invoke
- Rake::Task[:build_all].invoke
- Rake::Task[:specs].invoke
- Rake::Task[:focused_specs].invoke
- Rake::Task[:uispecs].invoke
+def kill_simulator
+ system %Q[killall -m -KILL "gdb"]
+ system %Q[killall -m -KILL "otest"]
+ system %Q[killall -m -KILL "iPhone Simulator"]
end
+task :default => [:trim_whitespace, :ocunit_specs, :ocunit_uispecs, :specs, :focused_specs, :uispecs]
+task :cruise => [:clean, :build_all, :ocunit_specs, :ocunit_uispecs, :specs, :focused_specs, :uispecs]
+
task :trim_whitespace do
- system_or_exit(%Q[git status --short | awk '{if ($1 != "D" && $1 != "R") print $2}' | grep -e '.*\.[mh]$' | xargs sed -i '' -e 's/ / /g;s/ *$//g;'])
+ system_or_exit %Q[git status --short | awk '{if ($1 != "D" && $1 != "R") print $2}' | grep -e '.*\.[cmh]$' | xargs sed -i '' -e 's/ / /g;s/ *$//g;']
end
task :clean do
- system_or_exit(%Q[xcodebuild -project #{PROJECT_NAME}.xcodeproj -alltargets -configuration #{CONFIGURATION} clean SYMROOT=#{BUILD_DIR}], output_file("clean"))
+ system_or_exit "xcodebuild -project #{PROJECT_NAME}.xcodeproj -alltargets -configuration #{CONFIGURATION} clean SYMROOT=#{BUILD_DIR}", output_file("clean")
end
task :build_specs do
@@ -51,19 +73,26 @@ task :build_specs do
end
task :build_uispecs do
- `osascript -e 'tell application "iPhone Simulator" to quit'`
- system_or_exit(%Q[xcodebuild -project #{PROJECT_NAME}.xcodeproj -target #{UI_SPECS_TARGET_NAME} -configuration #{CONFIGURATION} build], output_file("uispecs"))
+ kill_simulator
+ system_or_exit "xcodebuild -project #{PROJECT_NAME}.xcodeproj -target #{UI_SPECS_TARGET_NAME} -configuration #{CONFIGURATION} build", output_file("uispecs")
end
task :build_all do
- system_or_exit(%Q[xcodebuild -project #{PROJECT_NAME}.xcodeproj -alltargets -configuration #{CONFIGURATION} build SYMROOT=#{BUILD_DIR}], output_file("build_all"))
+ kill_simulator
+ system_or_exit "xcodebuild -project #{PROJECT_NAME}.xcodeproj -alltargets -configuration #{CONFIGURATION} build TEST_AFTER_BUILD=NO SYMROOT=#{BUILD_DIR}", output_file("build_all")
end
task :specs => :build_specs do
build_dir = build_dir("")
- ENV["DYLD_FRAMEWORK_PATH"] = build_dir
- ENV["CEDAR_REPORTER_CLASS"] = "CDRColorizedReporter"
- system_or_exit(File.join(build_dir, SPECS_TARGET_NAME))
+ with_env_vars("DYLD_FRAMEWORK_PATH" => build_dir, "CEDAR_REPORTER_CLASS" => "CDRColorizedReporter") do
+ system_or_exit(File.join(build_dir, SPECS_TARGET_NAME))
+ end
+end
+
+task :ocunit_specs do
+ with_env_vars("CEDAR_REPORTER_CLASS" => "CDRColorizedReporter") do
+ system_or_exit "xcodebuild -project #{PROJECT_NAME}.xcodeproj -target #{OCUNIT_SPECS_TARGET_NAME} -configuration #{CONFIGURATION} -arch x86_64 build SYMROOT=#{BUILD_DIR}"
+ end
end
task :focused_specs do
@@ -81,11 +110,38 @@ end
require 'tmpdir'
task :uispecs => :build_uispecs do
- ENV["DYLD_ROOT_PATH"] = SDK_DIR
- ENV["IPHONE_SIMULATOR_ROOT"] = SDK_DIR
- ENV["CFFIXED_USER_HOME"] = Dir.tmpdir
- ENV["CEDAR_HEADLESS_SPECS"] = "1"
- ENV["CEDAR_REPORTER_CLASS"] = "CDRColorizedReporter"
+ env_vars = {
+ "DYLD_ROOT_PATH" => SDK_DIR,
+ "IPHONE_SIMULATOR_ROOT" => SDK_DIR,
+ "CFFIXED_USER_HOME" => Dir.tmpdir,
+ "CEDAR_HEADLESS_SPECS" => "1",
+ "CEDAR_REPORTER_CLASS" => "CDRColorizedReporter",
+ }
+
+ with_env_vars(env_vars) do
+ system_or_exit "#{File.join(build_dir("-iphonesimulator"), "#{UI_SPECS_TARGET_NAME}.app", UI_SPECS_TARGET_NAME)} -RegisterForSystemEvents";
+ end
+end
- system_or_exit(%Q[#{File.join(build_dir("-iphonesimulator"), "#{UI_SPECS_TARGET_NAME}.app", UI_SPECS_TARGET_NAME)} -RegisterForSystemEvents]);
+task :ocunit_uispecs do
+ kill_simulator
+
+ system_or_exit "xcodebuild -project #{PROJECT_NAME}.xcodeproj -target #{APP_NAME} -configuration #{CONFIGURATION} -sdk iphonesimulator#{SDK_VERSION} build TEST_AFTER_BUILD=NO SYMROOT=#{BUILD_DIR}", output_file("app")
+ system_or_exit "xcodebuild -project #{PROJECT_NAME}.xcodeproj -target #{OCUNIT_UI_SPECS_TARGET_NAME} -configuration #{CONFIGURATION} -sdk iphonesimulator#{SDK_VERSION} build TEST_AFTER_BUILD=NO SYMROOT=#{BUILD_DIR}", output_file("ocunit_uispecs")
+
+ env_vars = {
+ "DYLD_ROOT_PATH" => SDK_DIR,
+ "DYLD_INSERT_LIBRARIES" => "/Developer/Library/PrivateFrameworks/IDEBundleInjection.framework/IDEBundleInjection",
+ "DYLD_FALLBACK_LIBRARY_PATH" => SDK_DIR,
+ "XCInjectBundle" => "#{File.join(build_dir("-iphonesimulator"), "#{OCUNIT_UI_SPECS_TARGET_NAME}.octest")}",
+ "XCInjectBundleInto" => "#{File.join(build_dir("-iphonesimulator"), "#{APP_NAME}.app/#{APP_NAME}")}",
+ "IPHONE_SIMULATOR_ROOT" => SDK_DIR,
+ "CFFIXED_USER_HOME" => Dir.tmpdir,
+ "CEDAR_HEADLESS_SPECS" => "1",
+ "CEDAR_REPORTER_CLASS" => "CDRColorizedReporter",
+ }
+
+ with_env_vars(env_vars) do
+ system_or_exit "#{File.join(build_dir("-iphonesimulator"), "#{APP_NAME}.app/#{APP_NAME}")} -RegisterForSystemEvents -SenTest All";
+ end
end
View
8 Source/CDRDefaultReporter.m
@@ -26,6 +26,7 @@ - (id)init {
- (void)dealloc {
[rootGroups_ release];
[startTime_ release];
+ [endTime_ release];
[failureMessages_ release];
[skippedMessages_ release];
[pendingMessages_ release];
@@ -40,6 +41,7 @@ - (void)runWillStartWithGroups:(NSArray *)groups {
}
- (void)runDidComplete {
+ endTime_ = [[NSDate alloc] init];
[self stopObservingExamples:rootGroups_];
printf("\n");
@@ -88,15 +90,15 @@ - (NSString *)failureToken {
}
- (NSString *)failureMessageForExample:(CDRExample *)example {
- return [NSString stringWithFormat:@"FAILURE %@\n%@\n", [example fullText], [example message]];
+ return [NSString stringWithFormat:@"FAILURE %@\n%@\n",[example fullText], example.failure];
}
- (NSString *)errorToken {
return @"E";
}
- (NSString *)errorMessageForExample:(CDRExample *)example {
- return [NSString stringWithFormat:@"EXCEPTION %@\n%@\n", [example fullText], [example message]];
+ return [NSString stringWithFormat:@"EXCEPTION %@\n%@\n", [example fullText], example.failure];
}
#pragma mark Private interface
@@ -190,7 +192,7 @@ - (void)reportOnExample:(CDRExample *)example {
}
- (void)printStats {
- printf("\nFinished in %.4f seconds\n\n", [[NSDate date] timeIntervalSinceDate:startTime_]);
+ printf("\nFinished in %.4f seconds\n\n", [endTime_ timeIntervalSinceDate:startTime_]);
printf("%u examples, %u failures", exampleCount_, (unsigned int)failureMessages_.count);
if (pendingMessages_.count) {
View
14 Source/CDRExample.m
@@ -11,7 +11,7 @@ - (void)setState:(CDRExampleState)state;
@implementation CDRExample
-@synthesize message = message_;
+@synthesize failure = failure_;
+ (id)exampleWithText:(NSString *)text andBlock:(CDRSpecBlock)block {
return [[[[self class] alloc] initWithText:text andBlock:block] autorelease];
@@ -36,11 +36,10 @@ - (CDRExampleState)state {
}
- (NSString *)message {
- if (message_) {
- return message_;
- } else {
- return [super message];
+ if (self.failure.reason) {
+ return self.failure.reason;
}
+ return [super message];
}
- (float)progress {
@@ -61,10 +60,10 @@ - (void)run {
block_();
self.state = CDRExampleStatePassed;
} @catch (CDRSpecFailure *x) {
- self.message = [x reason];
+ self.failure = x;
self.state = CDRExampleStateFailed;
} @catch (NSObject *x) {
- self.message = [x description];
+ self.failure = [CDRSpecFailure specFailureWithRaisedObject:x];
self.state = CDRExampleStateError;
}
[parent_ tearDown];
@@ -75,7 +74,6 @@ - (void)run {
}
#pragma mark Private interface
-
- (void)setState:(CDRExampleState)state {
state_ = state;
}
View
24 Source/CDRFunctions.m
@@ -67,6 +67,21 @@ void CDRDefineGlobalBeforeAndAfterEachBlocks() {
[SpecHelper specHelper].globalAfterEachClasses = CDRSelectClasses(^BOOL(Class class) { return CDRClassHasClassMethod(class, @selector(afterEach)); });
}
+Class CDRReporterClassFromEnv(const char *defaultReporterClassName) {
+ const char *reporterClassName = getenv("CEDAR_REPORTER_CLASS");
+ if (!reporterClassName) {
+ reporterClassName = defaultReporterClassName;
+ }
+
+ Class reporterClass = NSClassFromString([NSString stringWithCString:reporterClassName encoding:NSUTF8StringEncoding]);
+ if (!reporterClass) {
+ printf("***** The specified reporter class \"%s\" does not exist. *****\n", reporterClassName);
+ return nil;
+ }
+
+ return reporterClass;
+}
+
int runSpecsWithCustomExampleReporter(NSArray *specClasses, id<CDRExampleReporter> reporter) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
@@ -96,19 +111,14 @@ int runSpecsWithCustomExampleReporter(NSArray *specClasses, id<CDRExampleReporte
int runAllSpecs() {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- char *reporterClassName = getenv("CEDAR_REPORTER_CLASS");
- if (!reporterClassName) {
- reporterClassName = "CDRDefaultReporter";
- }
- Class reporterClass = NSClassFromString([NSString stringWithCString:reporterClassName encoding:NSUTF8StringEncoding]);
+ Class reporterClass = CDRReporterClassFromEnv("CDRDefaultReporter");
if (!reporterClass) {
- printf("***** The specified reporter class \"%s\" does not exist. *****\n", reporterClassName);
return -999;
}
id<CDRExampleReporter> reporter = [[[reporterClass alloc] init] autorelease];
int result = runSpecsWithCustomExampleReporter(NULL, reporter);
- [pool drain];
+ [pool drain];
return result;
}
View
53 Source/CDROTestReporter.m
@@ -0,0 +1,53 @@
+#import "CDROTestReporter.h"
+#import "CDRFunctions.h"
+#import "CDRExample.h"
+#import "CDRExampleGroup.h"
+
+@implementation CDROTestReporter
+
+- (id)init {
+ if ((self = [super init])) {
+ failedExamples_ = [[NSMutableArray alloc] init];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [failedExamples_ release];
+ failedExamples_ = nil;
+ [super dealloc];
+}
+
+- (void)reportOnExample:(CDRExample *)example {
+ [super reportOnExample:example];
+
+ if (example.state == CDRExampleStateError || example.state == CDRExampleStateFailed) {
+ [failedExamples_ addObject:example];
+ }
+}
+
+- (void)printStats {
+ const char *startTimeString = [[startTime_ description] cStringUsingEncoding:NSUTF8StringEncoding];
+ printf("Test Suite 'CDROTestReporter' started at %s\n", startTimeString);
+
+ for (CDRExample *example in failedExamples_) {
+ printf("Test Case '-[Spec example]' started.\n");
+ NSString *testResult =
+ [NSString stringWithFormat:@"%@:%d: error: -[Spec example] : %@ # %@",
+ example.failure.fileName, example.failure.lineNumber, example.fullText, example.failure.reason];
+ printf("%s\n", [testResult cStringUsingEncoding:NSUTF8StringEncoding]);
+ printf("Test Case '-[Spec example]' failed (0.001 seconds).\n");
+ }
+
+ const char *endTimeString = [[endTime_ description] cStringUsingEncoding:NSUTF8StringEncoding];
+ printf("Test Suite 'CDROTestReporter' finished at %s.\n", endTimeString);
+
+ const char *testsString = exampleCount_ == 1 ? "test" : "tests";
+ const char *failuresString = exampleCount_ == 1 ? "failure" : "failures";
+ float totalTimeElapsed = [endTime_ timeIntervalSinceDate:startTime_];
+
+ printf("Executed %u %s, with %u %s (0 unexpected) in %.4f (%.4f) seconds\n",
+ exampleCount_, testsString, (unsigned int)failedExamples_.count, failuresString, totalTimeElapsed, totalTimeElapsed);
+}
+
+@end
View
37 Source/CDROTestRunner.m
@@ -0,0 +1,37 @@
+#import "CDROTestRunner.h"
+#import "CDRFunctions.h"
+#import <objc/runtime.h>
+
+@implementation CDROTestRunner
+
+void runTests(id self, SEL _cmd, id ignored) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ // Since we want to have integration with XCode when running tests from inside the IDE
+ // CDROTestReporter needs to be default reporter; however, we can use any other reporter
+ // when running from the command line (e.g. CDRColorizedReporter).
+ Class reporterClass = CDRReporterClassFromEnv("CDROTestReporter");
+ if (!reporterClass) {
+ exit(-999);
+ }
+
+ id<CDRExampleReporter> reporter = [[[reporterClass alloc] init] autorelease];
+ int result = runSpecsWithCustomExampleReporter(NULL, reporter);
+
+ // otest always returns 0 as its exit code even if any test fails;
+ // we need to forcibly exit with correct exit code to make CI happy.
+ [pool drain];
+ exit(result);
+}
+
+// Hijack SenTestProble runTests: class method and run our specs instead.
+// See https://github.com/jy/SenTestingKit for more information.
++ (void)load {
+ Class senTestProbeClass = objc_getClass("SenTestProbe");
+ if (senTestProbeClass) {
+ Class senTestProbeMetaClass = objc_getMetaClass("SenTestProbe");
+ class_replaceMethod(senTestProbeMetaClass, @selector(runTests:), (IMP)runTests, "v@:@");
+ }
+}
+
+@end
View
109 Source/CDRSpecFailure.m
@@ -1,9 +1,116 @@
#import "CDRSpecFailure.h"
+#import <regex.h>
+
+@interface CDRSpecFailure ()
++ (void)extractReason:(NSString **)reason fileName:(NSString **)fileName lineNumber:(int *)lineNumber fromObject:(NSObject *)object;
++ (BOOL)extractReason:(NSString **)reason fileName:(NSString **)fileName lineNumber:(int *)lineNumber fromString:(NSString *)string;
+@end
+
@implementation CDRSpecFailure
+@synthesize fileName = fileName_, lineNumber = lineNumber_;
+
+ (id)specFailureWithReason:(NSString *)reason {
- return [[self class] exceptionWithName:@"Spec failure" reason:reason userInfo:nil];
+ return [[[self alloc] initWithReason:reason] autorelease];
+}
+
++ (id)specFailureWithReason:(NSString *)reason fileName:(NSString *)fileName lineNumber:(int)lineNumber {
+ return [[[self alloc] initWithReason:reason fileName:fileName lineNumber:lineNumber] autorelease];
+}
+
++ (id)specFailureWithRaisedObject:(NSObject *)object {
+ return [[[self alloc] initWithRaisedObject:object] autorelease];
+}
+
+- (id)initWithReason:(NSString *)reason {
+ return [self initWithRaisedObject:reason];
+}
+
+- (id)initWithReason:(NSString *)reason fileName:(NSString *)fileName lineNumber:(int)lineNumber {
+ if ((self = [super initWithName:@"Spec Failure" reason:reason userInfo:nil])) {
+ fileName_ = [fileName retain];
+ lineNumber_ = lineNumber;
+ }
+ return self;
+}
+
+- (id)initWithRaisedObject:(NSObject *)object {
+ NSString *fileName = nil;
+ int lineNumber;
+ NSString *reason = nil;
+ [[self class] extractReason:&reason fileName:&fileName lineNumber:&lineNumber fromObject:object];
+
+ if ((self = [super initWithName:@"Spec Failure" reason:[reason retain] userInfo:nil])) {
+ fileName_ = [fileName retain];
+ lineNumber_ = lineNumber;
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [fileName_ release];
+ [super dealloc];
+}
+
+- (NSString *)description {
+ if (self.fileName) {
+ return [NSString stringWithFormat:@"%@:%d %@", self.fileName, self.lineNumber, self.reason];
+ }
+ return self.reason;
+}
+
+#pragma mark Private Interface
++ (void)extractReason:(NSString **)reason fileName:(NSString **)fileName lineNumber:(int *)lineNumber fromObject:(NSObject *)object {
+ if ([object isKindOfClass:[NSException class]]) {
+ NSDictionary *userInfo = [(NSException *)object userInfo];
+ if ([userInfo objectForKey:@"fileName"] && [userInfo objectForKey:@"lineNumber"]) {
+ *fileName = [userInfo objectForKey:@"fileName"];
+ *lineNumber = [[userInfo objectForKey:@"lineNumber"] intValue];
+ *reason = [(NSException *)object reason];
+ return;
+ }
+ }
+
+ if ([object isKindOfClass:[NSString class]]) {
+ if ([self extractReason:reason fileName:fileName lineNumber:lineNumber fromString:(NSString *)object]) {
+ return;
+ }
+ }
+
+ *lineNumber = 0;
+ *reason = [object description];
+}
+
++ (BOOL)extractReason:(NSString **)reason fileName:(NSString **)fileName lineNumber:(int *)lineNumber fromString:(NSString *)string {
+ static const char *variations[] = {
+ "(.+):([[:digit:]]+)[[:space:]]+(.*)", // File.m:123 reason
+ "(.+)\\(([[:digit:]]+)\\):?[[:space:]]+(.*)" // File.m(123): reason
+ };
+
+ const char *buf = [string UTF8String];
+
+ for (int i=0; i<2; ++i) {
+ regex_t rx;
+ regmatch_t *matches;
+
+ regcomp(&rx, variations[i], REG_EXTENDED);
+ matches = (regmatch_t *)malloc((rx.re_nsub+1) * sizeof(regmatch_t));
+
+ int result = regexec(&rx, buf, rx.re_nsub+1, matches, 0);
+ if (!result) {
+ *fileName = [[[NSString alloc] initWithBytes:(buf + matches[1].rm_so) length:(NSInteger)(matches[1].rm_eo - matches[1].rm_so) encoding:NSUTF8StringEncoding] autorelease];
+ *lineNumber = [[[[NSString alloc] initWithBytes:(buf + matches[2].rm_so) length:(NSInteger)(matches[2].rm_eo - matches[2].rm_so) encoding:NSUTF8StringEncoding] autorelease] intValue];
+ *reason = [[[NSString alloc] initWithBytes:(buf + matches[3].rm_so) length:(NSInteger)(matches[3].rm_eo - matches[3].rm_so) encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ free(matches);
+ regfree(&rx);
+
+ if (!result)
+ return YES;
+ }
+ return NO;
}
@end
View
4 Source/Headers/CDRDefaultReporter.h
@@ -10,6 +10,7 @@
NSMutableArray *failureMessages_;
NSDate *startTime_;
+ NSDate *endTime_;
unsigned int exampleCount_;
}
@end
@@ -24,4 +25,7 @@
- (NSString *)failureMessageForExample:(CDRExample *)example;
- (NSString *)errorToken;
- (NSString *)errorMessageForExample:(CDRExample *)example;
+
+- (void)reportOnExample:(CDRExample *)example;
+- (void)printStats;
@end
View
5 Source/Headers/CDRExample.h
@@ -1,12 +1,13 @@
#import "CDRExampleBase.h"
+#import "CDRSpecFailure.h"
@interface CDRExample : CDRExampleBase {
CDRSpecBlock block_;
CDRExampleState state_;
- NSString *message_;
+ CDRSpecFailure *failure_;
}
-@property (nonatomic, copy) NSString *message;
+@property (nonatomic, retain) CDRSpecFailure *failure;
+ (id)exampleWithText:(NSString *)text andBlock:(CDRSpecBlock)block;
- (id)initWithText:(NSString *)text andBlock:(CDRSpecBlock)block;
View
4 Source/Headers/CDRFunctions.h
@@ -2,6 +2,8 @@
@protocol CDRExampleReporter;
-int runSpecsWithCustomExampleReporter(NSArray *specClasses, id<CDRExampleReporter> runner);
+Class CDRReporterClassFromEnv(const char *defaultReporterClassName);
+
int runAllSpecs();
int runAllSpecsWithCustomExampleReporter(id<CDRExampleReporter> runner);
+int runSpecsWithCustomExampleReporter(NSArray *specClasses, id<CDRExampleReporter> runner);
View
8 Source/Headers/CDROTestReporter.h
@@ -0,0 +1,8 @@
+#import <Foundation/Foundation.h>
+#import "CDRDefaultReporter.h"
+
+@interface CDROTestReporter : CDRDefaultReporter {
+ NSMutableArray *failedExamples_;
+}
+
+@end
View
4 Source/Headers/CDROTestRunner.h
@@ -0,0 +1,4 @@
+#import <Foundation/Foundation.h>
+
+@interface CDROTestRunner : NSObject
+@end
View
16 Source/Headers/CDRSpecFailure.h
@@ -1,5 +1,19 @@
#import <Foundation/Foundation.h>
-@interface CDRSpecFailure : NSException
+@interface CDRSpecFailure : NSException {
+ NSString *fileName_;
+ int lineNumber_;
+}
+
+@property (nonatomic, retain, readonly) NSString *fileName;
+@property (nonatomic, assign, readonly) int lineNumber;
+
+ (id)specFailureWithReason:(NSString *)reason;
++ (id)specFailureWithReason:(NSString *)reason fileName:(NSString *)fileName lineNumber:(int)lineNumber;
++ (id)specFailureWithRaisedObject:(NSObject *)object;
+
+- (id)initWithReason:(NSString *)reason;
+- (id)initWithReason:(NSString *)reason fileName:(NSString *)fileName lineNumber:(int)lineNumber;
+- (id)initWithRaisedObject:(NSObject *)object;
+
@end
View
26 Source/Headers/Matchers/ActualValue.h
@@ -17,35 +17,49 @@ namespace Cedar { namespace Matchers {
ActualValue & operator=(const ActualValue<U> &);
public:
- explicit ActualValue(const T &);
+ explicit ActualValue(const char *, int, const T &);
+ ~ActualValue();
template<typename Matcher> void to(const Matcher &) const;
template<typename Matcher> void to_not(const Matcher &) const;
private:
const T value_;
+ NSString *fileName_;
+ int lineNumber_;
};
template<typename T>
- ActualValue<T>::ActualValue(const T & value) : value_(value) {
+ ActualValue<T>::ActualValue(const char *fileName, int lineNumber, const T & value) : lineNumber_(lineNumber), value_(value) {
+ fileName_ = [[NSString alloc] initWithUTF8String:fileName];
}
template<typename T>
- const ActualValue<T> expect(const T & actualValue) {
- return ActualValue<T>(actualValue);
+ ActualValue<T>::~ActualValue() {
+ [fileName_ release];
+ fileName_ = nil;
}
+ template<typename T>
+ const ActualValue<T> CDR_expect(const char *fileName, int lineNumber, const T & actualValue) {
+ return ActualValue<T>(fileName, lineNumber, actualValue);
+ }
+
+ #ifndef CEDAR_MATCHERS_COMPATIBILITY_MODE
+ #define expect(x) CDR_expect(__FILE__, __LINE__, x)
+ #endif
+
template<typename T> template<typename Matcher>
void ActualValue<T>::to(const Matcher & matcher) const {
if (!matcher.matches(value_)) {
- [[CDRSpecFailure specFailureWithReason:matcher.failure_message()] raise];
+ [[CDRSpecFailure specFailureWithReason:matcher.failure_message() fileName:fileName_ lineNumber:lineNumber_] raise];
}
}
template<typename T> template<typename Matcher>
void ActualValue<T>::to_not(const Matcher & matcher) const {
if (matcher.matches(value_)) {
- [[CDRSpecFailure specFailureWithReason:matcher.negative_failure_message()] raise];
+ [[CDRSpecFailure specFailureWithReason:matcher.negative_failure_message() fileName:fileName_ lineNumber:lineNumber_] raise];
}
}
}}
View
1 Source/Headers/Matchers/Base/BeNil.h
@@ -27,6 +27,7 @@ namespace Cedar { namespace Matchers {
template<typename U>
bool BeNil::matches(const U & actualValue) const {
[[CDRSpecFailure specFailureWithReason:@"Attempt to compare non-pointer type to nil"] raise];
+ return NO;
}
template<typename U>
View
1 Source/Headers/Matchers/Base/BeSameInstanceAs.h
@@ -48,6 +48,7 @@ namespace Cedar { namespace Matchers {
template<typename T> template<typename U>
bool BeSameInstanceAs<T>::matches(const U & actualValue) const {
[[CDRSpecFailure specFailureWithReason:@"Attempt to compare non-pointer type for sameness."] raise];
+ return NO;
}
template<typename T> template<typename U>
View
4 Source/Headers/iPhone/CDROTestIPhoneRunner.h
@@ -0,0 +1,4 @@
+#import <Foundation/Foundation.h>
+
+@interface CDROTestIPhoneRunner : NSObject
+@end
View
13 Source/Headers/iPhone/CedarApplicationDelegate.h
@@ -1,10 +1,21 @@
#import <UIKit/UIKit.h>
+void runSpecsWithinUIApplication();
+
@class CDRExampleReporterViewController;
-@interface CedarApplicationDelegate : NSObject <UIApplicationDelegate> {
+// In some cases CDRIPhoneOTestRunner needs to spin up an instance of Cedar app.
+// It appears that SenTestingKit fails to start up the test when CedarApplicationDelegate
+// is used. Solution is to use a subclass of UIApplicaton.
+@interface CedarApplication : UIApplication {
UIWindow *window_;
CDRExampleReporterViewController *viewController_;
}
+@end
+// Needed for backwards compatibility with existing projects using CedarApplicationDelegate
+@interface CedarApplicationDelegate : NSObject <UIApplicationDelegate> {
+ UIWindow *window_;
+ CDRExampleReporterViewController *viewController_;
+}
@end
View
12 Source/iPhone/CDRExampleReporterViewController.m
@@ -1,7 +1,9 @@
#import "CDRExampleReporterViewController.h"
#import "CDRFunctions.h"
+#import "CedarApplicationDelegate.h"
#import "CDRSpecStatusViewController.h"
#import "CDRDefaultReporter.h"
+#import <objc/runtime.h>
@implementation CDRExampleReporterViewController
@@ -10,15 +12,7 @@ - (void)viewDidLoad {
[super viewDidLoad];
if (getenv("CEDAR_HEADLESS_SPECS")) {
- int exitStatus = runAllSpecs();
-
- UIApplication *application = [UIApplication sharedApplication];
- if ([application respondsToSelector:@selector(_terminateWithStatus:)]) {
- [application performSelector:@selector(_terminateWithStatus:)
- withObject:(id)exitStatus];
- } else {
- exit(exitStatus);
- }
+ runSpecsWithinUIApplication();
} else {
[self performSelectorInBackground:@selector(startSpecs) withObject:NULL];
}
View
63 Source/iPhone/CDROTestIPhoneRunner.m
@@ -0,0 +1,63 @@
+#import <UIKit/UIKit.h>
+#import "CDROTestIPhoneRunner.h"
+#import "CedarApplicationDelegate.h"
+#import "CDRFunctions.h"
+#import <objc/runtime.h>
+
+extern int *_NSGetArgc(void);
+extern char ***_NSGetArgv(void);
+
+@implementation NSBundle (MainBundleHijack)
+static NSBundle *mainBundle__ = nil;
+
+NSBundle *mainBundle(id self, SEL _cmd) {
+ return mainBundle__;
+}
+
++ (void)load {
+ if (!objc_getClass("SenTestProbe"))
+ return;
+
+ BOOL mainBundleIsApp = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".app"];
+ BOOL mainBundleIsOctest = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".octest"];
+
+ if (!mainBundleIsApp && !mainBundleIsOctest) {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ for (NSBundle *bundle in [NSBundle allBundles]) {
+ if ([[bundle bundlePath] hasSuffix:@".octest"]) {
+ mainBundle__ = [bundle retain];
+ Class nsBundleMetaClass = objc_getMetaClass("NSBundle");
+ class_replaceMethod(nsBundleMetaClass, @selector(mainBundle), (IMP)mainBundle, "v@:");
+ }
+ }
+ [pool drain];
+ }
+}
+
+@end
+
+@implementation CDROTestIPhoneRunner
+
+void runTests(id self, SEL _cmd, id ignored) {
+ if ([UIApplication sharedApplication]) { // most likely loaded from the bundle
+ runSpecsWithinUIApplication();
+ } else {
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ const char* argv[] = { "executable", "-RegisterForSystemEvents" };
+ int result = UIApplicationMain(2, (char **)argv, @"CedarApplication", nil);
+
+ [pool release];
+ exit(result);
+ }
+}
+
++ (void)load {
+ Class senTestProbeClass = objc_getClass("SenTestProbe");
+ if (senTestProbeClass) {
+ Class senTestProbeMetaClass = objc_getMetaClass("SenTestProbe");
+ class_replaceMethod(senTestProbeMetaClass, @selector(runTests:), (IMP)runTests, "v@:@");
+ }
+}
+
+@end
View
37 Source/iPhone/CedarApplicationDelegate.m
@@ -1,5 +1,42 @@
#import "CedarApplicationDelegate.h"
#import "CDRExampleReporterViewController.h"
+#import "CDRFunctions.h"
+#import <objc/runtime.h>
+
+void runSpecsWithinUIApplication() {
+ int exitStatus;
+
+ char *defaultReporterClassName = objc_getClass("SenTestProbe") ? "CDROTestReporter" : "CDRDefaultReporter";
+ Class reporterClass = CDRReporterClassFromEnv(defaultReporterClassName);
+
+ if (!reporterClass) {
+ exitStatus = -999;
+ } else {
+ id<CDRExampleReporter> reporter = [[[reporterClass alloc] init] autorelease];
+ exitStatus = runSpecsWithCustomExampleReporter(NULL, reporter);
+ }
+
+ UIApplication *application = [UIApplication sharedApplication];
+ if ([application respondsToSelector:@selector(_terminateWithStatus:)]) {
+ [application performSelector:@selector(_terminateWithStatus:) withObject:(id)exitStatus];
+ } else {
+ exit(exitStatus);
+ }
+}
+
+@implementation CedarApplication
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ window_ = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+
+ viewController_ = [[CDRExampleReporterViewController alloc] init];
+ [window_ addSubview:viewController_.view];
+ [window_ makeKeyAndVisible];
+
+ return NO;
+}
+
+@end
@implementation CedarApplicationDelegate
View
152 Spec/CDRSpecFailureSpec.mm
@@ -0,0 +1,152 @@
+#if TARGET_OS_IPHONE
+// Normally you would include this file out of the framework. However, we're
+// testing the framework here, so including the file from the framework will
+// conflict with the compiler attempting to include the file from the project.
+#import "SpecHelper.h"
+#import "OCMock.h"
+#else
+#import <Cedar/SpecHelper.h>
+#import <OCMock/OCMock.h>
+#endif
+
+#import "CDRSpecFailure.h"
+
+using namespace Cedar::Matchers;
+static NSDictionary *reasonsToFileNames;
+
+SPEC_BEGIN(CDRSpecFailureSpec)
+
+reasonsToFileNames = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"File.h", @"File.h:123 reason",
+ @"C:/Directory/File.h", @"C:/Directory/File.h:123 reason",
+ @"Some Directory/File.m", @"Some Directory/File.m:123 reason",
+ @"Some\\ Directory/File.m", @"Some\\ Directory/File.m:123 reason",
+ @"Some Directory/File.m", @"Some Directory/File.m(123): reason",
+ @"Some Directory (something)/File.m", @"Some Directory (something)/File.m(123) reason",
+ nil];
+
+describe(@"CDRSpecFailure", ^{
+ __block CDRSpecFailure *failure;
+
+ context(@"for a failure instantiated only with a reason", ^{
+ context(@"when file name and line number are not specified inside reason", ^{
+ beforeEach(^{
+ failure = [CDRSpecFailure specFailureWithReason:@"reason"];
+ });
+
+ it(@"should return failure's reason", ^{
+ expect([failure reason]).to(equal(@"reason"));
+ });
+
+ it(@"should return nil for file name", ^{
+ expect([failure fileName]).to(be_nil());
+ });
+
+ it(@"should return 0 for line number", ^{
+ expect([failure lineNumber]).to(equal(0));
+ });
+ });
+
+ context(@"when file name and line number are specified inside reason", ^{
+ for (NSString *reason in reasonsToFileNames) {
+ NSString *fileName = [reasonsToFileNames objectForKey:reason];
+
+ it([NSString stringWithFormat:@"should return reason 'reason' parsed from '%@'", reason], ^{
+ CDRSpecFailure *failure = [CDRSpecFailure specFailureWithReason:reason];
+ expect([failure reason]).to(equal(@"reason"));
+ });
+
+ it([NSString stringWithFormat:@"should return file name '%@' parsed from '%@'", fileName, reason], ^{
+ CDRSpecFailure *failure = [CDRSpecFailure specFailureWithReason:reason];
+ expect([failure fileName]).to(equal(fileName));
+ });
+
+ it([NSString stringWithFormat:@"should return line number '123' parsed from '%@'", reason], ^{
+ CDRSpecFailure *failure = [CDRSpecFailure specFailureWithReason:reason];
+ expect([failure lineNumber]).to(equal(123));
+ });
+ }
+ });
+ });
+
+ context(@"for a failure instantiated with reason, file name and line number", ^{
+ beforeEach(^{
+ failure = [CDRSpecFailure specFailureWithReason:@"reason" fileName:@"File.m" lineNumber:123];
+ });
+
+ it(@"should return failure's reason", ^{
+ expect([failure reason]).to(equal(@"reason"));
+ });
+
+ it(@"should return failure's file name", ^{
+ expect([failure fileName]).to(equal(@"File.m"));
+ });
+
+ it(@"should return failure's line number", ^{
+ expect([failure lineNumber]).to(equal(123));
+ });
+ });
+
+ context(@"for a failure instantiated with a raised object", ^{
+ context(@"when raised object is a subclass of NSException", ^{
+ context(@"when file name and line number are specified in exception's userInfo", ^{
+ beforeEach(^{
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:@"File.m", @"fileName", [NSNumber numberWithInt:123], @"lineNumber", nil];
+ NSException *exception = [NSException exceptionWithName:nil reason:@"exception reason" userInfo:userInfo];
+ failure = [CDRSpecFailure specFailureWithRaisedObject:exception];
+ });
+
+ it(@"should return exception's reason", ^{
+ expect([failure reason]).to(equal(@"exception reason"));
+ });
+
+ it(@"should return file name specified in exception's userInfo", ^{
+ expect([failure fileName]).to(equal(@"File.m"));
+ });
+
+ it(@"should return line number specified in exception's userInfo", ^{
+ expect([failure lineNumber]).to(equal(123));
+ });
+ });
+
+ context(@"when file name and line number are not specified in userInfo of exception", ^{
+ beforeEach(^{
+ NSException *exception = [NSException exceptionWithName:nil reason:@"exception reason" userInfo:nil];
+ failure = [CDRSpecFailure specFailureWithRaisedObject:exception];
+ });
+
+ it(@"should return raised object's reason", ^{
+ expect([failure reason]).to(equal(@"exception reason"));
+ });
+
+ it(@"should return nil for file name", ^{
+ expect([failure fileName]).to(be_nil());
+ });
+
+ it(@"should return 0 for line number", ^{
+ expect([failure lineNumber]).to(equal(0));
+ });
+ });
+ });
+
+ context(@"when raised object is not a subclass of NSException", ^{
+ beforeEach(^{
+ failure = [CDRSpecFailure specFailureWithRaisedObject:[NSNumber numberWithInt:19901113]];
+ });
+
+ it(@"should return raised object's description for reason", ^{
+ expect([failure reason]).to(equal(@"19901113"));
+ });
+
+ it(@"should return nil for file name", ^{
+ expect([failure fileName]).to(be_nil());
+ });
+
+ it(@"should return 0 for line number", ^{
+ expect([failure lineNumber]).to(equal(0));
+ });
+ });
+ });
+});
+
+SPEC_END

0 comments on commit b1acc18

Please sign in to comment.