Skip to content

Commit

Permalink
Fix unavailable generic protocol mock initializer
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewchang-bird committed Jan 6, 2022
1 parent 158c78e commit 08a053e
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,37 +80,46 @@ struct MockableTypeInitializerTemplate: Template {
supportingTypeDeclaration = ""
}

let returnType: String
let returnStatement: String
let returnTypeDescription: String

let mockTypeScopedName =
mockableTypeTemplate.createScopedName(with: containingTypeNames,
genericTypeContext: genericTypeContext,
suffix: "Mock")

let leadingTrivia: String
let attributes: [String]
let returnType: String
let returnStatement: String

if !mockableTypeTemplate.isAvailable {
// Unavailable mocks do not generate real initializers.
leadingTrivia = ""
attributes = [mockableTypeTemplate.unavailableMockAttribute]
returnType = mockTypeScopedName
returnStatement = "fatalError()"
returnTypeDescription = mockableTypeTemplate.unavailableMockAttribute
} else if !mockableTypeTemplate.shouldGenerateDefaultInitializer {
// Requires an initializer proxy to create the partial class mock.
leadingTrivia = "/// Returns an abstract mock which should be initialized using `mock(\(mockableTypeTemplate.mockableType.name).self).initialize(…)`."
attributes = []
returnType = "\(mockTypeScopedName).InitializerProxy.Type"
returnStatement = "return \(mockTypeScopedName).InitializerProxy.self"
returnTypeDescription = "/// Returns an abstract mock which should be initialized using `mock(\(mockableTypeTemplate.mockableType.name).self).initialize(…)`."
} else {
// Does not require an initializer proxy.
leadingTrivia = "/// Returns a concrete mock of `\(mockableTypeTemplate.mockableType.name)`."
attributes = []
returnType = mockTypeScopedName
returnStatement = "return \(mockTypeScopedName)(sourceLocation: Mockingbird.SourceLocation(file, line))"
returnTypeDescription = "/// Returns a concrete mock of `\(mockableTypeTemplate.mockableType.name)`."
}

return """
\(supportingTypeDeclaration)\(returnTypeDescription)
public func mock\(genericTypeConstraints)(_ type: \(metatype), file: StaticString = #file, line: UInt = #line) -> \(returnType) {
\(returnStatement)
}
let functionDeclaration = """
public func mock\(genericTypeConstraints)(_ type: \(metatype), file: StaticString = #file, line: UInt = #line) -> \(returnType)
"""

return String(lines: [
supportingTypeDeclaration,
FunctionDefinitionTemplate(leadingTrivia: leadingTrivia,
attributes: attributes,
declaration: functionDeclaration,
body: returnStatement).render(),
])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
import Foundation

struct FunctionDefinitionTemplate: Template {
let leadingTrivia: String
let attributes: [String]
let declaration: String
let genericConstraints: [String]
let body: String

init(attributes: [String] = [],
init(leadingTrivia: String = "",
attributes: [String] = [],
declaration: String,
genericConstraints: [String] = [],
body: String) {
self.leadingTrivia = leadingTrivia
self.attributes = attributes
self.declaration = declaration
self.genericConstraints = genericConstraints
Expand All @@ -27,6 +30,7 @@ struct FunctionDefinitionTemplate: Template {
let genericConstraintsString = genericConstraints.isEmpty ? "" :
" where \(separated: genericConstraints)"
return String(lines: [
leadingTrivia,
attributes.filter({ !$0.isEmpty }).joined(separator: " "),
declaration + genericConstraintsString + " " + BlockTemplate(body: body).render()
])
Expand Down
9 changes: 9 additions & 0 deletions Sources/MockingbirdTestsHost/ExternalModuleTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Foundation
import MockingbirdModuleTestsHost
import MockingbirdShadowedTestsHost
import CoreBluetooth

protocol LocalPublicExternalProtocol: PublicExternalProtocol {}

Expand All @@ -17,6 +18,14 @@ class SubclassingExternalClass: ExternalClass {
func internalMethod() {}
}

/// Cannot be mocked because of external inheritence without corresponding supporting source files.
class SubclassingMissingExternalClass: CBCentralManager {}
class SubclassingMissingExternalClassGeneric<T>: CBCentralManager {}
protocol InheritingMissingExternalClass: CBCentralManager {}
protocol InheritingMissingExternalClassGeneric: CBCentralManager {
associatedtype T
}

// MARK: - Inherited external initializer

class SubclassingExternalClassWithInheritedIntializer: ExternalClassWithInitializer {
Expand Down

0 comments on commit 08a053e

Please sign in to comment.