Skip to content
Snippets Groups Projects
Commit 64c32dad authored by Oliver Wiese's avatar Oliver Wiese
Browse files

add cuckoo

parent 35d38456
Branches
No related tags found
No related merge requests found
Showing
with 1455 additions and 1 deletion
......@@ -17,6 +17,7 @@ def pods
pod 'AppAuth'
pod 'GTMAppAuth'
pod 'Travellib', :git => "https://git.imp.fu-berlin.de/jakobsbode/travellib.git", :branch => 'master'
pod 'Cuckoo'
end
target 'enzevalos_iphone' do
......
......@@ -5,6 +5,9 @@ PODS:
- AppAuth/Core (1.3.0)
- AppAuth/ExternalUserAgent (1.3.0)
- BZipCompression (1.0.2)
- Cuckoo (1.3.2):
- Cuckoo/Swift (= 1.3.2)
- Cuckoo/Swift (1.3.2)
- FrameAccessor (1.3.2)
- GTMAppAuth (1.0.0):
- AppAuth/Core (~> 1.0)
......@@ -25,6 +28,7 @@ PODS:
DEPENDENCIES:
- AppAuth
- BZipCompression
- Cuckoo
- GTMAppAuth
- KeychainAccess
- mailcore2-ios (from `https://github.com/MailCore/mailcore2.git`, branch `master`)
......@@ -37,6 +41,7 @@ SPEC REPOS:
trunk:
- AppAuth
- BZipCompression
- Cuckoo
- FrameAccessor
- GTMAppAuth
- GTMSessionFetcher
......@@ -64,6 +69,7 @@ CHECKOUT OPTIONS:
SPEC CHECKSUMS:
AppAuth: 73574f3013a1e65b9601a3ddc8b3158cce68c09d
BZipCompression: e0f96508e60eb93b5f5d4c7de1eb3dd0ac4f9ae9
Cuckoo: 57f71c6616b6b08cd9fe27f803da056fb619ff3e
FrameAccessor: 0f7ba6ce37be9a5d0302a27c731dca70af8d438b
GTMAppAuth: 4deac854479704f348309e7b66189e604cf5e01e
GTMSessionFetcher: cea130bbfe5a7edc8d06d3f0d17288c32ffe9925
......@@ -74,6 +80,6 @@ SPEC CHECKSUMS:
Travellib: 819ccc356d19fdaf6f0b3c89db069d34aa6c3ec9
VENTokenField: 5a19b838fb97f040e3d4c93f584b4adeaf3fc1ee
PODFILE CHECKSUM: 45a5974132c11f7d67770b26136f69bc87d99bec
PODFILE CHECKSUM: a0e2b9d269dae873c01f9b28257284bc7add6675
COCOAPODS: 1.9.0
{
"autoPin": true,
"pins": [
{
"package": "Clang_C",
"reason": null,
"repositoryURL": "https://github.com/norio-nomura/Clang_C.git",
"version": "1.0.2"
},
{
"package": "Commandant",
"reason": null,
"repositoryURL": "https://github.com/Carthage/Commandant.git",
"version": "0.12.0"
},
{
"package": "FileKit",
"reason": null,
"repositoryURL": "https://github.com/TadeasKriz/FileKit.git",
"version": "4.0.2"
},
{
"package": "PathKit",
"reason": null,
"repositoryURL": "https://github.com/kylef/PathKit.git",
"version": "0.8.0"
},
{
"package": "Result",
"reason": null,
"repositoryURL": "https://github.com/antitypical/Result.git",
"version": "3.2.1"
},
{
"package": "SWXMLHash",
"reason": null,
"repositoryURL": "https://github.com/drmohundro/SWXMLHash.git",
"version": "3.0.4"
},
{
"package": "SourceKit",
"reason": null,
"repositoryURL": "https://github.com/norio-nomura/SourceKit.git",
"version": "1.0.1"
},
{
"package": "SourceKitten",
"reason": null,
"repositoryURL": "https://github.com/jpsim/SourceKitten.git",
"version": "0.17.2"
},
{
"package": "Spectre",
"reason": null,
"repositoryURL": "https://github.com/kylef/Spectre.git",
"version": "0.7.2"
},
{
"package": "Stencil",
"reason": null,
"repositoryURL": "https://github.com/kylef/Stencil.git",
"version": "0.8.0"
},
{
"package": "Yams",
"reason": null,
"repositoryURL": "https://github.com/jpsim/Yams.git",
"version": "0.3.2"
}
],
"version": 1
}
\ No newline at end of file
{
"object": {
"pins": [
{
"package": "Commandant",
"repositoryURL": "https://github.com/Carthage/Commandant.git",
"state": {
"branch": null,
"revision": "07cad52573bad19d95844035bf0b25acddf6b0f6",
"version": "0.15.0"
}
},
{
"package": "FileKit",
"repositoryURL": "https://github.com/nvzqz/FileKit.git",
"state": {
"branch": "develop",
"revision": "48b5ddb287f131a5c628badc819b880453f94449",
"version": null
}
},
{
"package": "Nimble",
"repositoryURL": "https://github.com/Quick/Nimble.git",
"state": {
"branch": null,
"revision": "e9d769113660769a4d9dd3afb855562c0b7ae7b0",
"version": "7.3.4"
}
},
{
"package": "PathKit",
"repositoryURL": "https://github.com/kylef/PathKit.git",
"state": {
"branch": null,
"revision": "e2f5be30e4c8f531c9c1e8765aa7b71c0a45d7a0",
"version": "0.9.2"
}
},
{
"package": "Quick",
"repositoryURL": "https://github.com/Quick/Quick.git",
"state": {
"branch": null,
"revision": "f2b5a06440ea87eba1a167cab37bf6496646c52e",
"version": "1.3.4"
}
},
{
"package": "Result",
"repositoryURL": "https://github.com/antitypical/Result.git",
"state": {
"branch": null,
"revision": "2ca499ba456795616fbc471561ff1d963e6ae160",
"version": "4.1.0"
}
},
{
"package": "SourceKitten",
"repositoryURL": "https://github.com/jpsim/SourceKitten.git",
"state": {
"branch": null,
"revision": "79ca340f609adee48defa966e6a3dd0e0acbeb08",
"version": "0.21.3"
}
},
{
"package": "Spectre",
"repositoryURL": "https://github.com/kylef/Spectre.git",
"state": {
"branch": null,
"revision": "f14ff47f45642aa5703900980b014c2e9394b6e5",
"version": "0.9.0"
}
},
{
"package": "Stencil",
"repositoryURL": "https://github.com/kylef/Stencil.git",
"state": {
"branch": null,
"revision": "0e9a78d6584e3812cd9c09494d5c7b483e8f533c",
"version": "0.13.1"
}
},
{
"package": "SWXMLHash",
"repositoryURL": "https://github.com/drmohundro/SWXMLHash.git",
"state": {
"branch": null,
"revision": "f43166a8e18fdd0857f29e303b1bb79a5428bca0",
"version": "4.9.0"
}
},
{
"package": "Yams",
"repositoryURL": "https://github.com/jpsim/Yams.git",
"state": {
"branch": null,
"revision": "b08dba4bcea978bf1ad37703a384097d3efce5af",
"version": "1.0.2"
}
}
]
},
"version": 1
}
// swift-tools-version:4.2
import PackageDescription
let package = Package(
name: "CuckooGenerator",
products: [
.library(name: "CuckooGeneratorFramework", targets:["CuckooGeneratorFramework", "cuckoo_generator"]),
.executable(name: "cuckoo_generator", targets: ["cuckoo_generator"])
],
dependencies: [
.package(url: "https://github.com/jpsim/SourceKitten.git", .upToNextMinor(from: "0.21.2")),
.package(url: "https://github.com/nvzqz/FileKit.git", .branch("develop")),
.package(url: "https://github.com/kylef/Stencil.git", from: "0.13.1"),
.package(url: "https://github.com/Carthage/Commandant.git", from: "0.12.0")
],
targets: [
.target(name: "CuckooGeneratorFramework", dependencies: [
"FileKit", "SourceKittenFramework", "Stencil", "Commandant"], exclude: ["Tests"]),
.target(name: "cuckoo_generator", dependencies: [
.target(name: "CuckooGeneratorFramework")], exclude: ["Tests"]),
]
)
//
// CodeBuilder.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 06.07.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public class CodeBuilder {
fileprivate static let Tab = " "
public fileprivate(set) var code = ""
fileprivate var nesting = 0
public func clear() {
code = ""
}
public func nest(_ closure: () -> ()) {
nesting += 1
closure()
nesting -= 1
}
public func nest(_ line: String) {
nest { self += line }
}
}
public func +=(left: CodeBuilder, right: String) {
(0..<left.nesting).forEach { _ in left.code += CodeBuilder.Tab }
left.code += right
left.code += "\n"
}
//
// FileHeaderHandler.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
import FileKit
public struct FileHeaderHandler {
public static func getHeader(of file: FileRepresentation, includeTimestamp: Bool) -> String {
let path: String
if let absolutePath = file.sourceFile.path {
path = getRelativePath(from: absolutePath)
} else {
path = "unknown"
}
let generationInfo = "// MARK: - Mocks generated from file: \(path)" + (includeTimestamp ? " at \(Date())\n" : "")
let header = getHeader(of: file)
return generationInfo + "\n" + header + "\n"
}
public static func getImports(of file: FileRepresentation, testableFrameworks: [String]) -> String {
var imports = Array(Set(file.declarations.only(Import.self).map { "import \($0.importee)\n" })).sorted().joined(separator: "")
if imports.isEmpty == false {
imports = "\n\(imports)"
}
return "import Cuckoo\n" + getTestableImports(testableFrameworks: testableFrameworks) + imports
}
private static func getRelativePath(from absolutePath: String) -> String {
let path = Path(absolutePath)
let base = path.commonAncestor(Path.current)
let components = path.components.suffix(from: base.components.endIndex)
let result = components.map { $0.rawValue }.joined(separator: Path.separator)
let difference = Path.current.components.endIndex - base.components.endIndex
return (0..<difference).reduce(result) { acc, _ in ".." + Path.separator + acc }
}
private static func getHeader(of file: FileRepresentation) -> String {
let possibleHeaderEnd = getPossibleHeaderEnd(current: file.sourceFile.contents.unicodeScalars.count, declarations: file.declarations)
let possibleHeader = String(file.sourceFile.contents.utf8.prefix(possibleHeaderEnd)) ?? ""
let singleLine = getPrefixToLastSingleLineComment(text: possibleHeader)
let multiLine = getPrefixToLastMultiLineComment(text: possibleHeader)
return singleLine.count > multiLine.count ? singleLine : multiLine
}
private static func getPossibleHeaderEnd(current: Int, declarations: [Token]) -> Int {
return declarations.reduce(current) { minimum, declaration in
let declarationMinimum: Int
switch declaration {
case let containerToken as ContainerToken:
declarationMinimum = containerToken.range.lowerBound
case let method as Method:
declarationMinimum = method.range.lowerBound
case let importToken as Import:
declarationMinimum = importToken.range.lowerBound
default:
declarationMinimum = minimum
}
return min(declarationMinimum, minimum)
}
}
private static func getPrefixToLastSingleLineComment(text: String) -> String {
if let range = text.range(of: "//", options: .backwards) {
let lastLine = text.lineRange(for: range)
return String(text[..<lastLine.upperBound])
} else {
return ""
}
}
private static func getPrefixToLastMultiLineComment(text: String) -> String {
if let range = text.range(of: "*/", options: .backwards) {
return String(text[..<range.upperBound]) + "\n"
} else {
return ""
}
}
private static func getTestableImports(testableFrameworks: [String]) -> String {
func replaceIllegalCharacters(_ char: UnicodeScalar) -> Character {
if CharacterSet.letters.contains(UnicodeScalar(char.value)!) || CharacterSet.decimalDigits.contains(UnicodeScalar(char.value)!) {
return Character(char)
} else {
return "_"
}
}
return testableFrameworks.map { String($0.unicodeScalars.map(replaceIllegalCharacters)) }.map { "@testable import \($0)\n" }.joined(separator: "")
}
}
//
// Generator.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 13/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
import Stencil
public struct Generator {
private let declarations: [Token]
private let code = CodeBuilder()
public init(file: FileRepresentation) {
declarations = file.declarations
}
public func generate(debug: Bool = false) throws -> String {
code.clear()
let ext = Extension()
ext.registerFilter("genericSafe") { (value: Any?) in
guard let string = value as? String else { return value }
return self.genericSafeType(from: string)
}
ext.registerFilter("matchableGenericNames") { (value: Any?) in
guard let method = value as? Method else { return value }
return self.matchableGenericTypes(from: method)
}
ext.registerFilter("matchableGenericWhereClause") { (value: Any?) in
guard let method = value as? Method else { return value }
return self.matchableGenericsWhereClause(from: method)
}
ext.registerFilter("matchableParameterSignature") { (value: Any?) in
guard let parameters = value as? [MethodParameter] else { return value }
return self.matchableParameterSignature(with: parameters)
}
ext.registerFilter("parameterMatchers") { (value: Any?) in
guard let parameters = value as? [MethodParameter] else { return value }
return self.parameterMatchers(for: parameters)
}
ext.registerFilter("openNestedClosure") { (value: Any?) in
guard let method = value as? Method else { return value }
return self.openNestedClosure(for: method)
}
ext.registerFilter("closeNestedClosure") { (value: Any?) in
guard let parameters = value as? [MethodParameter] else { return value }
return self.closeNestedClosure(for: parameters)
}
let environment = Environment(extensions: [ext])
let containers = declarations.compactMap { $0 as? ContainerToken }
.filter { $0.accessibility.isAccessible }
.map { $0.serializeWithType() }
return try environment.renderTemplate(string: Templates.mock, context: ["containers": containers, "debug": debug])
}
private func matchableGenericTypes(from method: Method) -> String {
guard !method.parameters.isEmpty || !method.genericParameters.isEmpty else { return "" }
let matchableGenericParameters = method.parameters.enumerated().map { index, parameter -> String in
let type = parameter.isOptional ? "OptionalMatchable" : "Matchable"
return "M\(index + 1): Cuckoo.\(type)"
}
let methodGenericParameters = method.genericParameters.map { $0.description }
return "<\((matchableGenericParameters + methodGenericParameters).joined(separator: ", "))>"
}
private func matchableGenericsWhereClause(from method: Method) -> String {
guard method.parameters.isEmpty == false else { return "" }
let matchableWhereConstraints = method.parameters.enumerated().map { index, parameter -> String in
let type = parameter.isOptional ? "OptionalMatchedType" : "MatchedType"
return "M\(index + 1).\(type) == \(genericSafeType(from: parameter.type.withoutAttributes.unoptionaled.sugarized))"
}
let methodWhereConstraints = method.returnSignature.whereConstraints
return " where \((matchableWhereConstraints + methodWhereConstraints).joined(separator: ", "))"
}
private func matchableParameterSignature(with parameters: [MethodParameter]) -> String {
guard parameters.isEmpty == false else { return "" }
return parameters.enumerated().map { "\($1.labelAndName): M\($0 + 1)" }.joined(separator: ", ")
}
private func parameterMatchers(for parameters: [MethodParameter]) -> String {
guard parameters.isEmpty == false else { return "let matchers: [Cuckoo.ParameterMatcher<Void>] = []" }
let tupleType = parameters.map { $0.typeWithoutAttributes }.joined(separator: ", ")
let matchers = parameters.enumerated().map { "wrap(matchable: \($1.name)) { $0\(parameters.count > 1 ? ".\($0)" : "") }" }.joined(separator: ", ")
return "let matchers: [Cuckoo.ParameterMatcher<(\(genericSafeType(from: tupleType)))>] = [\(matchers)]"
}
private func genericSafeType(from type: String) -> String {
return type.replacingOccurrences(of: "!", with: "?")
}
private func openNestedClosure(for method: Method) -> String {
var fullString = ""
for (index, parameter) in method.parameters.enumerated() {
if parameter.isClosure && !parameter.isEscaping {
let indents = String(repeating: "\t", count: index)
let tries = method.isThrowing ? "try " : ""
let sugarizedReturnType = method.returnType.sugarized
let returnSignature: String
if sugarizedReturnType.isEmpty {
returnSignature = sugarizedReturnType
} else {
returnSignature = " -> \(sugarizedReturnType)"
}
fullString += "\(indents)return \(tries)withoutActuallyEscaping(\(parameter.name), do: { (\(parameter.name): @escaping \(parameter.type))\(returnSignature) in\n"
}
}
return fullString
}
private func closeNestedClosure(for parameters: [MethodParameter]) -> String {
var fullString = ""
for (index, parameter) in parameters.enumerated() {
if parameter.isClosure && !parameter.isEscaping {
let indents = String(repeating: "\t", count: index)
fullString += "\(indents)})\n"
}
}
return fullString
}
}
//
// StderrPrint.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 18.12.16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
public private(set) var stderrUsed = false
func stderrPrint(_ item: Any) {
stderrUsed = true
fputs("error: \(item)\n", stderr)
}
//
// CuckooGeneratorFramework.h
// CuckooGeneratorFramework
//
// Created by Tadeas Kriz on 13/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
#import <Foundation/Foundation.h>
//! Project version number for CuckooGeneratorFramework.
FOUNDATION_EXPORT double CuckooGeneratorFrameworkVersionNumber;
//! Project version string for CuckooGeneratorFramework.
FOUNDATION_EXPORT const unsigned char CuckooGeneratorFrameworkVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <CuckooGeneratorFramework/PublicHeader.h>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.8.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016 Brightify. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
//
// MockTemplate.swift
// CuckooGeneratorFramework
//
// Created by Tadeas Kriz on 11/14/17.
//
import Foundation
extension Templates {
static let staticGenericParameter = "_CUCKOO$$GENERIC"
static let typeErasureClassName = "DefaultImplCaller"
static let mock = """
{% for container in containers %}
{% for attribute in container.attributes %}
{{ attribute.text }}
{% endfor %}
{{ container.accessibility }} class {{ container.mockName }}{{ container.genericParameters }}: {{ container.name }}{% if container.isImplementation %}{{ container.genericArguments }}{% endif %}, {% if container.isImplementation %}Cuckoo.ClassMock{% else %}Cuckoo.ProtocolMock{% endif %} {
{% if container.isGeneric and not container.isImplementation %}
{{ container.accessibility }} typealias MocksType = \(typeErasureClassName){{ container.genericArguments }}
{% else %}
{{ container.accessibility }} typealias MocksType = {{ container.name }}{{ container.genericArguments }}
{% endif %}
{{ container.accessibility }} typealias Stubbing = __StubbingProxy_{{ container.name }}
{{ container.accessibility }} typealias Verification = __VerificationProxy_{{ container.name }}
{{ container.accessibility }} let cuckoo_manager = Cuckoo.MockManager.preconfiguredManager ?? Cuckoo.MockManager(hasParent: {{ container.isImplementation }})
{% if container.isGeneric and not container.isImplementation %}
\(Templates.typeErasure.indented())
private var __defaultImplStub: \(typeErasureClassName){{ container.genericArguments }}?
{{ container.accessibility }} func enableDefaultImplementation<\(staticGenericParameter): {{ container.name }}>(_ stub: \(staticGenericParameter)) where {{ container.genericProtocolIdentity }} {
var mutableStub = stub
__defaultImplStub = \(typeErasureClassName)(from: &mutableStub, keeping: mutableStub)
cuckoo_manager.enableDefaultStubImplementation()
}
{{ container.accessibility }} func enableDefaultImplementation<\(staticGenericParameter): {{ container.name }}>(mutating stub: UnsafeMutablePointer<\(staticGenericParameter)>) where {{ container.genericProtocolIdentity }} {
__defaultImplStub = \(typeErasureClassName)(from: stub, keeping: nil)
cuckoo_manager.enableDefaultStubImplementation()
}
{% else %}
private var __defaultImplStub: {{ container.name }}{{ container.genericArguments }}?
{{ container.accessibility }} func enableDefaultImplementation(_ stub: {{ container.name }}{{ container.genericArguments }}) {
__defaultImplStub = stub
cuckoo_manager.enableDefaultStubImplementation()
}
{% endif %}
{% for property in container.properties %}
{% if debug %}
// {{property}}
{% endif %}
{% for attribute in property.attributes %}
{{ attribute.text }}
{% endfor %}
{{ property.accessibility }}{% if container.isImplementation %} override{% endif %} var {{ property.name }}: {{ property.type }} {
get {
return cuckoo_manager.getter("{{ property.name }}",
superclassCall:
{% if container.isImplementation %}
super.{{ property.name }}
{% else %}
Cuckoo.MockManager.crashOnProtocolSuperclassCall()
{% endif %},
defaultCall: __defaultImplStub!.{{property.name}})
}
{% ifnot property.isReadOnly %}
set {
cuckoo_manager.setter("{{ property.name }}",
value: newValue,
superclassCall:
{% if container.isImplementation %}
super.{{ property.name }} = newValue
{% else %}
Cuckoo.MockManager.crashOnProtocolSuperclassCall()
{% endif %},
defaultCall: __defaultImplStub!.{{property.name}} = newValue)
}
{% endif %}
}
{% endfor %}
{% for initializer in container.initializers %}
{% if debug %}
// {{initializer}}
{% endif %}
{{ initializer.accessibility }}{% if container.isImplementation %} override{% endif %}{% if initializer.@type == "ProtocolMethod" %} required{% endif %} init({{initializer.parameterSignature}}) {
{% if container.isImplementation %}
super.init({{initializer.call}})
{% endif %}
}
{% endfor %}
{% for method in container.methods %}
{% if debug %}
// {{method}}
{% endif %}
{% for attribute in method.attributes %}
{{ attribute.text }}
{% endfor %}
{{ method.accessibility }}{% if container.isImplementation and method.isOverriding %} override{% endif %} func {{ method.name }}{{ method.genericParameters }}({{ method.parameterSignature }}) {{ method.returnSignature }} {
{{ method.self|openNestedClosure }}
return{% if method.isThrowing %} try{% endif %} cuckoo_manager.call{% if method.isThrowing %}{{ method.throwType|capitalize }}{% endif %}("{{method.fullyQualifiedName}}",
parameters: ({{method.parameterNames}}),
escapingParameters: ({{method.escapingParameterNames}}),
superclassCall:
{% if container.isImplementation %}
super.{{method.name}}({{method.call}})
{% else %}
Cuckoo.MockManager.crashOnProtocolSuperclassCall()
{% endif %},
defaultCall: __defaultImplStub!.{{method.name}}{%if method.isOptional %}!{%endif%}({{method.call}}))
{{ method.parameters|closeNestedClosure }}
}
{% endfor %}
\(Templates.stubbingProxy.indented())
\(Templates.verificationProxy.indented())
}
\(Templates.noImplStub)
{% endfor %}
"""
}
//
// NopImplStubTemplate.swift
// CuckooGeneratorFramework
//
// Created by Tadeas Kriz on 11/14/17.
//
extension Templates {
static let noImplStub = """
{{container.accessibility}} class {{ container.name }}Stub{{ container.genericParameters }}: {{ container.name }}{% if container.isImplementation %}{{ container.genericArguments }}{% endif %} {
{% for property in container.properties %}
{% for attribute in property.attributes %}
{{ attribute.text }}
{% endfor %}
{{ property.accessibility }}{% if container.@type == "ClassDeclaration" %} override{% endif %} var {{ property.name }}: {{ property.type }} {
get {
return DefaultValueRegistry.defaultValue(for: ({{property.type|genericSafe}}).self)
}
{% ifnot property.isReadOnly %}
set { }
{% endif %}
}
{% endfor %}
{% for initializer in container.initializers %}
{{ initializer.accessibility }}{% if container.@type == "ClassDeclaration" %} override{% endif %}{% if initializer.@type == "ProtocolMethod" %} required{%endif%} init({{initializer.parameterSignature}}) {
{% if container.@type == "ClassDeclaration" %}
super.init({{initializer.call}})
{% endif %}
}
{% endfor %}
{% for method in container.methods %}
{{ method.accessibility }}{% if container.@type == "ClassDeclaration" and method.isOverriding %} override{% endif %} func {{ method.name }}{{ method.genericParameters }}({{ method.parameterSignature }}) {{ method.returnSignature }} {{ method.whereClause }} {
return DefaultValueRegistry.defaultValue(for: ({{method.returnType|genericSafe}}).self)
}
{% endfor %}
}
"""
}
//
// StubbingProxyTemplate.swift
// CuckooGeneratorFramework
//
// Created by Tadeas Kriz on 11/14/17.
//
import Foundation
extension Templates {
static let stubbingProxy = """
{{ container.accessibility }} struct __StubbingProxy_{{ container.name }}: Cuckoo.StubbingProxy {
private let cuckoo_manager: Cuckoo.MockManager
{{ container.accessibility }} init(manager: Cuckoo.MockManager) {
self.cuckoo_manager = manager
}
{% for property in container.properties %}
{% for attribute in property.attributes %}
{{ attribute.text }}
{% endfor %}
var {{property.name}}: Cuckoo.{{ property.stubType }}<{{ container.mockName }}, {% if property.isReadOnly %}{{property.type|genericSafe}}{% else %}{{property.nonOptionalType|genericSafe}}{% endif %}> {
return .init(manager: cuckoo_manager, name: "{{property.name}}")
}
{% endfor %}
{% for method in container.methods %}
func {{method.name}}{{method.self|matchableGenericNames}}({{method.parameters|matchableParameterSignature}}) -> {{method.stubFunction}}<({{method.inputTypes|genericSafe}}){%if method.returnType != "Void" %}, {{method.returnType|genericSafe}}{%endif%}>{{method.self|matchableGenericWhereClause}} {
{{method.parameters|parameterMatchers}}
return .init(stub: cuckoo_manager.createStub(for: {{ container.mockName }}.self, method: "{{method.fullyQualifiedName}}", parameterMatchers: matchers))
}
{% endfor %}
}
"""
}
//
// Templates.swift
// CuckooGeneratorFramework
//
// Created by Tadeas Kriz on 11/14/17.
//
import Foundation
struct Templates { }
extension String {
func indented(times: Int = 1) -> String {
let indentation = String(repeating: "\t", count: times)
return self.components(separatedBy: CharacterSet.newlines).map {
indentation + $0
}.joined(separator: "\n")
}
}
//
// TypeErasureTemplate.swift
// CuckooGeneratorFramework
//
// Created by Matyáš Kříž on 26/11/2018.
//
import Foundation
extension Templates {
static let typeErasure = """
{{ container.accessibility }} class \(typeErasureClassName){{ container.genericParameters }}: {{ container.name }} {
private let reference: Any
{% for property in container.properties %}private let _getter_storage$${{ property.name }}: () -> {{ property.type }}{% if not property.isReadOnly %}
private let _setter_storage$${{ property.name }}: ({{ property.type }}) -> Void{% endif %}
{{ container.accessibility }} var {{ property.name }}: {{ property.type }} {
get { return _getter_storage$${{ property.name }}() }{% if not property.isReadOnly %}
set { _setter_storage$${{ property.name }}(newValue) }{% endif %}
}
{% endfor %}
{# For developers: The `keeping reference: Any?` is necessary because when called from the `enableDefaultImplementation(stub:)` method
instead of `enableDefaultImplementation(mutating:)`, we need to prevent the struct getting deallocated. #}
init<\(staticGenericParameter): {{ container.name }}>(from defaultImpl: UnsafeMutablePointer<\(staticGenericParameter)>, keeping reference: @escaping @autoclosure () -> Any?) where {{ container.genericProtocolIdentity }} {
self.reference = reference
{% for property in container.properties %}_getter_storage$${{ property.name }} = { defaultImpl.pointee.{{ property.name }} }
{% if not property.isReadOnly %}_setter_storage$${{ property.name }} = { defaultImpl.pointee.{{ property.name }} = $0 }{% endif %}
{% endfor %}
{% for method in container.methods %}_storage${{ forloop.counter }}${{ method.name }} = defaultImpl.pointee.{{ method.name }}
{% endfor %}
}
{% if container.initializers %}
/// MARK:- ignored required initializers{% endif %}
{% for initializer in container.initializers %}{{ container.accessibility }} required init({{ initializer.parameterSignature }}) {
fatalError("`DefaultImplCaller` class is only used for calling default implementation and can't be initialized on its own.")
}
{% endfor %}
{% for method in container.methods %}
private let _storage${{ forloop.counter }}${{ method.name }}: ({{ method.inputTypes }}) -> {{ method.returnType }}
{{ container.accessibility }} func {{ method.name }}({{ method.parameterSignature }}) {{ method.returnSignature }} {
return _storage${{ forloop.counter }}${{ method.name }}({{ method.parameterNames }})
}
{% endfor %}
}
"""
}
//
// VerificationProxyTemplate.swift
// CuckooGeneratorFramework
//
// Created by Tadeas Kriz on 11/14/17.
//
import Foundation
extension Templates {
static let verificationProxy = """
{{ container.accessibility }} struct __VerificationProxy_{{ container.name }}: Cuckoo.VerificationProxy {
private let cuckoo_manager: Cuckoo.MockManager
private let callMatcher: Cuckoo.CallMatcher
private let sourceLocation: Cuckoo.SourceLocation
{{ container.accessibility }} init(manager: Cuckoo.MockManager, callMatcher: Cuckoo.CallMatcher, sourceLocation: Cuckoo.SourceLocation) {
self.cuckoo_manager = manager
self.callMatcher = callMatcher
self.sourceLocation = sourceLocation
}
{% for property in container.properties %}
{% for attribute in property.attributes %}
{{ attribute.text }}
{% endfor %}
var {{property.name}}: Cuckoo.{{property.verifyType}}<{% if property.isReadOnly %}{{property.type|genericSafe}}{% else %}{{property.nonOptionalType|genericSafe}}{% endif %}> {
return .init(manager: cuckoo_manager, name: "{{property.name}}", callMatcher: callMatcher, sourceLocation: sourceLocation)
}
{% endfor %}
{% for method in container.methods %}
@discardableResult
func {{method.name}}{{method.self|matchableGenericNames}}({{method.parameters|matchableParameterSignature}}) -> Cuckoo.__DoNotUse<({{method.inputTypes|genericSafe}}), {{method.returnType|genericSafe}}>{{method.self|matchableGenericWhereClause}} {
{{method.parameters|parameterMatchers}}
return cuckoo_manager.verify("{{method.fullyQualifiedName}}", callMatcher: callMatcher, parameterMatchers: matchers, sourceLocation: sourceLocation)
}
{% endfor %}
}
"""
}
This diff is collapsed.
//
// Accessibility.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public enum Accessibility: String {
case Open = "source.lang.swift.accessibility.open"
case Public = "source.lang.swift.accessibility.public"
case Internal = "source.lang.swift.accessibility.internal"
case Private = "source.lang.swift.accessibility.private"
case FilePrivate = "source.lang.swift.accessibility.fileprivate"
public var sourceName: String {
switch self {
case .Open:
fallthrough
case .Public:
return "public"
case .Internal:
return ""
case .Private:
return "private"
case .FilePrivate:
return "fileprivate"
}
}
public var isAccessible: Bool {
return self != .Private && self != .FilePrivate
}
}
extension Accessibility: Comparable {
/// How open is this accessibility. The higher number the more accessible.
private var openness: Int {
switch self {
case .Open:
return 4
case .Public:
return 3
case .Internal:
return 2
case .FilePrivate:
return 1
case .Private:
return 0
}
}
public static func < (lhs: Accessibility, rhs: Accessibility) -> Bool {
return lhs.openness < rhs.openness
}
}
//
// Attribute.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 2/25/17.
//
//
public struct Attribute {
public enum Kind: String {
case objc = "source.decl.attribute.objc"
case optional = "source.decl.attribute.optional"
case lazy = "source.decl.attribute.lazy"
case required = "source.decl.attribute.required"
case override = "source.decl.attribute.override"
case convenience = "source.decl.attribute.convenience"
case weak = "source.decl.attribute.weak"
case ibAction = "source.decl.attribute.ibaction"
case ibOutlet = "source.decl.attribute.iboutlet"
case available = "source.decl.attribute.available"
case final = "source.decl.attribute.final"
}
public var kind: Kind
public var text: String
public var isSupported: Bool {
switch (kind) {
case .objc, .optional, .lazy, .required, .override, .convenience, .weak, .ibAction, .ibOutlet, .final:
return false
case .available:
return true
}
}
}
extension Attribute: Token {
public func isEqual(to other: Token) -> Bool {
guard let otherAttribute = other as? Attribute else { return false }
return self.kind == otherAttribute.kind && self.text == otherAttribute.text
}
public func serialize() -> [String : Any] {
return ["text": text]
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment