Skip to content

Commit

Permalink
Merge uber/optional-mock-observable
Browse files Browse the repository at this point in the history
Add an optional input flag to use propertywrapper @MockObservable (default set to no)
  • Loading branch information
Ellie Shin committed May 14, 2020
1 parent e53edab commit 34dd141
Show file tree
Hide file tree
Showing 24 changed files with 517 additions and 148 deletions.
6 changes: 6 additions & 0 deletions Sources/Mockolo/Executor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Executor {
private var customImports: OptionArgument<[String]>!
private var annotation: OptionArgument<String>!
private var useTemplateFunc: OptionArgument<Bool>!
private var useMockObservable: OptionArgument<Bool>!
private var mockAll: OptionArgument<Bool>!
private var concurrencyLimit: OptionArgument<Int>!
private var useSourceKit: OptionArgument<Bool>!
Expand Down Expand Up @@ -114,6 +115,9 @@ class Executor {
useTemplateFunc = parser.add(option: "--use-template-func",
kind: Bool.self,
usage: "If set, a common template function will be called from all functions in mock classes (default is set to false).")
useMockObservable = parser.add(option: "--use-mock-observable",
kind: Bool.self,
usage: "If set, a property wrapper will be used to mock RxSwift Observable variables (default is set to false).")
mockAll = parser.add(option: "--mock-all",
kind: Bool.self,
usage: "If set, it will mock all types (protocols and classes) with a mock annotation (default is set to false and only mocks protocols with a mock annotation).")
Expand Down Expand Up @@ -178,6 +182,7 @@ class Executor {
let customImports = arguments.get(self.customImports)
let shouldUseSourceKit = arguments.get(useSourceKit) ?? false
let shouldUseTemplateFunc = arguments.get(useTemplateFunc) ?? false
let shouldUseMockObservable = arguments.get(useMockObservable) ?? false
let shouldMockAll = arguments.get(mockAll) ?? false

do {
Expand All @@ -191,6 +196,7 @@ class Executor {
macro: macro,
declType: shouldMockAll ? .all : .protocolType,
useTemplateFunc: shouldUseTemplateFunc,
useMockObservable: shouldUseMockObservable,
testableImports: testableImports,
customImports: customImports,
to: outputFilePath,
Expand Down
4 changes: 2 additions & 2 deletions Sources/MockoloFramework/Models/ClassModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ final class ClassModel: Model {
self.accessLevel = acl
}

func render(with identifier: String, encloser: String, useTemplateFunc: Bool) -> String? {
return applyClassTemplate(name: name, identifier: self.identifier, accessLevel: accessLevel, attribute: attribute, declType: declType, metadata: metadata, useTemplateFunc: useTemplateFunc, initParamCandidates: initParamCandidates, declaredInits: declaredInits, entities: entities)
func render(with identifier: String, encloser: String, useTemplateFunc: Bool, useMockObservable: Bool) -> String? {
return applyClassTemplate(name: name, identifier: self.identifier, accessLevel: accessLevel, attribute: attribute, declType: declType, metadata: metadata, useTemplateFunc: useTemplateFunc, useMockObservable: useMockObservable, initParamCandidates: initParamCandidates, declaredInits: declaredInits, entities: entities)
}
}
2 changes: 1 addition & 1 deletion Sources/MockoloFramework/Models/ClosureModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ final class ClosureModel: Model {
self.type = Type.toClosureType(with: paramTypes, typeParams: genericTypeNameList, suffix: suffix, returnType: returnType)
}

func render(with identifier: String, encloser: String, useTemplateFunc: Bool = false) -> String? {
func render(with identifier: String, encloser: String, useTemplateFunc: Bool = false, useMockObservable: Bool = false) -> String? {
return applyClosureTemplate(name: identifier + .handlerSuffix,
type: type,
genericTypeNames: genericTypeNames,
Expand Down
4 changes: 2 additions & 2 deletions Sources/MockoloFramework/Models/IfMacroModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ final class IfMacroModel: Model {
self.type = Type(name)
}

func render(with identifier: String, encloser: String, useTemplateFunc: Bool) -> String? {
return applyMacroTemplate(name: name, useTemplateFunc: useTemplateFunc, entities: entities)
func render(with identifier: String, encloser: String, useTemplateFunc: Bool, useMockObservable: Bool) -> String? {
return applyMacroTemplate(name: name, useTemplateFunc: useTemplateFunc, useMockObservable: useMockObservable, entities: entities)
}
}
10 changes: 5 additions & 5 deletions Sources/MockoloFramework/Models/MethodModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import SourceKittenFramework

public enum MethodKind: Equatable {
case funcKind
case initKind(required: Bool)
case initKind(required: Bool, override: Bool)
case subscriptKind
}

Expand Down Expand Up @@ -49,7 +49,7 @@ final class MethodModel: Model {
}

var isInitializer: Bool {
if case .initKind(_) = kind {
if case .initKind(_, _) = kind {
return true
}
return false
Expand Down Expand Up @@ -145,7 +145,7 @@ final class MethodModel: Model {
self.kind = .subscriptKind
} else if ast.isInitializer {
let isRequired = ast.isRequired || encloserType == .protocolType
self.kind = .initKind(required: isRequired)
self.kind = .initKind(required: isRequired, override: shouldOverride)
} else {
self.kind = .funcKind
}
Expand Down Expand Up @@ -200,11 +200,11 @@ final class MethodModel: Model {
return name(by: level - 1) + postfix
}

func render(with identifier: String, encloser: String, useTemplateFunc: Bool) -> String? {
func render(with identifier: String, encloser: String, useTemplateFunc: Bool, useMockObservable: Bool) -> String? {
if processed {
var prefix = shouldOverride ? "\(String.override) " : ""

if case .initKind(required: let isRequired) = self.kind {
if case .initKind(required: let isRequired, override: let override) = self.kind {
if isRequired {
prefix = ""
}
Expand Down
8 changes: 1 addition & 7 deletions Sources/MockoloFramework/Models/Model.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public protocol Model {
var offset: Int64 { get set }

/// Applies a corresponding template to this model to output mocks
func render(with identifier: String, encloser: String, useTemplateFunc: Bool) -> String?
func render(with identifier: String, encloser: String, useTemplateFunc: Bool, useMockObservable: Bool) -> String?

/// Used to differentiate multiple entities with the same name
/// @param level The verbosity level
Expand Down Expand Up @@ -87,12 +87,6 @@ extension Model {
}

var underlyingName: String {
if isStatic {
return "_\(name)"
}
if !(type.isRxObservable || type.defaultVal() != nil) {
return "_\(name)"
}
return name.safeName
}

Expand Down
14 changes: 5 additions & 9 deletions Sources/MockoloFramework/Models/ParamModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,6 @@ final class ParamModel: Model {
return label + "_" + name
}

func underlyingName(with defaultTypeValue: String?) -> String {
if let _ = defaultTypeValue, !type.isRxObservable {
return "_\(name)"
}
return name
}


init(label: String = "", name: String, typeName: String, isGeneric: Bool = false, inInit: Bool = false, needVarDecl: Bool, offset: Int64, length: Int64) {
self.name = name.trimmingCharacters(in: .whitespaces)
self.type = Type(typeName.trimmingCharacters(in: .whitespaces))
Expand Down Expand Up @@ -74,14 +66,18 @@ final class ParamModel: Model {
self.label = ast.name != label ? label : ""
}

var underlyingName: String {
return "_\(name)"
}

var asVarDecl: String? {
if self.inInit, self.needVarDecl {
return applyVarTemplate(name: name, type: type)
}
return nil
}

func render(with identifier: String, encloser: String, useTemplateFunc: Bool = false) -> String? {
func render(with identifier: String, encloser: String, useTemplateFunc: Bool = false, useMockObservable: Bool = false) -> String? {
return applyParamTemplate(name: name, label: label, type: type, inInit: inInit)
}
}
2 changes: 1 addition & 1 deletion Sources/MockoloFramework/Models/TypeAliasModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ final class TypeAliasModel: Model {
return fullName
}

func render(with identifier: String, encloser: String, useTemplateFunc: Bool = false) -> String? {
func render(with identifier: String, encloser: String, useTemplateFunc: Bool = false, useMockObservable: Bool = false) -> String? {
if processed || useDescription, let modelDescription = modelDescription?.trimmingCharacters(in: .whitespacesAndNewlines) {
if addAcl {
return "\(1.tab)\(accessLevel) \(modelDescription)"
Expand Down
14 changes: 11 additions & 3 deletions Sources/MockoloFramework/Models/VariableModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ final class VariableModel: Model {
return name + suffix
}

var underlyingName: String {
if isStatic || type.defaultVal() == nil {
return "_\(name)"
}
return name
}

init(name: String,
typeName: String,
acl: String?,
Expand Down Expand Up @@ -67,7 +74,7 @@ final class VariableModel: Model {
self.filePath = filepath
}

func render(with identifier: String, encloser: String, useTemplateFunc: Bool = false) -> String? {
func render(with identifier: String, encloser: String, useTemplateFunc: Bool = false, useMockObservable: Bool = false) -> String? {
if processed {
var prefix = ""
if shouldOverride, !name.isGenerated(type: type) {
Expand All @@ -91,10 +98,11 @@ final class VariableModel: Model {

if let rxVar = applyRxVariableTemplate(name: identifier,
type: type,
overrideTypes: overrideTypes,
encloser: encloser,
isStatic: isStatic,
overrideTypes: overrideTypes,
shouldOverride: shouldOverride,
useMockObservable: useMockObservable,
isStatic: isStatic,
accessLevel: accessLevel) {
return rxVar
}
Expand Down
4 changes: 3 additions & 1 deletion Sources/MockoloFramework/Operations/Generator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public func generate(sourceDirs: [String]?,
macro: String?,
declType: DeclType,
useTemplateFunc: Bool,
useMockObservable: Bool,
testableImports: [String]?,
customImports: [String]?,
to outputFilePath: String,
Expand Down Expand Up @@ -128,7 +129,8 @@ public func generate(sourceDirs: [String]?,
signpost_begin(name: "Render models")
log("Render models with templates...", level: .info)
renderTemplates(entities: resolvedEntities,
useTemplateFunc: useTemplateFunc) { (mockString: String, offset: Int64) in
useTemplateFunc: useTemplateFunc,
useMockObservable: useMockObservable) { (mockString: String, offset: Int64) in
candidates.append((mockString, offset))
}
signpost_end(name: "Render models")
Expand Down
3 changes: 1 addition & 2 deletions Sources/MockoloFramework/Operations/ImportsHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation

func handleImports(pathToImportsMap: ImportMap,
pathToContentMap: [(String, Data, Int64)], // TODO: is this needed??
pathToContentMap: [(String, Data, Int64)],
customImports: [String]?,
testableImports: [String]?) -> String {

Expand All @@ -30,7 +30,6 @@ func handleImports(pathToImportsMap: ImportMap,
for (_, filecontent, offset) in pathToContentMap {
let v = filecontent.findImportLines(at: offset)
importLines[defaultKey]?.append(contentsOf: v)
// break // TODO: why break here?
}

if let customImports = customImports {
Expand Down
3 changes: 2 additions & 1 deletion Sources/MockoloFramework/Operations/TemplateRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ import Foundation

func renderTemplates(entities: [ResolvedEntity],
useTemplateFunc: Bool,
useMockObservable: Bool,
completion: @escaping (String, Int64) -> ()) {
scan(entities) { (resolvedEntity, lock) in
let mockModel = resolvedEntity.model()
if let mockString = mockModel.render(with: resolvedEntity.key, encloser: mockModel.name, useTemplateFunc: useTemplateFunc), !mockString.isEmpty {
if let mockString = mockModel.render(with: resolvedEntity.key, encloser: mockModel.name, useTemplateFunc: useTemplateFunc, useMockObservable: useMockObservable), !mockString.isEmpty {
lock?.lock()
completion(mockString, mockModel.offset)
lock?.unlock()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,6 @@ extension IfConfigDeclSyntax {
hasInit = hasInit || initFlag
}
}
} else if let list = cl.elements.as(ImportDeclSyntax.self) {

}
}
}
Expand Down Expand Up @@ -474,7 +472,7 @@ extension InitializerDeclSyntax {

return MethodModel(name: "init",
typeName: "",
kind: .initKind(required: requiredInit),
kind: .initKind(required: requiredInit, override: declType == .classType),
encloserType: declType,
acl: acl,
genericTypeParams: genericTypeParams,
Expand Down
71 changes: 59 additions & 12 deletions Sources/MockoloFramework/Templates/ClassTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extension ClassModel {
declType: DeclType,
metadata: AnnotationMetadata?,
useTemplateFunc: Bool,
useMockObservable: Bool,
initParamCandidates: [Model],
declaredInits: [MethodModel],
entities: [(String, Model)]) -> String {
Expand All @@ -39,7 +40,10 @@ extension ClassModel {
if model.modelType == .variable, model.name == String.hasBlankInit {
return nil
}
if let ret = model.render(with: uniqueId, encloser: name, useTemplateFunc: useTemplateFunc) {
if model.modelType == .method, model.isInitializer, !model.processed {
return nil
}
if let ret = model.render(with: uniqueId, encloser: name, useTemplateFunc: useTemplateFunc, useMockObservable: useMockObservable) {
return (ret, model.offset)
}
return nil
Expand Down Expand Up @@ -68,7 +72,7 @@ extension ClassModel {
}

let extraInits = extraInitsIfNeeded(initParamCandidates: initParamCandidates, declaredInits: declaredInits, acl: acl, declType: declType, overrides: metadata?.varTypes)

var body = ""
if !typealiasTemplate.isEmpty {
body += "\(typealiasTemplate)\n"
Expand Down Expand Up @@ -165,22 +169,65 @@ extension ClassModel {
return nil
}
.joined(separator: "\n")

var blankInit = ""

let declaredInitStr = declaredInits.compactMap { (m: MethodModel) -> String? in
if case let .initKind(required, override) = m.kind, !m.processed {
let modifier = required ? "\(String.required) " : (override ? "\(String.override) " : "")
let mAcl = m.accessLevel.isEmpty ? "" : "\(m.accessLevel) "
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: ", ")

if override {
let paramsList = m.params.map { param in
return "\(param.name): \(param.name.safeName)"
}.joined(separator: ", ")

return """
\(1.tab)\(modifier)\(mAcl)init\(genericTypesStr)(\(paramDeclsStr)) {
\(2.tab)super.init(\(paramsList))
\(1.tab)}
"""
} else {
let paramsAssign = m.params.map { param in
let underVars = initParamCandidates.compactMap { return $0.name.safeName == param.name.safeName ? $0.underlyingName : nil}
if let underVar = underVars.first {
return "\(2.tab)self.\(underVar) = \(param.name.safeName)"
} else {
return "\(2.tab)self.\(param.underlyingName) = \(param.name.safeName)"
}
}.joined(separator: "\n")

return """
\(1.tab)\(modifier)\(mAcl)init\(genericTypesStr)(\(paramDeclsStr)) {
\(paramsAssign)
\(1.tab)}
"""
}
}
return nil
}.sorted().joined(separator: "\n")

var template = ""

if !extraVarsToDecl.isEmpty {
template += "\(1.tab)\(extraVarsToDecl)\n"
}

if needBlankInit {
// In case of protocol mocking, we want to provide a blank init (if not present already) for convenience,
// where instance vars do not have to be set in init since they all have get/set (see VariableTemplate).
blankInit = "\(acl)init() { }"
let blankInit = "\(acl)init() { }"
template += "\(1.tab)\(blankInit)\n"
}

var template = ""
if !extraVarsToDecl.isEmpty {
template += "\(1.tab)\(extraVarsToDecl)\n"
if !initTemplate.isEmpty {
template += "\(initTemplate)\n"
}

if !declaredInitStr.isEmpty {
template += "\(declaredInitStr)\n"
}
template += """
\(1.tab)\(blankInit)
\(initTemplate)
"""

return template
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/MockoloFramework/Templates/IfMacroTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ import Foundation
extension IfMacroModel {
func applyMacroTemplate(name: String,
useTemplateFunc: Bool,
useMockObservable: Bool,
entities: [Model]) -> String {
let rendered = entities
.compactMap {$0.render(with: $0.name, encloser: "", useTemplateFunc: useTemplateFunc) }
.compactMap {$0.render(with: $0.name, encloser: "", useTemplateFunc: useTemplateFunc, useMockObservable: useMockObservable) }
.joined(separator: "\n")

let template = """
Expand Down
Loading

0 comments on commit 34dd141

Please sign in to comment.