Skip to content

Commit

Permalink
Demangle QuickSpec test names as much as possible
Browse files Browse the repository at this point in the history
  • Loading branch information
younata committed Apr 21, 2023
1 parent e299102 commit 7ef5a2e
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 16 deletions.
4 changes: 4 additions & 0 deletions Sources/Quick/Async/AsyncSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ open class AsyncSpec: XCTestCase {
}
let implementation = imp_implementationWithBlock(block as Any)

// The Objc version of QuickSpec can override `testInvocations`, allowing it to
// omit the leading "test ". Unfortunately, there's not a similar API available
// to Swift. So the compromise is this.
let originalName = "test \(example.name)"
var selectorName = originalName
var index: UInt = 2
Expand All @@ -93,6 +96,7 @@ open class AsyncSpec: XCTestCase {
return selector
}
#endif // canImport(Darwin)

#if !canImport(Darwin)
public required init() {
super.init(name: "", testClosure: { _ in })
Expand Down
9 changes: 6 additions & 3 deletions Sources/Quick/QuickSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,15 @@ open class QuickSpec: QuickSpecBase {
}
let implementation = imp_implementationWithBlock(block as Any)

let originalName = example.name.c99ExtendedIdentifier
// The Objc version of QuickSpec can override `testInvocations`, allowing it to
// omit the leading "test ". Unfortunately, there's not a similar API available
// to Swift. So the compromise is this.
let originalName = "test \(example.name)"
var selectorName = originalName
var index: UInt = 2

while selectorNames.contains(selectorName) {
selectorName = String(format: "%@_%tu", originalName, index)
selectorName = String(format: "%@ (%tu)", originalName, index)
index += 1
}

Expand All @@ -96,7 +99,7 @@ open class QuickSpec: QuickSpecBase {

return selector
}
#endif
#endif // canImport(Darwin)

#if !canImport(Darwin)
public required init() {
Expand Down
14 changes: 10 additions & 4 deletions Sources/QuickObjectiveC/QuickSpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ + (void)buildExamplesIfNeeded {
}];
}

- (void)example_thing:(void (^)(void))completionHandler {}

/**
QuickSpec uses this method to dynamically define a new instance method for the
given example. The instance method runs the example, catching any exceptions.
Expand All @@ -136,12 +134,20 @@ + (SEL)addInstanceMethodForExample:(Example *)example classSelectorNames:(NSMuta

const char *types = [[NSString stringWithFormat:@"%s%s%s", @encode(void), @encode(id), @encode(SEL)] UTF8String];

NSString *originalName = [QCKObjCStringUtils c99ExtendedIdentifierFrom:example.name];
// Unlike the Swift version of QuickSpec (and AsyncSpec), because the Objc version
// of QuickSpec can override `testInvocations`, we don't need to rely on XCTest
// using the "all methods that start with 'test'" heuristic to determine which
// methods to call when running a test.
// Meaning that cocoapods, carthage, et. al. get a slightly cleaner test list
// while SPM has to prepend 'test' to all the created tests.
// This is only a thing on apple platforms - platforms using the open source
// version of XCTest don't have this issue.
NSString *originalName = example.name;
NSString *selectorName = originalName;
NSUInteger i = 2;

while ([selectorNames containsObject:selectorName]) {
selectorName = [NSString stringWithFormat:@"%@_%tu", originalName, i++];
selectorName = [NSString stringWithFormat:@"%@ (%tu)", originalName, i++];
}

[selectorNames addObject:selectorName];
Expand Down
32 changes: 25 additions & 7 deletions Tests/QuickTests/QuickTests/FunctionalTests/ItTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,25 @@ class FunctionalTests_ItSpec: QuickSpec {

beforeEach {
allSelectors = FunctionalTests_ItSpec.allSelectors()
.filter { $0.hasPrefix("when_an_example_has_a_unique_name__") }
.filter { $0.contains("when an example has a unique name") }
.map {
// SPM-version needs to prepend "test" to all test methods created.
if $0.hasPrefix("test ") {
var method = $0
method.removeFirst(5)
return method
}
return $0
}
.sorted(by: <)
}

it("has a unique name") {}

it("doesn't add multiple selectors for it") {
expect(allSelectors) == [
"when_an_example_has_a_unique_name__doesn_t_add_multiple_selectors_for_it",
"when_an_example_has_a_unique_name__has_a_unique_name",
"when an example has a unique name, doesn't add multiple selectors for it",
"when an example has a unique name, has a unique name",
]
}
}
Expand All @@ -41,7 +50,16 @@ class FunctionalTests_ItSpec: QuickSpec {

beforeEach {
allSelectors = FunctionalTests_ItSpec.allSelectors()
.filter { $0.hasPrefix("when_two_examples_have_the_exact_name__") }
.filter { $0.contains("when two examples have the exact name") }
.map {
// SPM-version needs to prepend "test" to all test methods created.
if $0.hasPrefix("test ") {
var method = $0
method.removeFirst(5)
return method
}
return $0
}
.sorted(by: <)
}

Expand All @@ -50,9 +68,9 @@ class FunctionalTests_ItSpec: QuickSpec {

it("makes a unique name for each of the above") {
expect(allSelectors) == [
"when_two_examples_have_the_exact_name__has_exactly_the_same_name",
"when_two_examples_have_the_exact_name__has_exactly_the_same_name_2",
"when_two_examples_have_the_exact_name__makes_a_unique_name_for_each_of_the_above",
"when two examples have the exact name, has exactly the same name",
"when two examples have the exact name, has exactly the same name (2)",
"when two examples have the exact name, makes a unique name for each of the above",
]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
it(@"is a test with a unique name", ^{
NSSet<NSString*> *allSelectors = [FunctionalTests_ItSpec_ObjC allSelectors];

expect(allSelectors).to(contain(@"is_a_test_with_a_unique_name"));
expect(allSelectors).toNot(contain(@"is_a_test_with_a_unique_name_2"));
expect(allSelectors).to(contain(@"is a test with a unique name"));
expect(allSelectors).toNot(contain(@"is a test with a unique name (2)"));
});

it(@"is executed on the main thread", ^{
Expand Down

0 comments on commit 7ef5a2e

Please sign in to comment.