Skip to content

Commit

Permalink
Merge pull request #166 from sidepelican/asyncfunc
Browse files Browse the repository at this point in the history
Add async function support
  • Loading branch information
EspressoCup authored Nov 17, 2021
2 parents f9110ce + 1562aec commit c4f4765
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 10 deletions.
5 changes: 3 additions & 2 deletions Sources/MockoloFramework/Models/MethodModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ final class MethodModel: Model {
genericTypeParams: [ParamModel],
genericWhereClause: String?,
params: [ParamModel],
throwsOrRethrows: String,
throwsOrRethrows: String?,
asyncOrReasync: String?,
isStatic: Bool,
offset: Int64,
length: Int64,
Expand All @@ -141,7 +142,7 @@ final class MethodModel: Model {
processed: Bool) {
self.name = name.trimmingCharacters(in: .whitespaces)
self.type = Type(typeName.trimmingCharacters(in: .whitespaces))
self.suffix = throwsOrRethrows
self.suffix = [asyncOrReasync, throwsOrRethrows].compactMap { $0 }.joined(separator: " ")
self.offset = offset
self.length = length
self.kind = kind
Expand Down
17 changes: 14 additions & 3 deletions Sources/MockoloFramework/Parsers/SwiftSyntaxExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,8 @@ extension SubscriptDeclSyntax {
genericTypeParams: genericTypeParams,
genericWhereClause: genericWhereClause,
params: params,
throwsOrRethrows: "",
throwsOrRethrows: nil,
asyncOrReasync: nil,
isStatic: isStatic,
offset: self.offset,
length: self.length,
Expand Down Expand Up @@ -434,7 +435,8 @@ extension FunctionDeclSyntax {
genericTypeParams: genericTypeParams,
genericWhereClause: genericWhereClause,
params: params,
throwsOrRethrows: self.signature.throwsOrRethrowsKeyword?.text ?? "",
throwsOrRethrows: self.signature.throwsOrRethrowsKeyword?.text,
asyncOrReasync: self.signature.asyncOrReasyncKeyword?.text,
isStatic: isStatic,
offset: self.offset,
length: self.length,
Expand Down Expand Up @@ -477,7 +479,8 @@ extension InitializerDeclSyntax {
genericTypeParams: genericTypeParams,
genericWhereClause: genericWhereClause,
params: params,
throwsOrRethrows: self.throwsOrRethrowsKeyword?.text ?? "",
throwsOrRethrows: self.throwsOrRethrowsKeyword?.text,
asyncOrReasync: nil, // "init() async" is not supperted in SwiftSyntax
isStatic: false,
offset: self.offset,
length: self.length,
Expand Down Expand Up @@ -797,3 +800,11 @@ extension Trivia {
return nil
}
}

#if swift(<5.5)
extension FunctionSignatureSyntax {
var asyncOrReasyncKeyword: TokenSyntax? {
return nil
}
}
#endif
5 changes: 4 additions & 1 deletion Sources/MockoloFramework/Templates/ClosureTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ extension ClosureModel {
}
let handlerReturnDefault = renderReturnDefaultStatement(name: name, type: returnDefaultType)

let prefix = suffix.isThrowsOrRethrows ? String.SwiftKeywords.try.rawValue + " " : ""
let prefix = [
suffix.hasThrowsOrRethrows ? String.SwiftKeywords.try.rawValue + " " : nil,
suffix.hasAsync ? String.SwiftKeywords.await.rawValue + " " : nil,
].compactMap { $0 }.joined()

let returnStr = returnDefaultType.typeName.isEmpty ? "" : "return "
let callExpr = "\(returnStr)\(prefix)\(name)(\(handlerParamValsStr))\(type.cast ?? "")"
Expand Down
2 changes: 1 addition & 1 deletion Sources/MockoloFramework/Templates/MethodTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ extension MethodModel {
var body = ""

if useTemplateFunc {
let callMockFunc = !suffix.isThrowsOrRethrows && (handler.type.cast?.isEmpty ?? false)
let callMockFunc = !suffix.hasThrowsOrRethrows && (handler.type.cast?.isEmpty ?? false)
if callMockFunc {
let handlerParamValsStr = params.map { (arg) -> String in
if arg.type.typeName.hasPrefix(String.autoclosure) {
Expand Down
14 changes: 12 additions & 2 deletions Sources/MockoloFramework/Utils/StringExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ extension String {
case `throws` = "throws"
case `rethrows` = "rethrows"
case `try` = "try"
case `async` = "async"
case `await` = "await"
case `for` = "for"
case `in` = "in"
case `where` = "where"
Expand Down Expand Up @@ -109,8 +111,16 @@ extension String {
"""


var isThrowsOrRethrows: Bool {
return self == SwiftKeywords.throws.rawValue || self == SwiftKeywords.rethrows.rawValue
var hasThrowsOrRethrows: Bool {
return components(separatedBy: .whitespaces).contains { component in
return component == SwiftKeywords.throws.rawValue || component == SwiftKeywords.rethrows.rawValue
}
}

var hasAsync: Bool {
return components(separatedBy: .whitespaces).contains { component in
return component == SwiftKeywords.async.rawValue
}
}

var safeName: String {
Expand Down
5 changes: 4 additions & 1 deletion Sources/MockoloFramework/Utils/TypeParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,10 @@ public final class Type {
displayableReturnType = "(\(displayableReturnType))"
}

let suffixStr = suffix.isThrowsOrRethrows ? String.SwiftKeywords.throws.rawValue + " " : ""
let suffixStr = [
suffix.hasAsync ? String.SwiftKeywords.async.rawValue + " " : nil,
suffix.hasThrowsOrRethrows ? String.SwiftKeywords.throws.rawValue + " " : nil,
].compactMap { $0 }.joined()

let typeStr = "((\(displayableParamStr)) \(suffixStr)-> \(displayableReturnType))?"
return Type(typeStr, cast: returnTypeCast)
Expand Down
155 changes: 155 additions & 0 deletions Tests/TestFuncs/TestFuncAsync/FixtureFuncAsync.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import MockoloFramework

let funcAsync = """
import Foundation
/// \(String.mockAnnotation)
protocol FuncAsync {
func f1(arg: Int) async -> String
func f2(arg: Int) async
func g1(arg: (Int) async -> ())
async -> String
func g2(arg: (Int) async -> ()) async
func update<T, U>(arg1: T, arg2: @escaping (U) async -> ()) async -> ((T) -> (U))
}
"""

let funcAsyncMock =
"""
import Foundation
class FuncAsyncMock: FuncAsync {
init() { }
private(set) var f1CallCount = 0
var f1Handler: ((Int) async -> (String))?
func f1(arg: Int) async -> String {
f1CallCount += 1
if let f1Handler = f1Handler {
return await f1Handler(arg)
}
return ""
}
private(set) var f2CallCount = 0
var f2Handler: ((Int) async -> ())?
func f2(arg: Int) async {
f2CallCount += 1
if let f2Handler = f2Handler {
await f2Handler(arg)
}
}
private(set) var g1CallCount = 0
var g1Handler: (((Int) async -> ()) async -> (String))?
func g1(arg: (Int) async -> ()) async -> String {
g1CallCount += 1
if let g1Handler = g1Handler {
return await g1Handler(arg)
}
return ""
}
private(set) var g2CallCount = 0
var g2Handler: (((Int) async -> ()) async -> ())?
func g2(arg: (Int) async -> ()) async {
g2CallCount += 1
if let g2Handler = g2Handler {
await g2Handler(arg)
}
}
private(set) var updateCallCount = 0
var updateHandler: ((Any, Any) async -> (Any))?
func update<T, U>(arg1: T, arg2: @escaping (U) async -> ()) async -> ((T) -> (U)) {
updateCallCount += 1
if let updateHandler = updateHandler {
return await updateHandler(arg1, arg2) as! ((T) -> (U))
}
fatalError("updateHandler returns can't have a default value thus its handler must be set")
}
}
"""

let funcAsyncThrows = """
import Foundation
/// \(String.mockAnnotation)
protocol FuncAsyncThrows {
func f1(arg: Int) async throws -> String
func f2(arg: Int) async throws
func g1(arg: (Int) async throws -> ())
async
throws -> String
func g2(arg: (Int) async throws -> ()) async throws
func update<T, U>(arg1: T, arg2: @escaping (U) async throws -> ()) async throws -> ((T) -> (U))
}
"""

let funcAsyncThrowsMock = """
import Foundation
class FuncAsyncThrowsMock: FuncAsyncThrows {
init() { }
private(set) var f1CallCount = 0
var f1Handler: ((Int) async throws -> (String))?
func f1(arg: Int) async throws -> String {
f1CallCount += 1
if let f1Handler = f1Handler {
return try await f1Handler(arg)
}
return ""
}
private(set) var f2CallCount = 0
var f2Handler: ((Int) async throws -> ())?
func f2(arg: Int) async throws {
f2CallCount += 1
if let f2Handler = f2Handler {
try await f2Handler(arg)
}
}
private(set) var g1CallCount = 0
var g1Handler: (((Int) async throws -> ()) async throws -> (String))?
func g1(arg: (Int) async throws -> ()) async throws -> String {
g1CallCount += 1
if let g1Handler = g1Handler {
return try await g1Handler(arg)
}
return ""
}
private(set) var g2CallCount = 0
var g2Handler: (((Int) async throws -> ()) async throws -> ())?
func g2(arg: (Int) async throws -> ()) async throws {
g2CallCount += 1
if let g2Handler = g2Handler {
try await g2Handler(arg)
}
}
private(set) var updateCallCount = 0
var updateHandler: ((Any, Any) async throws -> (Any))?
func update<T, U>(arg1: T, arg2: @escaping (U) async throws -> ()) async throws -> ((T) -> (U)) {
updateCallCount += 1
if let updateHandler = updateHandler {
return try await updateHandler(arg1, arg2) as! ((T) -> (U))
}
fatalError("updateHandler returns can't have a default value thus its handler must be set")
}
}
"""
22 changes: 22 additions & 0 deletions Tests/TestFuncs/TestFuncAsync/FuncAsyncTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Foundation
import XCTest

class FuncAsyncTests: MockoloTestCase {
override func setUpWithError() throws {
try super.setUpWithError()

#if swift(<5.5)
throw XCTSkip("async/await support needs swift5.5")
#endif
}

func testFuncAsyncs() {
verify(srcContent: funcAsync,
dstContent: funcAsyncMock)
}

func testFuncAsyncThrows() {
verify(srcContent: funcAsyncThrows,
dstContent: funcAsyncThrowsMock)
}
}

0 comments on commit c4f4765

Please sign in to comment.