Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@
<key>Type</key>
<string>PSChildPaneSpecifier</string>
</dict>
<dict>
<key>File</key>
<string>Acknowledgements/Sourcery</string>
<key>Title</key>
<string>Sourcery</string>
<key>Type</key>
<string>PSChildPaneSpecifier</string>
</dict>
<dict>
<key>File</key>
<string>Acknowledgements/swift-custom-dump</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?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>PreferenceSpecifiers</key>
<array>
<dict>
<key>FooterText</key>
<string>MIT License

Copyright (c) 2016-2021 Krzysztof Zabล‚ocki

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
</array>
</dict>
</plist>
2 changes: 2 additions & 0 deletions AuthenticatorShared/Sourcery/Generated/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignore SwiftGen generated files
*.swift
18 changes: 18 additions & 0 deletions AuthenticatorShared/Sourcery/sourcery.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
sources:
- ..

templates:
- ../../Sourcery/Templates/AutoMockable.stencil

output:
Generated

exclude:
- Generated
- Tests
- TestHelpers
- Fixtures

args:
autoMockableImports: ["BitwardenKit", "BitwardenSdk", "Combine"]
autoMockableTestableImports: ["AuthenticatorShared"]
2 changes: 2 additions & 0 deletions BitwardenShared/Sourcery/Generated/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignore SwiftGen generated files
*.swift
18 changes: 18 additions & 0 deletions BitwardenShared/Sourcery/sourcery.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
sources:
- ..

templates:
- ../../Sourcery/Templates/AutoMockable.stencil

output:
Generated

exclude:
- Generated
- Tests
- TestHelpers
- Fixtures

args:
autoMockableImports: ["BitwardenKit", "BitwardenSdk", "Combine"]
autoMockableTestableImports: ["BitwardenShared"]
1 change: 1 addition & 0 deletions Mintfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ nicklockwood/[email protected]
SwiftGen/[email protected]
realm/[email protected]
yonaskolb/[email protected]
krzysztofzablocki/[email protected]
136 changes: 136 additions & 0 deletions Sourcery/Templates/AutoMockable.stencil
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// swiftlint:disable line_length
// swiftlint:disable variable_name

import Foundation

{% for import in argument.autoMockableImports %}
import {{ import }}
{% endfor %}

{% for import in argument.autoMockableTestableImports %}
@testable import {{ import }}
{% endfor %}

{% macro swiftifyMethodName name %}{{ name | replace:"(","_" | replace:")","" | replace:":","_" | replace:"`","" | snakeToCamelCase | lowerFirstWord }}{% endmacro %}

{% macro methodThrowableErrorDeclaration method %}
var {% call swiftifyMethodName method.selectorName %}Error: Error?
{% endmacro %}

{% macro methodThrowableErrorUsage method %}
if let error = {% call swiftifyMethodName method.selectorName %}Error {
throw error
}
{% endmacro %}

{% macro methodReceivedParameters method %}
{%if method.parameters.count == 1 %}
{% set receivedVarName %}{% call swiftifyMethodName method.selectorName %}Received{% for param in method.parameters %}{{ param.name|upperFirstLetter }}{% endfor %}{% endset %}
{{ receivedVarName }}{% for param in method.parameters %} = {{ param.name }}{% endfor %}
{% call swiftifyMethodName method.selectorName %}ReceivedInvocations.append({{ receivedVarName }}!)
{% else %}
{% if not method.parameters.count == 0 %}
{% set receivedVarName %}{% call swiftifyMethodName method.selectorName %}ReceivedArguments{% endset %}
{{ receivedVarName }} = ({% for param in method.parameters %}{{ param.name }}: {{ param.name }}{% if not forloop.last%}, {% endif %}{% endfor %}){% if method.isGeneric %} as? ({% for param in method.parameters %}{{ param.name }}: {% call parameterType param %}{% if not forloop.last%}, {% endif %}{% endfor %}){% endif %}
{% call swiftifyMethodName method.selectorName %}ReceivedInvocations.append({{ receivedVarName }}!)
{% endif %}
{% endif %}
{% endmacro %}

{% macro methodReturnType method %}{% if method.returnTypeName.isVoid %}Void{% elif method.annotations["GenericReturn"] %}{{ method.annotations["GenericReturn"] }}{% else %}{{ method.returnTypeName }}{% endif %}{% endmacro %}

{% macro parameterType param %}{% if param.annotations["Generic"] %}{{ param.annotations["Generic"] }}{% else %}{{ param.unwrappedTypeName if param.typeAttributes.escaping else param.typeName }}{% endif %}{% endmacro %}
{% macro parameterArguments param %}{% if param.typeAttributes.escaping %}@escaping {% endif %}{% endmacro %}

