Skip to content

Commit feffdec

Browse files
committed
Add a mechanism to fallback to encoded test selector names.
Set the QUICK_USE_LEGACY_TEST_SELECTOR_NAMES environment variable to any value, and Quick will generate the legacy-style test selector names.
1 parent e6583c4 commit feffdec

6 files changed

Lines changed: 129 additions & 23 deletions

File tree

Quick.xcodeproj/project.pbxproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,12 @@
212212
89E3F80E29F5AA8A00EA7370 /* AsyncSpec+testMethodSelectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 89E3F80A29F5A94400EA7370 /* AsyncSpec+testMethodSelectors.m */; };
213213
89E3F80F29F5AA8B00EA7370 /* AsyncSpec+testMethodSelectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 89E3F80A29F5A94400EA7370 /* AsyncSpec+testMethodSelectors.m */; };
214214
89E3F81029F5AA8C00EA7370 /* AsyncSpec+testMethodSelectors.m in Sources */ = {isa = PBXBuildFile; fileRef = 89E3F80A29F5A94400EA7370 /* AsyncSpec+testMethodSelectors.m */; };
215+
89E3F81229F76EC100EA7370 /* TestSelectorNameProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E3F81129F76EC100EA7370 /* TestSelectorNameProvider.swift */; };
216+
89E3F81329F76EC100EA7370 /* TestSelectorNameProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E3F81129F76EC100EA7370 /* TestSelectorNameProvider.swift */; };
217+
89E3F81429F76EC100EA7370 /* TestSelectorNameProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E3F81129F76EC100EA7370 /* TestSelectorNameProvider.swift */; };
218+
89E3F81929F773E200EA7370 /* TestSelectorNameProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E3F81529F7738B00EA7370 /* TestSelectorNameProviderTests.swift */; };
219+
89E3F81A29F773E300EA7370 /* TestSelectorNameProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E3F81529F7738B00EA7370 /* TestSelectorNameProviderTests.swift */; };
220+
89E3F81B29F773E400EA7370 /* TestSelectorNameProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E3F81529F7738B00EA7370 /* TestSelectorNameProviderTests.swift */; };
215221
89E8E59B290045AE003B08F0 /* Example.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E8E59A290045AE003B08F0 /* Example.swift */; };
216222
89E8E59C290045AE003B08F0 /* Example.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E8E59A290045AE003B08F0 /* Example.swift */; };
217223
89E8E59D290045AE003B08F0 /* Example.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89E8E59A290045AE003B08F0 /* Example.swift */; };
@@ -486,6 +492,8 @@
486492
89D2C670284D9E18004B108C /* SubclassDetectionSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubclassDetectionSpec.swift; sourceTree = "<group>"; };
487493
89D2C674284DA77D004B108C /* SubclassDetection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubclassDetection.swift; sourceTree = "<group>"; };
488494
89E3F80A29F5A94400EA7370 /* AsyncSpec+testMethodSelectors.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "AsyncSpec+testMethodSelectors.m"; sourceTree = "<group>"; };
495+
89E3F81129F76EC100EA7370 /* TestSelectorNameProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSelectorNameProvider.swift; sourceTree = "<group>"; };
496+
89E3F81529F7738B00EA7370 /* TestSelectorNameProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSelectorNameProviderTests.swift; sourceTree = "<group>"; };
489497
89E8E59A290045AE003B08F0 /* Example.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Example.swift; sourceTree = "<group>"; };
490498
8D010A561C11726F00633E2B /* DescribeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DescribeTests.swift; sourceTree = "<group>"; };
491499
AED9C8621CC8A7BD00432F62 /* CrossReferencingSpecs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrossReferencingSpecs.swift; sourceTree = "<group>"; };
@@ -856,6 +864,7 @@
856864
DAB0136E19FC4315006AFBEE /* SharedExamples+BeforeEachTests.swift */,
857865
DA8F91AA19F3299E006F6675 /* SharedExamplesTests.swift */,
858866
89D2C670284D9E18004B108C /* SubclassDetectionSpec.swift */,
867+
89E3F81529F7738B00EA7370 /* TestSelectorNameProviderTests.swift */,
859868
);
860869
path = FunctionalTests;
861870
sourceTree = "<group>";
@@ -966,6 +975,7 @@
966975
CE57CED91C430BD200D63004 /* QuickSelectedTestSuiteBuilder.swift */,
967976
CE57CED81C430BD200D63004 /* NSBundle+CurrentTestBundle.swift */,
968977
DED3036A1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift */,
978+
89E3F81129F76EC100EA7370 /* TestSelectorNameProvider.swift */,
969979
CE57CEDB1C430BD200D63004 /* URL+FileName.swift */,
970980
34C586071C4AC5E500D4F057 /* ErrorUtility.swift */,
971981
DAEB6B911943873100289F44 /* Supporting Files */,
@@ -1507,6 +1517,7 @@
15071517
1F118CFB1BDCA536005013A2 /* QuickConfiguration.m in Sources */,
15081518
34C5860A1C4AC5E500D4F057 /* ErrorUtility.swift in Sources */,
15091519
1F118CFF1BDCA536005013A2 /* QCKDSL.m in Sources */,
1520+
89E3F81429F76EC100EA7370 /* TestSelectorNameProvider.swift in Sources */,
15101521
89D2C677284DA77D004B108C /* SubclassDetection.swift in Sources */,
15111522
DED3036D1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift in Sources */,
15121523
1E4441D62988A422009AE584 /* AsyncDSL.swift in Sources */,
@@ -1568,6 +1579,7 @@
15681579
1F118D191BDCA556005013A2 /* AfterEachTests+ObjC.m in Sources */,
15691580
1F118D221BDCA556005013A2 /* SharedExamples+BeforeEachTests.swift in Sources */,
15701581
AED9C8651CC8A7BD00432F62 /* CrossReferencingSpecs.swift in Sources */,
1582+
89E3F81929F773E200EA7370 /* TestSelectorNameProviderTests.swift in Sources */,
15711583
1F118D211BDCA556005013A2 /* SharedExamplesTests+ObjC.m in Sources */,
15721584
1F118D201BDCA556005013A2 /* SharedExamplesTests.swift in Sources */,
15731585
1F118D0C1BDCA543005013A2 /* QuickConfigurationTests.m in Sources */,
@@ -1625,6 +1637,7 @@
16251637
34C586091C4AC5E500D4F057 /* ErrorUtility.swift in Sources */,
16261638
DA408BE719FF5599005DF92A /* SuiteHooks.swift in Sources */,
16271639
34F375BA19515CA700CE1B99 /* QuickSpec.m in Sources */,
1640+
89E3F81329F76EC100EA7370 /* TestSelectorNameProvider.swift in Sources */,
16281641
DED3036C1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift in Sources */,
16291642
89D2C676284DA77D004B108C /* SubclassDetection.swift in Sources */,
16301643
1E4441CF2988A421009AE584 /* AsyncDSL.swift in Sources */,
@@ -1686,6 +1699,7 @@
16861699
CE4A578E1EA7251C0063C0D4 /* FunctionalTests_BehaviorTests_Behaviors.swift in Sources */,
16871700
DA8F91AF19F32CE2006F6675 /* FunctionalTests_SharedExamplesTests_SharedExamples.swift in Sources */,
16881701
DAE714FB19FF682A005905B8 /* Configuration+AfterEachTests.swift in Sources */,
1702+
89E3F81A29F773E300EA7370 /* TestSelectorNameProviderTests.swift in Sources */,
16891703
AED9C8641CC8A7BD00432F62 /* CrossReferencingSpecs.swift in Sources */,
16901704
471590411A488F3F00FBA644 /* PendingTests+ObjC.m in Sources */,
16911705
DA8F919E19F31921006F6675 /* FailureTests+ObjC.m in Sources */,
@@ -1793,6 +1807,7 @@
17931807
34C586081C4AC5E500D4F057 /* ErrorUtility.swift in Sources */,
17941808
1E44417D2988A362009AE584 /* AsyncWorld.swift in Sources */,
17951809
DED3036B1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift in Sources */,
1810+
89E3F81229F76EC100EA7370 /* TestSelectorNameProvider.swift in Sources */,
17961811
89D2C675284DA77D004B108C /* SubclassDetection.swift in Sources */,
17971812
DA408BE619FF5599005DF92A /* SuiteHooks.swift in Sources */,
17981813
34F375B919515CA700CE1B99 /* QuickSpec.m in Sources */,
@@ -1854,6 +1869,7 @@
18541869
4748E8941A6AEBB3009EC992 /* SharedExamples+BeforeEachTests+ObjC.m in Sources */,
18551870
DA8F91AE19F32CE2006F6675 /* FunctionalTests_SharedExamplesTests_SharedExamples.swift in Sources */,
18561871
DAE714FA19FF682A005905B8 /* Configuration+AfterEachTests.swift in Sources */,
1872+
89E3F81B29F773E400EA7370 /* TestSelectorNameProviderTests.swift in Sources */,
18571873
AED9C8631CC8A7BD00432F62 /* CrossReferencingSpecs.swift in Sources */,
18581874
471590401A488F3F00FBA644 /* PendingTests+ObjC.m in Sources */,
18591875
DA8F919D19F31921006F6675 /* FailureTests+ObjC.m in Sources */,

