From 08a053ebc27e9bc922c5d1225eb18534d763b725 Mon Sep 17 00:00:00 2001 From: Andrew Chang Date: Fri, 24 Dec 2021 02:57:55 -1000 Subject: [PATCH] Fix unavailable generic protocol mock initializer --- .../MockableTypeInitializerTemplate.swift | 33 ++++++++++++------- .../FunctionDefinitionTemplate.swift | 6 +++- .../ExternalModuleTypes.swift | 9 +++++ 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/Sources/MockingbirdGenerator/Generator/Templates/MockableTypeInitializerTemplate.swift b/Sources/MockingbirdGenerator/Generator/Templates/MockableTypeInitializerTemplate.swift index b710fa30..e57a4829 100644 --- a/Sources/MockingbirdGenerator/Generator/Templates/MockableTypeInitializerTemplate.swift +++ b/Sources/MockingbirdGenerator/Generator/Templates/MockableTypeInitializerTemplate.swift @@ -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(), + ]) } } diff --git a/Sources/MockingbirdGenerator/Generator/Templates/Primitives/FunctionDefinitionTemplate.swift b/Sources/MockingbirdGenerator/Generator/Templates/Primitives/FunctionDefinitionTemplate.swift index 615f8e81..49c70a4c 100644 --- a/Sources/MockingbirdGenerator/Generator/Templates/Primitives/FunctionDefinitionTemplate.swift +++ b/Sources/MockingbirdGenerator/Generator/Templates/Primitives/FunctionDefinitionTemplate.swift @@ -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 @@ -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() ]) diff --git a/Sources/MockingbirdTestsHost/ExternalModuleTypes.swift b/Sources/MockingbirdTestsHost/ExternalModuleTypes.swift index 1770cc82..0492a89b 100644 --- a/Sources/MockingbirdTestsHost/ExternalModuleTypes.swift +++ b/Sources/MockingbirdTestsHost/ExternalModuleTypes.swift @@ -8,6 +8,7 @@ import Foundation import MockingbirdModuleTestsHost import MockingbirdShadowedTestsHost +import CoreBluetooth protocol LocalPublicExternalProtocol: PublicExternalProtocol {} @@ -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: CBCentralManager {} +protocol InheritingMissingExternalClass: CBCentralManager {} +protocol InheritingMissingExternalClassGeneric: CBCentralManager { + associatedtype T +} + // MARK: - Inherited external initializer class SubclassingExternalClassWithInheritedIntializer: ExternalClassWithInitializer {