diff --git a/Package.resolved b/Package.resolved index 20d72133..5042d9d6 100644 --- a/Package.resolved +++ b/Package.resolved @@ -12,7 +12,7 @@ { "identity" : "swift-syntax", "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax.git", + "location" : "https://github.com/swiftlang/swift-syntax.git", "state" : { "revision" : "0687f71944021d616d34d922343dcef086855920", "version" : "600.0.1" diff --git a/Package.swift b/Package.swift index 10eda523..3e529900 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ let package = Package( .library(name: "MockoloFramework", targets: ["MockoloFramework"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-syntax.git", from: "600.0.1"), + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "600.0.1"), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"), ], targets: [ diff --git a/Sources/MockoloFramework/Models/ClosureModel.swift b/Sources/MockoloFramework/Models/ClosureModel.swift index 4da33906..b6295812 100644 --- a/Sources/MockoloFramework/Models/ClosureModel.swift +++ b/Sources/MockoloFramework/Models/ClosureModel.swift @@ -32,6 +32,8 @@ final class ClosureModel: Model { } init(name: String, genericTypeParams: [ParamModel], paramNames: [String], paramTypes: [SwiftType], isAsync: Bool, throwing: ThrowingKind, returnType: SwiftType, encloser: String) { + // In the mock's call handler, rethrows is unavailable. + let throwing = throwing.coerceRethrowsToThrows self.name = name + .handlerSuffix self.isAsync = isAsync self.throwing = throwing diff --git a/Sources/MockoloFramework/Models/ThrowingKind.swift b/Sources/MockoloFramework/Models/ThrowingKind.swift index 5277e5c8..e7766999 100644 --- a/Sources/MockoloFramework/Models/ThrowingKind.swift +++ b/Sources/MockoloFramework/Models/ThrowingKind.swift @@ -33,3 +33,13 @@ enum ThrowingKind: Equatable { } } } + +extension ThrowingKind { + /// Replace rethrows with throws. + var coerceRethrowsToThrows: ThrowingKind { + if case .rethrows = self { + return .any + } + return self + } +} diff --git a/Sources/MockoloFramework/Templates/MethodTemplate.swift b/Sources/MockoloFramework/Templates/MethodTemplate.swift index b1abada3..a5a5cc0c 100644 --- a/Sources/MockoloFramework/Templates/MethodTemplate.swift +++ b/Sources/MockoloFramework/Templates/MethodTemplate.swift @@ -58,10 +58,10 @@ extension MethodModel { let handlerVarType = handler.type.typeName // ?? "Any" let handlerReturn = handler.render(with: identifier, encloser: "") ?? "" - let suffixStr = [ - isAsync ? String.async : nil, - throwing.applyThrowingTemplate(), - ].compactMap { $0 }.joined(separator: " ") + " " + let suffixStr = applyFunctionSuffixTemplate( + isAsync: isAsync, + throwing: throwing + ) let returnStr = returnTypeName.isEmpty ? "" : "-> \(returnTypeName)" let staticStr = isStatic ? String.static + " " : "" let keyword = isSubscript ? "" : "func " diff --git a/Sources/MockoloFramework/Templates/NominalTemplate.swift b/Sources/MockoloFramework/Templates/NominalTemplate.swift index e6f5cefb..63d84e9a 100644 --- a/Sources/MockoloFramework/Templates/NominalTemplate.swift +++ b/Sources/MockoloFramework/Templates/NominalTemplate.swift @@ -198,10 +198,10 @@ extension NominalModel { let genericTypeDeclsStr = m.genericTypeParams.compactMap {$0.render(with: "", encloser: "")}.joined(separator: ", ") let genericTypesStr = genericTypeDeclsStr.isEmpty ? "" : "<\(genericTypeDeclsStr)>" let paramDeclsStr = m.params.compactMap{$0.render(with: "", encloser: "")}.joined(separator: ", ") - let suffixStr = [ - m.isAsync ? String.async : nil, - m.throwing.applyThrowingTemplate(), - ].compactMap { $0 }.joined(separator: " ") + " " + let suffixStr = applyFunctionSuffixTemplate( + isAsync: m.isAsync, + throwing: m.throwing + ) if override { let paramsList = m.params.map { param in diff --git a/Sources/MockoloFramework/Utils/FunctionSuffixTemplate.swift b/Sources/MockoloFramework/Utils/FunctionSuffixTemplate.swift new file mode 100644 index 00000000..9629e3e1 --- /dev/null +++ b/Sources/MockoloFramework/Utils/FunctionSuffixTemplate.swift @@ -0,0 +1,25 @@ +// +// Copyright (c) 2018. Uber Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/// Apply template for function suffix +/// +/// Suffix consists of async and throws. +func applyFunctionSuffixTemplate(isAsync: Bool, throwing: ThrowingKind) -> String { + [ + isAsync ? String.async : nil, + throwing.applyThrowingTemplate(), + ].compactMap { $0 }.joined(separator: " ") + " " +} diff --git a/Sources/MockoloFramework/Utils/TypeParser.swift b/Sources/MockoloFramework/Utils/TypeParser.swift index 7830dde6..60d102b1 100644 --- a/Sources/MockoloFramework/Utils/TypeParser.swift +++ b/Sources/MockoloFramework/Utils/TypeParser.swift @@ -564,10 +564,10 @@ public final class SwiftType { displayableReturnType = "(\(displayableReturnType))" } - let suffixStr = [ - isAsync ? String.async + " " : nil, - throwing.hasError ? String.throws + " " : nil, - ].compactMap { $0 }.joined() + let suffixStr = applyFunctionSuffixTemplate( + isAsync: isAsync, + throwing: throwing + ) let typeStr = "((\(displayableParamStr)) \(suffixStr)-> \(displayableReturnType))?" return SwiftType(typeStr, cast: returnTypeCast) diff --git a/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift b/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift index 963d316c..c6cf2e46 100644 --- a/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift +++ b/Tests/TestFuncs/TestFuncThrow/FixtureFuncThrow.swift @@ -1,16 +1,6 @@ import MockoloFramework -let funcThrow2 = """ -import Foundation - -/// \(String.mockAnnotation) -protocol FuncThrow { -func update(arg1: T, arg2: () throws -> T) rethrows -> T -func update(arg1: T, arg2: @escaping (U) throws -> ()) throws -> ((T) -> (U)) -} -""" - let funcThrow = """ import Foundation @@ -18,10 +8,15 @@ import Foundation protocol FuncThrow { func f1(arg: Int) throws -> String func f2(arg: Int) throws + func f3(arg: Int) throws(SomeError) + func f4(arg: Int) throws(SomeError) -> String + func f5() throws (MyError) + func f6() async throws (any Error) func g1(arg: (Int) throws -> ()) throws -> String func g2(arg: (Int) throws -> ()) throws func h(arg: (Int) throws -> ()) rethrows -> String + func h2(arg: (Int) throws(SomeError) -> ()) rethrows -> String func update(arg1: T, arg2: @escaping (U) throws -> ()) throws -> ((T) -> (U)) func update(arg1: T, arg2: () throws -> T) rethrows -> T } @@ -29,7 +24,6 @@ protocol FuncThrow { let funcThrowMock = """ - import Foundation @@ -57,6 +51,46 @@ class FuncThrowMock: FuncThrow { } + private(set) var f3CallCount = 0 + var f3Handler: ((Int) throws(SomeError) -> ())? + func f3(arg: Int) throws(SomeError) { + f3CallCount += 1 + if let f3Handler = f3Handler { + try f3Handler(arg) + } + + } + + private(set) var f4CallCount = 0 + var f4Handler: ((Int) throws(SomeError) -> (String))? + func f4(arg: Int) throws(SomeError) -> String { + f4CallCount += 1 + if let f4Handler = f4Handler { + return try f4Handler(arg) + } + return "" + } + + private(set) var f5CallCount = 0 + var f5Handler: (() throws(MyError) -> ())? + func f5() throws(MyError) { + f5CallCount += 1 + if let f5Handler = f5Handler { + try f5Handler() + } + + } + + private(set) var f6CallCount = 0 + var f6Handler: (() async throws(any Error) -> ())? + func f6() async throws(any Error) { + f6CallCount += 1 + if let f6Handler = f6Handler { + try await f6Handler() + } + + } + private(set) var g1CallCount = 0 var g1Handler: (((Int) throws -> ()) throws -> (String))? func g1(arg: (Int) throws -> ()) throws -> String { @@ -87,6 +121,16 @@ class FuncThrowMock: FuncThrow { return "" } + private(set) var h2CallCount = 0 + var h2Handler: (((Int) throws(SomeError) -> ()) throws -> (String))? + func h2(arg: (Int) throws(SomeError) -> ()) rethrows -> String { + h2CallCount += 1 + if let h2Handler = h2Handler { + return try h2Handler(arg) + } + return "" + } + private(set) var updateCallCount = 0 var updateHandler: ((Any, Any) throws -> (Any))? func update(arg1: T, arg2: @escaping (U) throws -> ()) throws -> ((T) -> (U)) { diff --git a/Tests/TestInit/FixtureInit.swift b/Tests/TestInit/FixtureInit.swift index 161d1fe3..eda4ff79 100644 --- a/Tests/TestInit/FixtureInit.swift +++ b/Tests/TestInit/FixtureInit.swift @@ -326,3 +326,43 @@ class MyProtocolMock: MyProtocol { } """ + +let throwableInit = """ +/// \(String.mockAnnotation) +protocol MyProtocol { + init(param: String) throws +} +""" + +let throwableInitMock = """ + + + +class MyProtocolMock: MyProtocol { + private var _param: String! + init() { } + required init(param: String = "") throws { + self._param = param + } +} +""" + +let typedThrowableInit = """ +/// \(String.mockAnnotation) +protocol MyProtocol { + init(param: String) throws(SomeError) +} +""" + +let typedThrowableInitMock = """ + + + +class MyProtocolMock: MyProtocol { + private var _param: String! + init() { } + required init(param: String = "") throws(SomeError) { + self._param = param + } +} +""" diff --git a/Tests/TestInit/InitTests.swift b/Tests/TestInit/InitTests.swift index a3c2af90..2a8da0b0 100644 --- a/Tests/TestInit/InitTests.swift +++ b/Tests/TestInit/InitTests.swift @@ -41,4 +41,18 @@ class InitTests: MockoloTestCase { dstContent: initWithSameParamNameButDifferentTypeMock ) } + + func testThrowableInit() { + verify( + srcContent: throwableInit, + dstContent: throwableInitMock + ) + } + + func testTypedThrowableInit() { + verify( + srcContent: typedThrowableInit, + dstContent: typedThrowableInitMock + ) + } }