Sources/Quick/Async/AsyncSpec.swift

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,7 @@ open class AsyncSpec: AsyncSpecBase {
9090
}
9191
let implementation = imp_implementationWithBlock(block as Any)
9292

93-
let originalName = example.name
94-
var selectorName = originalName
95-
var index: UInt = 2
96-
97-
while selectorNames.contains(selectorName) {
98-
selectorName = String(format: "%@ (%tu)", originalName, index)
99-
index += 1
100-
}
93+
let selectorName = TestSelectorNameProvider.testSelectorName(forAsync: example, classSelectorNames: selectorNames)
10194

10295
selectorNames.insert(selectorName)
10396

Sources/Quick/QuickSpec.swift

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,7 @@ open class QuickSpec: QuickSpecBase {
8080
}
8181
let implementation = imp_implementationWithBlock(block as Any)
8282

83-
let originalName = example.name
84-
var selectorName = originalName
85-
var index: UInt = 2
86-
87-
while selectorNames.contains(selectorName) {
88-
selectorName = String(format: "%@ (%tu)", originalName, index)
89-
index += 1
90-
}
83+
let selectorName = TestSelectorNameProvider.testSelectorName(for: example, classSelectorNames: selectorNames)
9184

9285
selectorNames.insert(selectorName)
9386

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import Foundation
2+
3+
@objc internal final class TestSelectorNameProvider: NSObject {
4+
@objc static func testSelectorName(for example: Example, classSelectorNames selectorNames: Set<String>) -> String {
5+
if useLegacyStyleTestSelectorNames {
6+
return legacyStyleTestSelectorName(exampleName: example.name, classSelectorNames: selectorNames, isAsync: false)
7+
} else {
8+
return humanReadableTestSelectorName(exampleName: example.name, classSelectorNames: selectorNames)
9+
}
10+
}
11+
12+
static func testSelectorName(forAsync example: AsyncExample, classSelectorNames selectorNames: Set<String>) -> String {
13+
if useLegacyStyleTestSelectorNames {
14+
return legacyStyleTestSelectorName(exampleName: example.name, classSelectorNames: selectorNames, isAsync: true)
15+
} else {
16+
return humanReadableTestSelectorName(exampleName: example.name, classSelectorNames: selectorNames)
17+
}
18+
}
19+
20+
internal static var useLegacyStyleTestSelectorNames: Bool = {
21+
ProcessInfo.processInfo.environment["QUICK_USE_LEGACY_TEST_SELECTOR_NAMES"] != nil
22+
}()
23+
24+
private static func legacyStyleTestSelectorName(exampleName: String, classSelectorNames selectorNames: Set<String>, isAsync: Bool) -> String {
25+
let originalName = exampleName.c99ExtendedIdentifier
26+
var selectorName = originalName
27+
var index: UInt = 2
28+
29+
var proposedName = isAsync ? selectorName.appending(":") : selectorName
30+
31+
while selectorNames.contains(proposedName) {
32+
selectorName = String(format: "%@_%tu", originalName, index)
33+
proposedName = isAsync ? selectorName.appending(":") : selectorName
34+
index += 1
35+
}
36+
37+
return proposedName
38+
}
39+
40+
private static func humanReadableTestSelectorName(exampleName: String, classSelectorNames selectorNames: Set<String>) -> String {
41+
var selectorName = exampleName
42+
var index: UInt = 2
43+
44+
while selectorNames.contains(selectorName) {
45+
selectorName = String(format: "%@ (%tu)", exampleName, index)
46+
index += 1
47+
}
48+
return selectorName
49+
}
50+
}

Sources/QuickObjectiveC/QuickSpec.m

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,7 @@ + (SEL)addInstanceMethodForExample:(Example *)example classSelectorNames:(NSMuta
134134

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

137-
NSString *originalName = example.name;
138-
NSString *selectorName = originalName;
139-
NSUInteger i = 2;
140-
141-
while ([selectorNames containsObject:selectorName]) {
142-
selectorName = [NSString stringWithFormat:@"%@ (%tu)", originalName, i++];
143-
}
137+
NSString *selectorName = [TestSelectorNameProvider testSelectorNameFor:example classSelectorNames:selectorNames];
144138

145139
[selectorNames addObject:selectorName];
146140

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import Nimble
2+
import XCTest
3+
@testable import Quick
4+
5+
final class TestSelectorNameProviderTests: XCTestCase {
6+
let example = Example(description: "doesn't do the incorrect behavior 🤪", callsite: Callsite(file: #file, line: #line), flags: [:], closure: {})
7+
let asyncExample = AsyncExample(description: "doesn't do the incorrect behavior 🤨, but async!", callsite: Callsite(file: #file, line: #line), flags: [:], closure: {})
8+
9+
var originalUseLegacyStyleTestNames: Bool = false
10+
11+
override func setUp() {
12+
self.originalUseLegacyStyleTestNames = TestSelectorNameProvider.useLegacyStyleTestSelectorNames
13+
}
14+
15+
override func tearDown() {
16+
TestSelectorNameProvider.useLegacyStyleTestSelectorNames = originalUseLegacyStyleTestNames
17+
}
18+
19+
// MARK: - Sync Examples
20+
func testWithExampleAndHumanReadableCreatesHumanReadableTestSelectors() {
21+
expect(TestSelectorNameProvider.testSelectorName(for: self.example, classSelectorNames: [])).to(equal("doesn't do the incorrect behavior 🤪"))
22+
}
23+
24+
func testWithExampleAndHumanReadableDoesntAllowDuplicateSelectors() {
25+
expect(TestSelectorNameProvider.testSelectorName(for: self.example, classSelectorNames: ["doesn't do the incorrect behavior 🤪"])).to(equal("doesn't do the incorrect behavior 🤪 (2)"))
26+
}
27+
28+
func testWithExampleAndLegacyCreatesLegacyTestSelectors() {
29+
TestSelectorNameProvider.useLegacyStyleTestSelectorNames = true
30+
31+
expect(TestSelectorNameProvider.testSelectorName(for: self.example, classSelectorNames: [])).to(equal("doesn_t_do_the_incorrect_behavior__"))
32+
}
33+
34+
func testWithExampleAndLegacyDoesntAllowDuplicateSelectors() {
35+
TestSelectorNameProvider.useLegacyStyleTestSelectorNames = true
36+
37+
expect(TestSelectorNameProvider.testSelectorName(for: self.example, classSelectorNames: ["doesn_t_do_the_incorrect_behavior__"])).to(equal("doesn_t_do_the_incorrect_behavior___2"))
38+
}
39+
40+
// MARK: - Async Examples
41+
func testWithAsyncExampleAndHumanReadableCreatesHumanReadableTestSelectors() {
42+
expect(TestSelectorNameProvider.testSelectorName(forAsync: self.asyncExample, classSelectorNames: [])).to(equal("doesn't do the incorrect behavior 🤨, but async!"))
43+
}
44+
45+
func testWithAsyncExampleAndHumanReadableDoesntAllowDuplicateSelectors() {
46+
expect(TestSelectorNameProvider.testSelectorName(forAsync: self.asyncExample, classSelectorNames: ["doesn't do the incorrect behavior 🤨, but async!"])).to(equal("doesn't do the incorrect behavior 🤨, but async! (2)"))
47+
}
48+
49+
func testWithAsyncExampleAndLegacyCreatesLegacyTestSelectors() {
50+
TestSelectorNameProvider.useLegacyStyleTestSelectorNames = true
51+
52+
expect(TestSelectorNameProvider.testSelectorName(forAsync: self.asyncExample, classSelectorNames: [])).to(equal("doesn_t_do_the_incorrect_behavior____but_async_:"))
53+
}
54+
55+
func testWithAsyncExampleAndLegacyDoesntAllowDuplicateSelectors() {
56+
TestSelectorNameProvider.useLegacyStyleTestSelectorNames = true
57+
58+
expect(TestSelectorNameProvider.testSelectorName(forAsync: self.asyncExample, classSelectorNames: ["doesn_t_do_the_incorrect_behavior____but_async_:"])).to(equal("doesn_t_do_the_incorrect_behavior____but_async__2:"))
59+
}
60+
}

0 commit comments

Comments
 (0)