Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unique identifiers for all test examples #771

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions Quick.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@
7B5358CE1C3D4FBC00A23FAA /* ContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5358CA1C3D4E2A00A23FAA /* ContextTests.swift */; };
7B5358CF1C3D4FBE00A23FAA /* ContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5358CA1C3D4E2A00A23FAA /* ContextTests.swift */; };
7B5358D01C3D4FC000A23FAA /* ContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B5358CA1C3D4E2A00A23FAA /* ContextTests.swift */; };
7BB41F3C202A87E20064470A /* ExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BB41F3B202A87E20064470A /* ExampleTests.swift */; };
7BB41F3D202A87E20064470A /* ExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BB41F3B202A87E20064470A /* ExampleTests.swift */; };
7BB41F3E202A87E20064470A /* ExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BB41F3B202A87E20064470A /* ExampleTests.swift */; };
8D010A571C11726F00633E2B /* DescribeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D010A561C11726F00633E2B /* DescribeTests.swift */; };
8D010A581C11726F00633E2B /* DescribeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D010A561C11726F00633E2B /* DescribeTests.swift */; };
8D010A591C11726F00633E2B /* DescribeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D010A561C11726F00633E2B /* DescribeTests.swift */; };
Expand Down Expand Up @@ -237,9 +240,9 @@
DAEB6B9A1943873100289F44 /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEB6B8E1943873100289F44 /* Quick.framework */; };
DAF28BC31A4DB8EC00A5D9BF /* FocusedTests+ObjC.m in Sources */ = {isa = PBXBuildFile; fileRef = DAF28BC21A4DB8EC00A5D9BF /* FocusedTests+ObjC.m */; };
DAF28BC41A4DB8EC00A5D9BF /* FocusedTests+ObjC.m in Sources */ = {isa = PBXBuildFile; fileRef = DAF28BC21A4DB8EC00A5D9BF /* FocusedTests+ObjC.m */; };
DED3036B1DF6C66B0041394E /* NSString+C99ExtendedIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED3036A1DF6C66B0041394E /* NSString+C99ExtendedIdentifier.swift */; };
DED3036C1DF6C66B0041394E /* NSString+C99ExtendedIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED3036A1DF6C66B0041394E /* NSString+C99ExtendedIdentifier.swift */; };
DED3036D1DF6C66B0041394E /* NSString+C99ExtendedIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED3036A1DF6C66B0041394E /* NSString+C99ExtendedIdentifier.swift */; };
DED3036B1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED3036A1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift */; };
DED3036C1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED3036A1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift */; };
DED3036D1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED3036A1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift */; };
DED3037D1DF6CF140041394E /* BundleModuleNameTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED3037C1DF6CF140041394E /* BundleModuleNameTests.swift */; };
DED3037E1DF6CF140041394E /* BundleModuleNameTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED3037C1DF6CF140041394E /* BundleModuleNameTests.swift */; };
DED3037F1DF6CF140041394E /* BundleModuleNameTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED3037C1DF6CF140041394E /* BundleModuleNameTests.swift */; };
Expand Down Expand Up @@ -495,6 +498,7 @@
64076D241D6D80B500E2B499 /* AfterSuiteTests+ObjC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AfterSuiteTests+ObjC.m"; sourceTree = "<group>"; };
7B44ADBD1C5444940007AF2E /* HooksPhase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HooksPhase.swift; sourceTree = "<group>"; };
7B5358CA1C3D4E2A00A23FAA /* ContextTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextTests.swift; sourceTree = "<group>"; };
7BB41F3B202A87E20064470A /* ExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleTests.swift; sourceTree = "<group>"; };
8D010A561C11726F00633E2B /* DescribeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DescribeTests.swift; sourceTree = "<group>"; };
96327C611C56E90C00405AB3 /* QuickSpec+QuickSpec_MethodList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "QuickSpec+QuickSpec_MethodList.h"; sourceTree = "<group>"; };
96327C621C56E90C00405AB3 /* QuickSpec+QuickSpec_MethodList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "QuickSpec+QuickSpec_MethodList.m"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -553,7 +557,7 @@
DAEB6B991943873100289F44 /* Quick - macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Quick - macOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
DAEB6B9F1943873100289F44 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DAF28BC21A4DB8EC00A5D9BF /* FocusedTests+ObjC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "FocusedTests+ObjC.m"; sourceTree = "<group>"; };
DED3036A1DF6C66B0041394E /* NSString+C99ExtendedIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSString+C99ExtendedIdentifier.swift"; sourceTree = "<group>"; };
DED3036A1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+C99ExtendedIdentifier.swift"; sourceTree = "<group>"; };
DED3037C1DF6CF140041394E /* BundleModuleNameTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BundleModuleNameTests.swift; sourceTree = "<group>"; };
F8100E901A1E4447007595ED /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -793,6 +797,7 @@
DED3037C1DF6CF140041394E /* BundleModuleNameTests.swift */,
CE4A57891EA5DC270063C0D4 /* BehaviorTests.swift */,
DA5CBB471EAFA55800297C9E /* CurrentSpecTests.swift */,
7BB41F3B202A87E20064470A /* ExampleTests.swift */,
);
path = FunctionalTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -894,7 +899,7 @@
CE57CEDA1C430BD200D63004 /* QuickTestSuite.swift */,
CE57CED91C430BD200D63004 /* QuickSelectedTestSuiteBuilder.swift */,
CE57CED81C430BD200D63004 /* NSBundle+CurrentTestBundle.swift */,
DED3036A1DF6C66B0041394E /* NSString+C99ExtendedIdentifier.swift */,
DED3036A1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift */,
CE57CEDB1C430BD200D63004 /* URL+FileName.swift */,
34C586071C4AC5E500D4F057 /* ErrorUtility.swift */,
DAEB6B911943873100289F44 /* Supporting Files */,
Expand Down Expand Up @@ -1427,7 +1432,7 @@
34C5860A1C4AC5E500D4F057 /* ErrorUtility.swift in Sources */,
1F118D041BDCA536005013A2 /* Example.swift in Sources */,
1F118CFF1BDCA536005013A2 /* QCKDSL.m in Sources */,
DED3036D1DF6C66B0041394E /* NSString+C99ExtendedIdentifier.swift in Sources */,
DED3036D1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift in Sources */,
CE590E211C431FE400253D19 /* NSBundle+CurrentTestBundle.swift in Sources */,
1F118D071BDCA536005013A2 /* Callsite.swift in Sources */,
CE590E231C431FE400253D19 /* XCTestSuite+QuickTestSuiteBuilder.m in Sources */,
Expand All @@ -1452,6 +1457,7 @@
files = (
1F118D381BDCA6E1005013A2 /* Configuration+BeforeEachTests.swift in Sources */,
1F118D121BDCA556005013A2 /* ItTests.swift in Sources */,
7BB41F3E202A87E20064470A /* ExampleTests.swift in Sources */,
1F118D1C1BDCA556005013A2 /* BeforeSuiteTests.swift in Sources */,
1F118D1D1BDCA556005013A2 /* BeforeSuiteTests+ObjC.m in Sources */,
1F118D0E1BDCA547005013A2 /* QCKSpecRunner.m in Sources */,
Expand Down Expand Up @@ -1509,7 +1515,7 @@
34C586091C4AC5E500D4F057 /* ErrorUtility.swift in Sources */,
DA408BE719FF5599005DF92A /* SuiteHooks.swift in Sources */,
34F375BA19515CA700CE1B99 /* QuickSpec.m in Sources */,
DED3036C1DF6C66B0041394E /* NSString+C99ExtendedIdentifier.swift in Sources */,
DED3036C1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift in Sources */,
CE590E1C1C431FE300253D19 /* NSBundle+CurrentTestBundle.swift in Sources */,
DAE7150119FF6A62005905B8 /* QuickConfiguration.m in Sources */,
CE590E1E1C431FE300253D19 /* XCTestSuite+QuickTestSuiteBuilder.m in Sources */,
Expand All @@ -1535,6 +1541,7 @@
files = (
DAE714F819FF6812005905B8 /* Configuration+AfterEach.swift in Sources */,
DAA7C0D719F777EB0093D1D9 /* BeforeEachTests.swift in Sources */,
7BB41F3D202A87E20064470A /* ExampleTests.swift in Sources */,
DA8F919A19F31680006F6675 /* QCKSpecRunner.m in Sources */,
DA8940F11B35B1FA00161061 /* FailureUsingXCTAssertTests+ObjC.m in Sources */,
4728253C1A5EECCE008DC74F /* SharedExamplesTests+ObjC.m in Sources */,
Expand Down Expand Up @@ -1631,7 +1638,7 @@
DA02C91919A8073100093156 /* ExampleMetadata.swift in Sources */,
CE57CEDF1C430BD200D63004 /* QuickTestSuite.swift in Sources */,
34C586081C4AC5E500D4F057 /* ErrorUtility.swift in Sources */,
DED3036B1DF6C66B0041394E /* NSString+C99ExtendedIdentifier.swift in Sources */,
DED3036B1DF6C66B0041394E /* String+C99ExtendedIdentifier.swift in Sources */,
DA408BE619FF5599005DF92A /* SuiteHooks.swift in Sources */,
34F375B919515CA700CE1B99 /* QuickSpec.m in Sources */,
CE57CEE11C430BD200D63004 /* XCTestSuite+QuickTestSuiteBuilder.m in Sources */,
Expand All @@ -1657,6 +1664,7 @@
files = (
DAE714F719FF6812005905B8 /* Configuration+AfterEach.swift in Sources */,
DAB067E919F7801C00F970AC /* BeforeEachTests.swift in Sources */,
7BB41F3C202A87E20064470A /* ExampleTests.swift in Sources */,
DA8F919919F31680006F6675 /* QCKSpecRunner.m in Sources */,
DA8940F01B35B1FA00161061 /* FailureUsingXCTAssertTests+ObjC.m in Sources */,
4728253B1A5EECCE008DC74F /* SharedExamplesTests+ObjC.m in Sources */,
Expand Down
4 changes: 2 additions & 2 deletions Sources/Quick/Configuration/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ final public class Configuration: NSObject {
internal let exampleHooks = ExampleHooks()
internal let suiteHooks = SuiteHooks()
internal var exclusionFilters: [ExampleFilter] = [ { example in
if let pending = example.filterFlags[Filter.pending] {
return pending
if let excluded = example.filterFlags[Filter.excluded] {
return excluded
} else {
return false
}
Expand Down
5 changes: 3 additions & 2 deletions Sources/Quick/DSL/DSL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,9 @@ public func itBehavesLike<C>(_ behavior: Behavior<C>.Type, flags: FilterFlags =
- parameter description: An arbitrary string describing the example or example group.
- parameter closure: A closure that will not be evaluated.
*/
public func pending(_ description: String, closure: () -> Void) {
World.sharedWorld.pending(description, closure: closure)
public func pending(_ description: String, file: String = #file, line: UInt = #line,
closure: @escaping () -> Void) {
World.sharedWorld.pending(description, file: file, line: line, closure: closure)
}

/**
Expand Down
36 changes: 30 additions & 6 deletions Sources/Quick/DSL/World+DSL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ extension World {

internal func xdescribe(_ description: String, flags: FilterFlags, closure: () -> Void) {
var pendingFlags = flags
pendingFlags[Filter.pending] = true
pendingFlags[Filter.excluded] = true
self.describe(description, flags: pendingFlags, closure: closure)
}

Expand Down Expand Up @@ -111,10 +111,33 @@ extension World {
@nonobjc
internal func xit(_ description: String, flags: FilterFlags, file: String, line: UInt, closure: @escaping () -> Void) {
var pendingFlags = flags
pendingFlags[Filter.pending] = true
pendingFlags[Filter.excluded] = true
self.it(description, flags: pendingFlags, file: file, line: line, closure: closure)
}

@nonobjc
internal func pending(_ description: String, file: String, line: UInt, closure: @escaping () -> Void) {
if beforesCurrentlyExecuting {
raiseError("'pending' cannot be used inside 'beforeEach', 'pending' may only be used inside 'context' or 'describe'. ")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line Length Violation: Line should be 120 characters or less: currently 131 characters (line_length)

}
if aftersCurrentlyExecuting {
raiseError("'pending' cannot be used inside 'afterEach', 'pending' may only be used inside 'context' or 'describe'. ")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line Length Violation: Line should be 120 characters or less: currently 130 characters (line_length)

}
guard currentExampleMetadata == nil else {
raiseError("'pending' cannot be used inside 'it', 'pending' may only be used inside 'context' or 'describe'. ")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line Length Violation: Line should be 120 characters or less: currently 123 characters (line_length)

}
let callsite = Callsite(file: file, line: line)
let example = Example(
description: description,
callsite: callsite,
flags: [:],
closure: closure
)

example.isPending = true
currentExampleGroup.appendExample(example)
}

@nonobjc
internal func itBehavesLike(_ name: String, sharedExampleContext: @escaping SharedExampleContext, flags: FilterFlags, file: String, line: UInt) {
guard currentExampleMetadata == nil else {
Expand Down Expand Up @@ -168,7 +191,7 @@ extension World {

internal func xitBehavesLike<C>(_ behavior: Behavior<C>.Type, context: @escaping () -> C, flags: FilterFlags, file: String, line: UInt) {
var pendingFlags = flags
pendingFlags[Filter.pending] = true
pendingFlags[Filter.excluded] = true
self.itBehavesLike(behavior, context: context, flags: pendingFlags, file: file, line: line)
}

Expand All @@ -192,11 +215,12 @@ extension World {
internal func objc_itBehavesLike(_ name: String, sharedExampleContext: @escaping SharedExampleContext, flags: FilterFlags, file: String, line: UInt) {
itBehavesLike(name, sharedExampleContext: sharedExampleContext, flags: flags, file: file, line: line)
}
#endif

internal func pending(_ description: String, closure: () -> Void) {
print("Pending: \(description)")
@objc(pendingWithDescription:file:line:closure:)
internal func objc_pending(_ description: String, file: String, line: UInt, closure: @escaping () -> Void) {
pending(description, file: file, line: line, closure: closure)
}
#endif

private var currentPhase: String {
if beforesCurrentlyExecuting {
Expand Down
15 changes: 15 additions & 0 deletions Sources/Quick/Example.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ final public class Example: _ExampleBase {
private let closure: () -> Void
private let flags: FilterFlags

internal var isPending = false

internal init(description: String, callsite: Callsite, flags: FilterFlags, closure: @escaping () -> Void) {
self.internalDescription = description
self.closure = closure
Expand Down Expand Up @@ -63,6 +65,11 @@ final public class Example: _ExampleBase {
closures defined in the its surrounding example groups.
*/
public func run() {
if isPending {
print("Pending: \(uniqueIdentifier)")
return
}

let world = World.sharedWorld

if numberOfIncludedExamples == 0 {
Expand Down Expand Up @@ -115,6 +122,14 @@ final public class Example: _ExampleBase {
}
return aggregateFlags
}

internal var uniqueIdentifier: String {
guard let uniqueIdentifier = group?.uniqueIdentifier(forExample: self) else {
return description.c99ExtendedIdentifier
}

return uniqueIdentifier
}
}

extension Example {
Expand Down
74 changes: 73 additions & 1 deletion Sources/Quick/ExampleGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ final public class ExampleGroup: NSObject {
private let isInternalRootExampleGroup: Bool
private var childGroups = [ExampleGroup]()
private var childExamples = [Example]()
private var examplesToUniqueIdentifiers = [Example: String]()

internal init(description: String, flags: FilterFlags, isInternalRootExampleGroup: Bool = false) {
internal init(description: String,
flags: FilterFlags,
isInternalRootExampleGroup: Bool = false) {
self.internalDescription = description
self.flags = flags
self.isInternalRootExampleGroup = isInternalRootExampleGroup
Expand Down Expand Up @@ -89,6 +92,75 @@ final public class ExampleGroup: NSObject {
childExamples.append(example)
}

internal func uniqueIdentifier(forExample example: Example) -> String? {
return examplesToUniqueIdentifiers[example]
}

internal func assignUniqueIdentifiersToExamples() {
if !isInternalRootExampleGroup && parent != nil {
parent!.assignUniqueIdentifiersToExamples()
return
}

for group in childGroups {
group.clearUniqueExampleIdentifiers()
}

for group in childGroups {
group.generateUniqueIdentifiersForExamples()
}
}

private func clearUniqueExampleIdentifiers() {
examplesToUniqueIdentifiers.removeAll()
}

private func generateUniqueIdentifiersForExamples() {
for example in childExamples {
let uniqueIdentifier = generateUniqueIdentifier(
givenIdentifierSuffix: example.description
)

examplesToUniqueIdentifiers[example] = uniqueIdentifier
}
}

private func generateUniqueIdentifier(givenIdentifierSuffix suffix: String) -> String {
if isInternalRootExampleGroup {
return deduplicateIdentifier(suffix)
}

if let parent = parent {
return parent.generateUniqueIdentifier(
givenIdentifierSuffix: self.description + " " + suffix
)
}

let unformatted = self.description + " " + suffix
return deduplicateIdentifier(unformatted)
}

private func deduplicateIdentifier(_ identifier: String) -> String {
var uniqueIdentifier = identifier.c99ExtendedIdentifier
let baseUniqueIdentifier = uniqueIdentifier
var identifyingTag = 2
while allExampleUniqueIdentifiers.contains(uniqueIdentifier) {
uniqueIdentifier = baseUniqueIdentifier + "_\(identifyingTag)"
identifyingTag += 1
}

return uniqueIdentifier
}

private var allExampleUniqueIdentifiers: [String] {
var allIdentifiers = Array(examplesToUniqueIdentifiers.values)
for group in childGroups {
allIdentifiers.append(contentsOf: group.allExampleUniqueIdentifiers)
}

return allIdentifiers
}

private func walkUp(_ callback: (_ group: ExampleGroup) -> Void) {
var group = self
while let parent = group.parent {
Expand Down
12 changes: 6 additions & 6 deletions Sources/Quick/Filter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public typealias FilterFlags = [String: Bool]
*/
final public class Filter: _FilterBase {
/**
Example and example groups with [Focused: true] are included in test runs,
Example and example groups with [focused: true] are included in test runs,
excluding all other examples without this flag. Use this to only run one or
two tests that you're currently focusing on.
*/
Expand All @@ -29,10 +29,10 @@ final public class Filter: _FilterBase {
}

/**
Example and example groups with [Pending: true] are excluded from test runs.
Use this to temporarily suspend examples that you know do not pass yet.
*/
public class var pending: String {
return "pending"
Example and example groups with [excluded: true] are excluded from test runs.
Use this to temporarily suspend examples that you know do not pass yet.
*/
public class var excluded: String {
return "excluded"
}
}
Loading