{% macro callClosure method %}return {{ 'try ' if method.throws }}{% if method.isGeneric %}({% endif %}{% call methodClosureName method %}.map({ {{ 'try ' if method.throws }}$0({% call methodClosureCallParameters method %}) }) ?? {% call swiftifyMethodName method.selectorName %}ReturnValue{% if method.isGeneric %}) as! {{ method.returnTypeName }}{% endif %}{% endmacro %}
{% macro methodClosureName method %}{% call swiftifyMethodName method.selectorName %}Closure{% endmacro %}

{% macro methodClosureDeclaration method %}
var {% call methodClosureName method %}: (({% for param in method.parameters %}{% call parameterArguments param %}{% call parameterType param %}{% if not forloop.last %}, {% endif %}{% endfor %}) {% if method.throws %}throws {% endif %}-> {% if method.isInitializer %}Void{% else %}{% call methodReturnType method %}{% endif %})?
{% endmacro %}

{% macro methodClosureCallParameters method %}{% for param in method.parameters %}{{ param.name }}{% if not forloop.last %}, {% endif %}{% endfor %}{% endmacro %}
{% macro methodClosureCallParametersFromArguments method %}{% for param in method.parameters %}{% call receivedArgumentsVarName method %}!{% if method.parameters.count > 1 %}.{{ param.name }}{% if not forloop.last %}, {% endif %}{% endif %}{% endfor %}{% endmacro %}

{% macro receivedArgumentsVarName method %}{% if method.parameters.count == 1 %}{% call swiftifyMethodName method.selectorName %}Received{% for param in method.parameters %}{{ param.name|upperFirstLetter }}{% endfor %}{% else %}{% call swiftifyMethodName method.selectorName %}ReceivedArguments{% endif %}{% endmacro %}

{% macro mockMethod method %}
//MARK: - {{ method.shortName }}

{% if method.throws %}
{% call methodThrowableErrorDeclaration method %}
{% endif %}
{% if not method.isInitializer %}
var {% call swiftifyMethodName method.selectorName %}CalledCount = 0
var {% call swiftifyMethodName method.selectorName %}Called: Bool {
return {% call swiftifyMethodName method.selectorName %}CalledCount > 0
}
{% endif %}
{% if method.parameters.count == 1 %}
var {% call swiftifyMethodName method.selectorName %}Received{% for param in method.parameters %}{{ param.name|upperFirstLetter }}: {{ '(' if param.isClosure }}{{ param.typeName.unwrappedTypeName }}{{ ')' if param.isClosure }}?{% endfor %}
var {% call swiftifyMethodName method.selectorName %}ReceivedInvocations{% for param in method.parameters %}: [{{ '(' if param.isClosure }}{{ param.typeName.unwrappedTypeName }}{{ ')' if param.isClosure }}{%if param.typeName.isOptional%}?{%endif%}]{% endfor %} = []
{% elif not method.parameters.count == 0 %}
var {% call swiftifyMethodName method.selectorName %}ReceivedArguments: ({% for param in method.parameters %}{{ param.name }}: {% call parameterType param %}{{ ', ' if not forloop.last }}{% endfor %})?
var {% call swiftifyMethodName method.selectorName %}ReceivedInvocations: [({% for param in method.parameters %}{{ param.name }}: {% call parameterType param %}{{ ', ' if not forloop.last }}{% endfor %})] = []
{% endif %}
{% if not method.returnTypeName.isVoid and not method.isInitializer %}
var {% call swiftifyMethodName method.selectorName %}ReturnValue: {% call methodReturnType method %}{{ '!' if not method.isOptionalReturnType }}
{% endif %}
{% call methodClosureDeclaration method %}

{% if method.isInitializer %}
required {{ method.name }} {
{% call methodReceivedParameters method %}
{% call methodClosureName method %}?({% call methodClosureCallParametersFromArguments method %})
}
{% else %}
func {{ method.name }}{{ ' throws' if method.throws }}{% if not method.returnTypeName.isVoid %} -> {{ method.returnTypeName }}{% endif %} {
{% if method.throws %}
{% call methodThrowableErrorUsage method %}
{% endif %}
{% call swiftifyMethodName method.selectorName %}CalledCount += 1
{% call methodReceivedParameters method %}
{% if method.returnTypeName.isVoid %}
{% if method.throws %}try {% endif %}{% call methodClosureName method %}?({% call methodClosureCallParametersFromArguments method %})
{% else %}
{% call callClosure method %}
{% endif %}
}

{% endif %}
{% endmacro %}

{% macro mockOptionalVariable variable %}
var {% call mockedVariableName variable %}: {{ variable.typeName }}
{% endmacro %}

{% macro mockNonOptionalArrayOrDictionaryVariable variable %}
var {% call mockedVariableName variable %}: {{ variable.typeName }} = {% if variable.isArray %}[]{% elif variable.isDictionary %}[:]{% endif %}
{% endmacro %}

{% macro mockNonOptionalVariable variable %}
var {% call mockedVariableName variable %}: {{ variable.typeName }} {
get {
return {% call underlyingMockedVariableName variable %}
}
set(value) {
{% call underlyingMockedVariableName variable %} = value
}
}
var {% call underlyingMockedVariableName variable %}: {{ variable.typeName }}!
{% endmacro %}

{% macro underlyingMockedVariableName variable %}underlying{{ variable.name|upperFirstLetter }}{% endmacro %}
{% macro mockedVariableName variable %}{{ variable.name }}{% endmacro %}

{% for type in types.protocols where type.based.AutoMockable or type|annotated:"AutoMockable" %}{% if type.name != "AutoMockable" %}
class Mock{{ type.name }}: {{ type.name }} {
{% for variable in type.allVariables|!definedInExtension %}
{% if variable.isOptional %}{% call mockOptionalVariable variable %}{% elif variable.isArray or variable.isDictionary %}{% call mockNonOptionalArrayOrDictionaryVariable variable %}{% else %}{% call mockNonOptionalVariable variable %}{% endif %}
{% endfor %}

{% for method in type.allMethods|!definedInExtension %}
{% call mockMethod method %}
{% endfor %}
}
{% endif %}{% endfor %}
17 changes: 17 additions & 0 deletions project-bwa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ targets:
- "**/TestHelpers/*"
- "**/Fixtures/*"
- "**/__Snapshots__/*"
- "**/Sourcery/Generated/*"
- "**/sourcery.yml"
- path: AuthenticatorShared
includes:
- "**/__Snapshots__/*"
Expand All @@ -184,6 +186,8 @@ targets:
optional: true
- path: AuthenticatorShared/Core/Vault/Services/Importers/Support/Generated/GoogleAuth.pb.swift
optional: true
- path: AuthenticatorShared/Sourcery/sourcery.yml
buildPhase: none
dependencies:
- target: BitwardenKit/AuthenticatorBridgeKit
- package: BitwardenSdk
Expand All @@ -210,6 +214,15 @@ targets:
$SRCROOT/AuthenticatorShared/Core/Vault/Services/Importers/Support/GoogleAuth.proto
outputFiles:
- $(SRCROOT)/AuthenticatorShared/Core/Vault/Services/Importers/Support/Generated/GoogleAuth.pb.swift
- name: Sourcery
script: |
if [[ ! "$PATH" =~ "/opt/homebrew/bin" ]]; then
PATH="/opt/homebrew/bin:$PATH"
fi
mint run sourcery --config AuthenticatorShared/Sourcery/sourcery.yml
basedOnDependencyAnalysis: false
outputFiles:
- $(SRCROOT)/AuthenticatorShared/Sourcery/Generated/AutoMockable.generated.swift
AuthenticatorSharedTests:
type: bundle.unit-test
platform: iOS
Expand All @@ -225,6 +238,10 @@ targets:
- "**/TestHelpers/*"
- "**/Fixtures/*"
- path: GlobalTestHelpers-bwa
- path: AuthenticatorShared/Sourcery/Generated
optional: true
- path: AuthenticatorShared/Sourcery/Generated/AutoMockable.generated.swift
optional: true
dependencies:
- target: Authenticator
- target: AuthenticatorShared
Expand Down
17 changes: 17 additions & 0 deletions project-pm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ targets:
- "**/TestHelpers/*"
- "**/Fixtures/*"
- "**/__Snapshots__/*"
- "**/Sourcery/Generated/*"
- "**/sourcery.yml"
- path: BitwardenShared
includes:
- "**/__Snapshots__/*"
Expand All @@ -373,6 +375,8 @@ targets:
- path: BitwardenShared/UI/Platform/Application/Support/Generated/Localizations.swift
optional: true
- path: BitwardenWatchShared
- path: BitwardenShared/Sourcery/sourcery.yml
buildPhase: none
dependencies:
- package: BitwardenSdk
- package: SwiftUIIntrospect
Expand All @@ -391,6 +395,15 @@ targets:
- $(SRCROOT)/BitwardenShared/UI/Platform/Application/Support/Generated/Assets.swift
- $(SRCROOT)/BitwardenShared/UI/Platform/Application/Support/Generated/Fonts.swift
- $(SRCROOT)/BitwardenShared/UI/Platform/Application/Support/Generated/Localizations.swift
- name: Sourcery
script: |
if [[ ! "$PATH" =~ "/opt/homebrew/bin" ]]; then
PATH="/opt/homebrew/bin:$PATH"
fi
mint run sourcery --config BitwardenShared/Sourcery/sourcery.yml
basedOnDependencyAnalysis: false
outputFiles:
- $(SRCROOT)/BitwardenShared/Sourcery/Generated/AutoMockable.generated.swift
BitwardenSharedTests:
type: bundle.unit-test
platform: iOS
Expand All @@ -409,6 +422,10 @@ targets:
- "**/TestHelpers/*"
- "**/Fixtures/*"
- path: GlobalTestHelpers
- path: BitwardenShared/Sourcery/Generated
optional: true
- path: BitwardenShared/Sourcery/Generated/AutoMockable.generated.swift
optional: true
dependencies:
- target: Bitwarden
- target: BitwardenShared
Expand Down