Compare commits

..

No commits in common. "master" and "fix/final-class" have entirely different histories.

246 changed files with 6090 additions and 14367 deletions

View File

@ -33,11 +33,13 @@ platform :mac do
def upload_release release_type
# Building Cuckoo Generator
sh('../build_generator')
Dir.chdir('../Generator') do
sh('../build_generator')
end
# Settings
cuckoo_gen_path = '../Generator/.build/release/cuckoo_generator'
binary_name = 'cuckoo_generator'
cuckoo_gen_path = "../Generator/bin/#{binary_name}"
# GitHub username
username_var_name = 'GITHUB_USERNAME'
@ -49,10 +51,10 @@ platform :mac do
# Error Raisins
unless access_token
access_token = UI.input "Please type in your GitHub access token or cancel the release and define enviroment variable \"#{token_var_name}\" with the personal access token to use."
access_token = UI.input "Please type in your GitHub access token or cancel the release and define enviroment variable \"#{token_var_name}\" with the personal access token to use."
end
unless username
username = UI.input "Please type in your GitHub username or cancel the release and define enviroment variable \"#{username_var_name}\" with your GitHub username."
username = UI.input "Please type in your GitHub username or cancel the release and define enviroment variable \"#{username_var_name}\" with your GitHub username."
end
@ -63,18 +65,12 @@ platform :mac do
auth_string = "#{username}:#{access_token}"
changelog = create_changelog.gsub(/`/, '``').gsub(/'/, ''').gsub(/"/, '"').gsub(/\n/, '\\n')
version = version_bump_podspec(path: 'Cuckoo.podspec', bump_type: release_type)
# Make sure the changed podspec is valid.
pod_lib_lint(allow_warnings: true)
git_commit(path: './Cuckoo.podspec', message: "Bump version.")
add_git_tag(tag: version)
push_to_git_remote
# https://developer.github.com/v3/repos/releases/#create-a-release
release_title = "#{version}"
release_body = "#{changelog}"
creation_body = "'{\"tag_name\":\"#{version}\",\"target_commitish\":\"master\", \"name\":\"#{release_title}\", \"body\":\"#{release_body}\", \"draft\":false, \"prerelease\":false}'"
creation_body = "'{\"tag_name\":\"#{version}\",\"target_commitish\":\"master\", \"name\":\"New Release #{version}\", \"body\":\"#{changelog}\", \"draft\":false, \"prerelease\":false}'"
creation_response = JSON.parse(`curl -X POST -d #{creation_body} -u #{auth_string} #{api_url} -v`)
UI.crash! 'Release draft creation failed!' unless creation_response
upload_url = (creation_response['upload_url']).sub(/{.*name.*}/, '')
@ -83,7 +79,8 @@ platform :mac do
upload_response = JSON.parse(`curl -X POST --data-binary "@#{cuckoo_gen_path}" -u "#{auth_string}" "#{upload_url}?name=#{binary_name}" -H "Content-Type:application/octet-stream"`)
UI.crash! 'Release draft upload failed!' unless upload_response
pod_push(path: 'Cuckoo.podspec', allow_warnings: true)
pod_lib_lint
pod_push(path: 'Cuckoo.podspec')
UI.success "All done!\nYou can now visit #{creation_response['url']} (command+click) and release the thing."
end
@ -98,11 +95,10 @@ platform :mac do
end
after_all do
reset_git_repo(disregard_gitignore: false)
reset_git_repo
end
error do |_, exception|
reset_git_repo(disregard_gitignore: false)
UI.error "Release failed. This might help: #{exception}"
end
end

32
.gitignore vendored
View File

@ -1,4 +1,5 @@
# Xcode
#
.build/
build/
*.pbxuser
@ -18,33 +19,26 @@ DerivedData
*.xcuserstate
# CocoaPods
Pods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
# Pods/
# Carthage
Carthage/Build
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
.DS_Store
default.profraw
Tests/**/Generated/*.swift
Tests/Generated/*.swift
Generator/*.app
# AppCode
.idea/
cuckoo_generator
.fastlane
Generator/CuckooGenerator.xcodeproj
Cuckoo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Cuckoo.xcodeproj/project.xcworkspace/contents.xcworkspacedata
Cuckoo.xcworkspace
Cuckoo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Tuist/Dependencies/graph.json
Tuist/Dependencies/Carthage
Tuist/Dependencies/SwiftPackageManager
Tuist/Dependencies/Cocoapods
Generator/Generator.xcodeproj
Generator/GeneratedMocks.swift
.fastlane

View File

@ -1,104 +0,0 @@
{
"pins" : [
{
"identity" : "commandant",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Carthage/Commandant.git",
"state" : {
"revision" : "07cad52573bad19d95844035bf0b25acddf6b0f6",
"version" : "0.15.0"
}
},
{
"identity" : "filekit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/nvzqz/FileKit.git",
"state" : {
"branch" : "develop",
"revision" : "6937ec38b0c383b0505caeea6603e62086bf5431"
}
},
{
"identity" : "nimble",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Quick/Nimble.git",
"state" : {
"revision" : "e9d769113660769a4d9dd3afb855562c0b7ae7b0",
"version" : "7.3.4"
}
},
{
"identity" : "pathkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kylef/PathKit.git",
"state" : {
"revision" : "3bfd2737b700b9a36565a8c94f4ad2b050a5e574",
"version" : "1.0.1"
}
},
{
"identity" : "quick",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Quick/Quick.git",
"state" : {
"revision" : "f2b5a06440ea87eba1a167cab37bf6496646c52e",
"version" : "1.3.4"
}
},
{
"identity" : "result",
"kind" : "remoteSourceControl",
"location" : "https://github.com/antitypical/Result.git",
"state" : {
"revision" : "2ca499ba456795616fbc471561ff1d963e6ae160",
"version" : "4.1.0"
}
},
{
"identity" : "sourcekitten",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/SourceKitten.git",
"state" : {
"revision" : "79ca340f609adee48defa966e6a3dd0e0acbeb08",
"version" : "0.21.3"
}
},
{
"identity" : "spectre",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kylef/Spectre.git",
"state" : {
"revision" : "26cc5e9ae0947092c7139ef7ba612e34646086c7",
"version" : "0.10.1"
}
},
{
"identity" : "stencil",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kylef/Stencil.git",
"state" : {
"revision" : "ccd9402682f4c07dac9561befd207c8156e80e20",
"version" : "0.14.2"
}
},
{
"identity" : "swxmlhash",
"kind" : "remoteSourceControl",
"location" : "https://github.com/drmohundro/SWXMLHash.git",
"state" : {
"revision" : "f43166a8e18fdd0857f29e303b1bb79a5428bca0",
"version" : "4.9.0"
}
},
{
"identity" : "yams",
"kind" : "remoteSourceControl",
"location" : "https://github.com/jpsim/Yams.git",
"state" : {
"revision" : "b08dba4bcea978bf1ad37703a384097d3efce5af",
"version" : "1.0.2"
}
}
],
"version" : 2
}

1
.swift-version Normal file
View File

@ -0,0 +1 @@
4.0

View File

@ -1,15 +1,19 @@
language: objective-c
osx_image: xcode10.2
osx_image: xcode9
xcode_project: Cuckoo.xcodeproj
xcode_scheme: Cuckoo
xcode_sdk: macosx
before_script:
- pod install --repo-update
- gem install cucumber --no-rdoc --no-ri
- gem install aruba --no-rdoc --no-ri
script:
- xcodebuild -workspace 'Cuckoo.xcworkspace' -scheme 'Cuckoo+OCMock-macOS' clean test | xcpretty
- pod lib lint --allow-warnings
- pushd Generator
- make test
- popd
- xcodebuild -project 'Cuckoo.xcodeproj' -scheme 'Cuckoo-macOS+Run' clean test | xcpretty
- pod lib lint
after_script:
- sleep 5

View File

@ -1 +0,0 @@
3.11.0

View File

@ -1,172 +1,89 @@
# Changelog
## 1.4.1
- Sidestep `SourceKit`'s off-by-one bug when parsing generic parameter inheritance.
- Fix incorrect `where` clause parsing.
## 1.4.0
- Add tvOS targets and schemes.
- Integrate `tuist`, fixing Carthage issues.
- Fix type equality check to rule out whitespace inconsistencies.
## 1.3.2
- Fix `image not found` error for iOS 13 and beyond.
## 1.3.1
- **Swift Package Manager support**
- Added tvOS target (thanks @rodrigoff).
- Fixed accessibility to match enclosing container.
- Restructured README.
## 1.3.0
- Fix closure generation where explicit return type is required in `withoutActuallyEscaping` since swift 5.1 (XCode 11)
- Switch swift_version to 5.0 for Cocoapods
## 1.2.0
#### Features
- Objective-C mocking! Mock system classes/protocols as well as dynamic Swift classes. This is an optional subspec `Cuckoo/OCMock`.
- Convenience matchers for sequences and dictionaries. No need to use `equal(to:)` anymore, passing the `Array`/`Set`/`Dictionary` itself is enough from now on!
## 1.1.1
- Fix property, initializer, and function accessibility in public protocols.
## 1.1.0
#### Features:
- Add a simple type guesser based on assigned value.
#### Fixes:
- Fix accessibility problems.
## 1.0.6
- Fix a bug where adding a private name to a function made it generate twice and fail the compilation.
## 1.0.5
- Fix generic protocol generation and type erasure with multiple methods of same name.
- Fix a bug concerning empty public name methods with no private ones.
- Exit `run` script with error if it fails to get generator download URL.
## 1.0.4
- The `run` script doesn't use `realpath` command anymore because it's not available by default on Mac OS.
- Remove redundant stubbing of optional classes.
## 1.0.3
- Add support for optional read-only properties.
## 1.0.2
- Fix `where` clause that doesn't work in Swift 4.
- Fix some `run` script bugs.
## 1.0.1
- Fix `any()` not working anymore by itself with optional parameters in functions.
## 1.0.0
#### Features:
- **Generics** is now fully supported! This includes generic classes, protocols and methods.
- `Dictionary` matching out of the box.
- Better closure matching. Now allowing up to 7 parameter closures.
- `rethrows` functions now work properly.
- Allow non-optional values to be passed as matchers for `Optional`s just like in normal Swift code.
- Add support for inout method parameters.
#### Fixes:
- Update the `build_generator` script to work with Swift 5.
- Fix not being able to put `Optional` into functions accepting `Optional`s.
- Accessibility of variables and functions in `public` classes are now `public` as well.
## 0.13.0
- Updated for **Xcode 10.2** and **Swift 5**.
## 0.12.1
- Add class accessibility support.
- Add support for attributes (e.g. `@available`).
- Add support for subimport (e.g. `import struct UICat.Food`).
- Add `--clean` option to the run script to always build or download the generator (promptly forget to add its documentation to `README.md`).
- Ignore `final` classes (because we mock by inheritance).
- Smaller fixes and improvements in the whole project.
## 0.12.0
- Add first draft of a new Mock initialization DSL.
- Add `enableDefaultImplementation` to protocol `Mock`.
- Reintroduce support for pre-0.11.0 Cuckoo spies.
- Add **regular expression** `class` and `protocol` matching.
- Add **glob** switch that parses input paths as globs enabling for easier project scaling.
- Build generator by default. Download using `--download [VERSION]` option.
- Modify the `run` bash script to allow the user to build rather than download the `cuckoo_generator`.
- Add a debug flag that generates general info above methods when used.
- Fix escaping closure (crashing in Xcode 10).
## 0.11.0
- Added contribution guide.
- **BREAKING CHANGE**: Spies were reworked. They now use superclasses as their victims if enabled. Please see the Readme for more information.
- **BREAKING CHANGE**: Verification of properties' `get` is now a method you have to call, instead of a property. This change was made to remove the "unused result" warning. [bug #141](https://github.com/Brightify/Cuckoo/issues/141)
* Added contribution guide.
* **BREAKING CHANGE**: Spies were reworked. They now use superclasses as their victims if enabled. Please see the Readme for more information.
* **BREAKING CHANGE**: Verification of properties' `get` is now a method you have to call, instead of a property. This change was made to remove the "unused result" warning. [bug #141](https://github.com/Brightify/Cuckoo/issues/141)
## 0.10.2
- Double the maximum parameters in `call` and `callThrows` methods. [bug #145](https://github.com/Brightify/Cuckoo/issues/145)
- Make the generator deterministic by sorting input files.
- [bug #157](https://github.com/Brightify/Cuckoo/issues/157)
- [PR #158 - kudos to IanKeen](https://github.com/Brightify/Cuckoo/pull/158)
- Add `equalTo` for `Array` and `Set` where `Element` is `Equatable`.
* Double the maximum parameters in `call` and `callThrows` methods. [bug #145](https://github.com/Brightify/Cuckoo/issues/145)
* Make the generator deterministic by sorting input files.
* [bug #157](https://github.com/Brightify/Cuckoo/issues/157)
* [PR #158 - kudos to IanKeen](https://github.com/Brightify/Cuckoo/pull/158)
* Add `equalTo` for `Array` and `Set` where `Element` is `Equatable`.
## 0.10.1
- Fixed some errors with getters [bug #151](https://github.com/Brightify/Cuckoo/issues/151)
* Fixed some errors with getters [bug #151](https://github.com/Brightify/Cuckoo/issues/151)
## 0.10.0
- Updated for **Swift 4** (Xcode 9 GM)
* Updated for Swift 4 (Xcode 9 GM)
## 0.9.2
- Fixed crash when source files were using non-ASCII characters - [bug #126](https://github.com/Brightify/Cuckoo/issues/126)
- Added `--exclude` parameter to explicitly exclude some types from mocking - [PR #112](https://github.com/Brightify/Cuckoo/pull/112) - (thanks to nxtstep for the feature)
- Fixed compile errors when generating stubs where inner types were returned - [bug #118](https://github.com/Brightify/Cuckoo/issues/118)
- Added possibility to reset multiple mocks with different types at once - [but #103](https://github.com/Brightify/Cuckoo/issues/103)
* Fixed crash when source files were using non-ASCII characters - [bug #126](https://github.com/Brightify/Cuckoo/issues/126)
* Added `--exclude` parameter to explicitly exclude some types from mocking - [PR #112](https://github.com/Brightify/Cuckoo/pull/112) - (thanks to nxtstep for the feature)
* Fixed compile errors when generating stubs where inner types were returned - [bug #118](https://github.com/Brightify/Cuckoo/issues/118)
* Added possibility to reset multiple mocks with different types at once - [but #103](https://github.com/Brightify/Cuckoo/issues/103)
## 0.9.1
- Fixed "too complex to resolve in reasonable time" error in generator
- Fixed directory names for case sensitive file systems - [PR #114](https://github.com/Brightify/Cuckoo/pull/115) - (thanks to sundance2000 for the fix)
- Moved repository from `SwiftKit` to `Brightify` organization on GitHub.
* Fixed "too complex to resolve in reasonable time" error in generator
* Fixed directory names for case sensitive file systems - [PR #114](https://github.com/Brightify/Cuckoo/pull/115) - (thanks to sundance2000 for the fix)
* Moved repository from `SwiftKit` to `Brightify` organization on GitHub.
## 0.9.0
- Rewritten Generator to use Stencil
- Use Swift PM for generator binary (results in faster builds)
- This release works with Swift 3.1
* Rewritten Generator to use Stencil
* Use Swift PM for generator binary (results in faster builds)
* This release works with Swift 3.1
## 0.8.4
- Added support for inheritance mocking.
* Added support for inheritance mocking.
## 0.8.3
- Added support for `fileprivate` (thanks to lvdstam for implementation).
- Added support for default values (thanks to lvdstam for implementation).
- Fixed wrongly generated code where public class had internal members.
* Added support for `fileprivate` (thanks to lvdstam for implementation).
* Added support for default values (thanks to lvdstam for implementation).
* Fixed wrongly generated code where public class had internal members.
## 0.8.2
- Show error in generator in build log.
- Fixed crash of generator when instance variable type is not explicitly set.
- Fixed support of closures and unwrapped optionals.
* Show error in generator in build log.
* Fixed crash of generator when instance variable type is not explicitly set.
* Fixed support of closures and unwrapped optionals.
## 0.8.1
- Set "Reflection Metadata" to "None" to fix #72
* Set "Reflection Metadata" to "None" to fix #72
## 0.8.0
- Support for **Swift 3**
- Added --no-class-mocking parameter to generator
- Added Stub objects
* Support for Swift 3
* Added --no-class-mocking parameter to generator
* Added Stub objects
## 0.7.0
- Updated documentation
- Added more automated tests
- Added --file-prefix parameter to generator
- `and` and `or` methods can now be used with `Matchable` (literals)
- Using of custom `Matchable`, `ParameterMatcher` and `CallMatcher` is now easier
- Improved fail messages
- Merged generator and runtime repositories, making updating easier.
- Added support for named arguments in methods.
- Added support for classes with custom initializers.
- Changed usage of spies. Instead of `init(spyOn:)` use `init().spy(on:)`.
* Updated documentation
* Added more automated tests
* Added --file-prefix parameter to generator
* `and` and `or` methods can now be used with `Matchable` (literals)
* Using of custom `Matchable`, `ParameterMatcher` and `CallMatcher` is now easier
* Improved fail messages
* Merged generator and runtime repositories, making updating easier.
* Added support for named arguments in methods.
* Added support for classes with custom initializers.
* Changed usage of spies. Instead of `init(spyOn:)` use `init().spy(on:)`.
## 0.6.0
- Added release notes
- Added stub resetting
- Added `thenCallRealImplementation`
- Added argument capturing
- Added `verifyNoMoreInteractions`
- Added on going stubbing
- Added `thenDoNothing`
* Added release notes
* Added stub resetting
* Added `thenCallRealImplementation`
* Added argument capturing
* Added `verifyNoMoreInteractions`
* Added on going stubbing
* Added `thenDoNothing`

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Cuckoo"
s.version = "1.10.3"
s.version = "0.12.0"
s.summary = "Cuckoo - first boilerplate-free Swift mocking framework."
s.description = <<-DESC
Cuckoo is a mocking framework with an easy to use API (inspired by Mockito).
@ -15,12 +15,12 @@ Pod::Spec.new do |s|
:tag => s.version.to_s
}
s.ios.deployment_target = '9.0'
s.osx.deployment_target = '11.0'
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.9'
#s.watchos.deployment_target = '2.0' # watchos does not include XCTest framework :(
s.tvos.deployment_target = '9.0'
s.source_files = ['Source/**/*.swift']
generator_name = 'cuckoo_generator'
s.swift_version = '5.0'
s.preserve_paths = ['Generator/**/*', 'run', 'build_generator', generator_name]
s.prepare_command = <<-CMD
curl -Lo #{generator_name} https://github.com/Brightify/Cuckoo/releases/download/#{s.version}/#{generator_name}
@ -28,27 +28,5 @@ Pod::Spec.new do |s|
CMD
s.frameworks = 'XCTest', 'Foundation'
s.requires_arc = true
s.pod_target_xcconfig = {
'ENABLE_BITCODE' => 'NO',
'SWIFT_REFLECTION_METADATA_LEVEL' => 'none',
'OTHER_LDFLAGS' => '$(inherited) -weak-lXCTestSwiftSupport',
}
s.default_subspec = 'Swift'
# When building for iOS versions 12 and lower in Xcode 11, a wild `image not found` appears.
# This is a workaround for that annoying error.
s.xcconfig = (8..12).map { |major| (0..5).map { |minor| [major, minor] } }.flatten(1).inject(Hash.new) do |hash, (major, minor)|
hash["LIBRARY_SEARCH_PATHS[sdk=iphoneos#{major}.#{minor}]"] = '$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift-$(SWIFT_VERSION)/$(PLATFORM_NAME)'
hash
end
s.subspec 'Swift' do |sub|
sub.source_files = 'Source/**/*.swift'
end
s.subspec 'OCMock' do |sub|
sub.source_files = 'OCMock/**/*.{h,m,swift}'
sub.dependency 'Cuckoo/Swift'
sub.dependency 'OCMock', '3.8.1'
end
s.pod_target_xcconfig = { 'ENABLE_BITCODE' => 'NO', 'SWIFT_REFLECTION_METADATA_LEVEL' => 'none' }
end

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Cuckoo.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -14,9 +14,9 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC1E5664C17BDCFB45CF1DF4"
BlueprintIdentifier = "18E2A28C1FBB1CFC0058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-tvOS"
BlueprintName = "Cuckoo-iOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@ -26,24 +26,37 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6AD4A9670FA783A1EC213000"
BuildableName = "Cuckoo_tvOSTests.xctest"
BlueprintName = "Cuckoo-tvOSTests"
BlueprintIdentifier = "18E2A2B51FBB211F0058FEC5"
BuildableName = "Cuckoo_iOSTests.xctest"
BlueprintName = "Cuckoo_iOSTests"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A28C1FBB1CFC0058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-iOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@ -53,12 +66,21 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC1E5664C17BDCFB45CF1DF4"
BlueprintIdentifier = "18E2A28C1FBB1CFC0058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-tvOS"
BlueprintName = "Cuckoo-iOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "USE_RUN"
value = "true"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@ -69,9 +91,9 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AC1E5664C17BDCFB45CF1DF4"
BlueprintIdentifier = "18E2A28C1FBB1CFC0058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-tvOS"
BlueprintName = "Cuckoo-iOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -14,7 +14,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6A8F12C3A778933132EBA10D"
BlueprintIdentifier = "18E2A28C1FBB1CFC0058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-iOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
@ -26,24 +26,37 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "92C74015545203C1F4395DD4"
BlueprintIdentifier = "18E2A2B51FBB211F0058FEC5"
BuildableName = "Cuckoo_iOSTests.xctest"
BlueprintName = "Cuckoo-iOSTests"
BlueprintName = "Cuckoo_iOSTests"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A28C1FBB1CFC0058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-iOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@ -53,12 +66,14 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6A8F12C3A778933132EBA10D"
BlueprintIdentifier = "18E2A28C1FBB1CFC0058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-iOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@ -69,7 +84,7 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6A8F12C3A778933132EBA10D"
BlueprintIdentifier = "18E2A28C1FBB1CFC0058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-iOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">

View File

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A29A1FBB1D400058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A2A61FBB20830058FEC5"
BuildableName = "Cuckoo_macOSTests.xctest"
BlueprintName = "Cuckoo_macOSTests"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A29A1FBB1D400058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A29A1FBB1D400058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "USE_RUN"
value = "true"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A29A1FBB1D400058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -14,7 +14,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1DC56D8212E4351521A57BD1"
BlueprintIdentifier = "18E2A29A1FBB1D400058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
@ -26,24 +26,37 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DBDB2DD4E7BE487EEB257CC8"
BlueprintIdentifier = "18E2A2A61FBB20830058FEC5"
BuildableName = "Cuckoo_macOSTests.xctest"
BlueprintName = "Cuckoo-macOSTests"
BlueprintName = "Cuckoo_macOSTests"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A29A1FBB1D400058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@ -53,12 +66,14 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1DC56D8212E4351521A57BD1"
BlueprintIdentifier = "18E2A29A1FBB1D400058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@ -69,7 +84,7 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1DC56D8212E4351521A57BD1"
BlueprintIdentifier = "18E2A29A1FBB1D400058FEC5"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">

View File

@ -1,22 +0,0 @@
<?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>$(DEVELOPMENT_LANGUAGE)</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>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?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>$(DEVELOPMENT_LANGUAGE)</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>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,24 +0,0 @@
<?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>$(DEVELOPMENT_LANGUAGE)</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>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright ©. All rights reserved.</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?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>$(DEVELOPMENT_LANGUAGE)</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>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?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>$(DEVELOPMENT_LANGUAGE)</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>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?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>$(DEVELOPMENT_LANGUAGE)</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>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?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>$(DEVELOPMENT_LANGUAGE)</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>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,24 +0,0 @@
<?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>$(DEVELOPMENT_LANGUAGE)</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>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright ©. All rights reserved.</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?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>$(DEVELOPMENT_LANGUAGE)</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>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?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>$(DEVELOPMENT_LANGUAGE)</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>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,280 +1,192 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.5)
rexml
activesupport (6.1.4.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.600.0)
aws-sdk-core (3.131.2)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.57.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
claide (1.1.0)
cocoapods (1.11.2)
addressable (~> 2.8)
CFPropertyList (2.3.5)
activesupport (4.2.10)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
babosa (1.0.2)
claide (1.0.2)
cocoapods (1.4.0)
activesupport (>= 4.0.2, < 5)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.11.2)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.4.0, < 2.0)
cocoapods-core (= 1.4.0)
cocoapods-deintegrate (>= 1.0.2, < 2.0)
cocoapods-downloader (>= 1.1.3, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.4.0, < 2.0)
cocoapods-stats (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.3.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (>= 2.3.0, < 3.0)
fourflusher (~> 2.0.1)
gh_inspector (~> 1.0)
molinillo (~> 0.8.0)
molinillo (~> 0.6.4)
nap (~> 1.0)
ruby-macho (>= 1.0, < 3.0)
xcodeproj (>= 1.21.0, < 2.0)
cocoapods-core (1.11.2)
activesupport (>= 5.0, < 7)
addressable (~> 2.8)
algoliasearch (~> 1.0)
concurrent-ruby (~> 1.1)
ruby-macho (~> 1.1)
xcodeproj (>= 1.5.4, < 2.0)
cocoapods-core (1.4.0)
activesupport (>= 4.0.2, < 6)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
netrc (~> 0.11)
public_suffix (~> 4.0)
typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.5)
cocoapods-downloader (1.5.1)
cocoapods-deintegrate (1.0.2)
cocoapods-downloader (1.1.3)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.1)
cocoapods-trunk (1.6.0)
cocoapods-search (1.0.0)
cocoapods-stats (1.0.0)
cocoapods-trunk (1.3.0)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.2.0)
cocoapods-try (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
concurrent-ruby (1.1.9)
declarative (0.0.20)
digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701)
commander-fastlane (4.4.6)
highline (~> 1.7.2)
concurrent-ruby (1.0.5)
declarative (0.0.9)
declarative-option (0.1.0)
domain_name (0.5.20170404)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6)
emoji_regex (3.2.3)
dotenv (2.2.1)
escape (0.0.4)
ethon (0.15.0)
ffi (>= 1.15.0)
excon (0.92.3)
faraday (1.10.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
excon (0.58.0)
faraday (0.13.1)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
faraday_middleware (1.2.0)
faraday (~> 1.0)
fastimage (2.2.6)
fastlane (2.206.2)
faraday_middleware (0.12.2)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.0)
fastlane (2.84.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
bundler (>= 1.12.0, < 2.0.0)
colored
commander (~> 4.6)
commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
excon (>= 0.45.0, < 1.0.0)
faraday (~> 0.9)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
faraday_middleware (~> 0.9)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
google-api-client (>= 0.13.1, < 0.14.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
mini_magick (~> 4.5.1)
multi_json
multi_xml (~> 0.5)
multipart-post (~> 2.0.0)
naturally (~> 2.2)
optparse (~> 0.1.1)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
public_suffix (~> 2.0.0)
rubyzip (>= 1.1.0, < 2.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
slack-notifier (>= 2.0.0, < 3.0.0)
terminal-notifier (>= 1.6.2, < 2.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcodeproj (>= 1.5.2, < 2.0.0)
xcpretty (>= 0.2.4, < 1.0.0)
xcpretty-travis-formatter (>= 0.0.3)
ffi (1.15.4)
fourflusher (2.3.1)
fourflusher (2.0.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.22.0)
google-apis-core (>= 0.5, < 2.a)
google-apis-core (0.6.0)
google-api-client (0.13.1)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
googleauth (~> 0.5)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
webrick
google-apis-iamcredentials_v1 (0.12.0)
google-apis-core (>= 0.6, < 2.a)
google-apis-playcustomapp_v1 (0.9.0)
google-apis-core (>= 0.6, < 2.a)
google-apis-storage_v1 (0.15.0)
google-apis-core (>= 0.5, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.2.0)
google-cloud-storage (1.36.2)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.1.3)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
retriable (>= 2.0, < 4.0)
googleauth (0.5.3)
faraday (~> 0.12)
jwt (~> 1.4)
logging (~> 2.0)
memoist (~> 0.12)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.5)
os (~> 0.9)
signet (~> 0.7)
highline (1.7.8)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
i18n (1.8.11)
i18n (0.9.1)
concurrent-ruby (~> 1.0)
jmespath (1.6.1)
json (2.6.2)
jwt (2.4.1)
memoist (0.16.2)
mini_magick (4.11.0)
mini_mime (1.1.2)
minitest (5.14.4)
molinillo (0.8.0)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
nap (1.1.0)
naturally (2.2.1)
netrc (0.11.0)
optparse (0.1.1)
os (1.1.4)
plist (3.6.0)
public_suffix (4.0.7)
rake (13.0.6)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.2.5)
rouge (2.0.7)
ruby-macho (2.5.1)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
signet (0.16.1)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.0)
jwt (>= 1.5, < 3.0)
json (2.1.0)
jwt (1.5.6)
little-plugger (1.1.4)
logging (2.2.2)
little-plugger (~> 1.1)
multi_json (~> 1.10)
simctl (1.6.8)
CFPropertyList
naturally
terminal-notifier (2.0.0)
memoist (0.16.0)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mini_magick (4.5.1)
minitest (5.10.3)
molinillo (0.6.4)
multi_json (1.12.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
nanaimo (0.2.3)
nap (1.1.0)
netrc (0.11.0)
os (0.9.6)
plist (3.3.0)
public_suffix (2.0.5)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
retriable (3.1.1)
rouge (2.0.7)
ruby-macho (1.1.0)
rubyzip (1.2.1)
security (0.1.3)
signet (0.7.3)
addressable (~> 2.3)
faraday (~> 0.9)
jwt (~> 1.5)
multi_json (~> 1.10)
slack-notifier (2.3.2)
terminal-notifier (1.8.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
tty-screen (0.8.1)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
thread_safe (0.3.6)
tty-cursor (0.5.0)
tty-screen (0.6.4)
tty-spinner (0.8.0)
tty-cursor (>= 0.5.0)
tzinfo (1.2.4)
thread_safe (~> 0.1)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (1.8.0)
webrick (1.7.0)
unf_ext (0.0.7.4)
unicode-display_width (1.3.0)
word_wrap (1.0.0)
xcodeproj (1.21.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
xcodeproj (1.5.4)
CFPropertyList (~> 2.3.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
rexml (~> 3.2.4)
xcpretty (0.3.0)
nanaimo (~> 0.2.3)
xcpretty (0.2.8)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.1)
xcpretty-travis-formatter (0.0.4)
xcpretty (~> 0.2, >= 0.0.7)
zeitwerk (2.5.1)
PLATFORMS
arm64-darwin-21
ruby
DEPENDENCIES
cocoapods
@ -282,4 +194,4 @@ DEPENDENCIES
json
BUNDLED WITH
2.2.32
1.16.1

View File

@ -0,0 +1,46 @@
import Foundation
@discardableResult
func shell(_ args: [String], inDir dir: String? = nil) -> Int32 {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
if let dir = dir {
task.currentDirectoryPath = dir
}
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
let projectDir = ProcessInfo.processInfo.environment["PROJECT_DIR", default: "."]
let output = "\(projectDir)/Tests/Generated/GeneratedMocks.swift"
// Use seperate variables for each file.
let generatorArguments = [
"generate",
"--testable",
"Cuckoo",
"--exclude",
"ExcludedTestClass,ExcludedProtocol",
"--output",
output,
"\(projectDir)/Tests/Source/ClassForStubTesting.swift",
"\(projectDir)/Tests/Source/ClassWithOptionals.swift",
"\(projectDir)/Tests/Source/ObjcProtocol.swift",
"\(projectDir)/Tests/Source/UnicodeTestProtocol.swift",
"\(projectDir)/Tests/Source/TestedProtocol.swift",
"\(projectDir)/Tests/Source/TestedClass.swift",
"\(projectDir)/Tests/Source/TestedSubclass.swift",
"\(projectDir)/Tests/Source/TestedSubProtocol.swift",
"\(projectDir)/Tests/Source/ExcludedTestClass.swift",
]
let useRun = Bool(ProcessInfo.processInfo.environment["USE_RUN", default: "false"]) ?? false
if useRun {
shell(["\(projectDir)/run", "--clean"] + generatorArguments)
} else {
shell(["swift", "run", "cuckoo_generator"] + generatorArguments, inDir: "\(projectDir)/Generator/")
}

1
Generator/.swift-version Normal file
View File

@ -0,0 +1 @@
4.1

View File

@ -1,22 +0,0 @@
<?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>$(DEVELOPMENT_LANGUAGE)</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>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright ©. All rights reserved.</string>
</dict>
</plist>

72
Generator/Package.pins Normal file
View File

@ -0,0 +1,72 @@
{
"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
}

106
Generator/Package.resolved Normal file
View File

@ -0,0 +1,106 @@
{
"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/TadeasKriz/FileKit.git",
"state": {
"branch": "develop",
"revision": "0acc6e7c336bbd2336c2f9670564dac9ec9c03fe",
"version": null
}
},
{
"package": "Nimble",
"repositoryURL": "https://github.com/Quick/Nimble.git",
"state": {
"branch": null,
"revision": "cd6dfb86f496fcd96ce0bc6da962cd936bf41903",
"version": "7.3.1"
}
},
{
"package": "PathKit",
"repositoryURL": "https://github.com/kylef/PathKit.git",
"state": {
"branch": null,
"revision": "fa81fa9e3a9f59645159c4ea45c0c46ee6558f71",
"version": "0.9.1"
}
},
{
"package": "Quick",
"repositoryURL": "https://github.com/Quick/Quick.git",
"state": {
"branch": null,
"revision": "5fbf13871d185526993130c3a1fad0b70bfe37ce",
"version": "1.3.2"
}
},
{
"package": "Result",
"repositoryURL": "https://github.com/antitypical/Result.git",
"state": {
"branch": null,
"revision": "8fc088dcf72802801efeecba76ea8fb041fb773d",
"version": "4.0.0"
}
},
{
"package": "SourceKitten",
"repositoryURL": "https://github.com/jpsim/SourceKitten.git",
"state": {
"branch": null,
"revision": "4be914be6fa49cd30b1e7ef5d32d06c037d8f469",
"version": "0.21.2"
}
},
{
"package": "Spectre",
"repositoryURL": "https://github.com/kylef/Spectre.git",
"state": {
"branch": null,
"revision": "e34d5687e1e9d865e3527dd58bc2f7464ef6d936",
"version": "0.8.0"
}
},
{
"package": "Stencil",
"repositoryURL": "https://github.com/kylef/Stencil.git",
"state": {
"branch": null,
"revision": "b476e50f89577f5848e8013dbf0a850abac892aa",
"version": "0.12.1"
}
},
{
"package": "SWXMLHash",
"repositoryURL": "https://github.com/drmohundro/SWXMLHash.git",
"state": {
"branch": null,
"revision": "0ce63a93a455adb3cd5e4c55f78f1232a590a5a5",
"version": "4.7.2"
}
},
{
"package": "Yams",
"repositoryURL": "https://github.com/jpsim/Yams.git",
"state": {
"branch": null,
"revision": "26ab35f50ea891e8edefcc9d975db2f6b67e1d68",
"version": "1.0.1"
}
}
]
},
"version": 1
}

24
Generator/Package.swift Normal file
View File

@ -0,0 +1,24 @@
// swift-tools-version:4.0
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/TadeasKriz/FileKit.git", .branch("develop")),
.package(url: "https://github.com/kylef/Stencil.git", from: "0.9.0"),
.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"]),
]
)

View File

@ -1,56 +0,0 @@
import ProjectDescription
import ProjectDescriptionHelpers
// MARK: project definition
let project = Project(
name: "Generator",
options: .options(automaticSchemesOptions: .disabled, disableSynthesizedResourceAccessors: true),
packages: [
.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", .exact("0.14.2")),
.package(url: "https://github.com/Carthage/Commandant.git", .exact("0.15.0")),
],
targets: [
Target(
name: "CuckooGenerator",
platform: .macOS,
product: .commandLineTool,
productName: "cuckoo_generator",
bundleId: "CuckooGenerator",
deploymentTarget: DeploymentTarget.macOS(targetVersion: "10.13"),
sources: "Source/**",
dependencies: ["FileKit", "SourceKittenFramework", "Stencil", "Commandant"].map(TargetDependency.package(product:))
),
],
schemes: [
Scheme(
name: "Generator",
buildAction: BuildAction.buildAction(
targets: ["CuckooGenerator"],
postActions: [
ExecutionAction(
title: "Copy executable",
scriptText: #"\cp "$BUILT_PRODUCTS_DIR/$EXECUTABLE_NAME" "$PROJECT_DIR/bin/cuckoo_generator""#,
target: "CuckooGenerator"
)
],
runPostActionsOnFailure: false
),
runAction: RunAction.runAction(
executable: "CuckooGenerator",
arguments: Arguments(
launchArguments: [
// Any changes here should be reflected in `../Project.swift` as well.
LaunchArgument(name: "generate", isEnabled: true),
LaunchArgument(name: "--testable Cuckoo", isEnabled: true),
LaunchArgument(name: "--exclude ExcludedTestClass,ExcludedProtocol", isEnabled: true),
LaunchArgument(name: #"--output "$PROJECT_DIR"/GeneratedMocks.swift"#, isEnabled: true),
// This is the input file(s) glob.
LaunchArgument(name: #"--glob "$PROJECT_DIR"/../Tests/Swift/Source/*.swift"#, isEnabled: true),
]
)
)
),
]
)

View File

@ -0,0 +1,36 @@
//
// 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"
}

View File

@ -1,3 +1,11 @@
//
// FileHeaderHandler.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
import FileKit
@ -16,9 +24,9 @@ public struct FileHeaderHandler {
}
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: "")
var imports = Array(Set(file.declarations.only(Import.self).map { "import " + $0.library + "\n" })).sorted().joined(separator: "")
if imports.isEmpty == false {
imports = "\n\(imports)"
imports = "\n" + imports
}
return "import Cuckoo\n" + getTestableImports(testableFrameworks: testableFrameworks) + imports
}

View File

@ -0,0 +1,125 @@
//
// 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 parameters = value as? [MethodParameter] else { return value }
return self.matchableGenerics(with: parameters)
}
ext.registerFilter("matchableGenericWhere") { (value: Any?) in
guard let parameters = value as? [MethodParameter] else { return value }
return self.matchableGenerics(where: parameters)
}
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?, arguments: [Any?]) in
guard let parameters = value as? [MethodParameter] else { return value }
let s = self.openNestedClosure(for: parameters, throwing: arguments.first as? Bool)
return s
}
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 matchableGenerics(with parameters: [MethodParameter]) -> String {
guard parameters.isEmpty == false else { return "" }
let genericParameters = (1...parameters.count).map { "M\($0): Cuckoo.Matchable" }.joined(separator: ", ")
return "<\(genericParameters)>"
}
private func matchableGenerics(where parameters: [MethodParameter]) -> String {
guard parameters.isEmpty == false else { return "" }
let whereClause = parameters.enumerated().map { "M\($0 + 1).MatchedType == \(genericSafeType(from: $1.typeWithoutAttributes))" }.joined(separator: ", ")
return " where \(whereClause)"
}
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 parameters: [MethodParameter], throwing: Bool? = false) -> String {
var fullString = ""
for (index, parameter) in parameters.enumerated() {
if parameter.isClosure && !parameter.isEscaping {
let indents = String(repeating: "\t", count: index + 1)
let tries = (throwing ?? false) ? " try " : " "
fullString += "\(indents)return\(tries)withoutActuallyEscaping(\(parameter.name), do: { (\(parameter.name)) 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 + 1)
fullString += "\(indents)})\n"
}
}
return fullString
}
}

View File

@ -1,3 +1,11 @@
//
// 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

View File

@ -0,0 +1,17 @@
//
// 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>

View File

@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -15,10 +15,14 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>0.8.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright ©. All rights reserved.</string>
<string>Copyright © 2016 Brightify. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,108 @@
//
// MockTemplate.swift
// CuckooGeneratorFramework
//
// Created by Tadeas Kriz on 11/14/17.
//
import Foundation
extension Templates {
static let mock = """
{% for container in containers %}
{% for attribute in container.attributes %}
{{ attribute.text }}
{% endfor %}
{{ container.accessibility }} class {{ container.mockName }}: {{ container.name }}, {% if container.isImplementation %}Cuckoo.ClassMock{% else %}Cuckoo.ProtocolMock{% endif %} {
{{ container.accessibility }} typealias MocksType = {{ container.name }}
{{ container.accessibility }} typealias Stubbing = __StubbingProxy_{{ container.name }}
{{ container.accessibility }} typealias Verification = __VerificationProxy_{{ container.name }}
private var __defaultImplStub: {{ container.name }}?
{{ container.accessibility }} let cuckoo_manager = Cuckoo.MockManager.preconfiguredManager ?? Cuckoo.MockManager(hasParent: {{ container.isImplementation }})
{{ container.accessibility }} func enableDefaultImplementation(_ stub: {{ container.name }}) {
__defaultImplStub = stub
cuckoo_manager.enableDefaultStubImplementation()
}
{% 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.parameterSignature }}) {{ method.returnSignature }} {
{{ method.parameters|openNestedClosure:method.isThrowing }}
return{% if method.isThrowing %} try{% endif %} cuckoo_manager.call{% if method.isThrowing %}Throws{% 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 %}
"""
}

View File

@ -0,0 +1,37 @@
//
// NopImplStubTemplate.swift
// CuckooGeneratorFramework
//
// Created by Tadeas Kriz on 11/14/17.
//
extension Templates {
static let noImplStub = """
{{container.accessibility}} class {{ container.name }}Stub: {{ container.name }} {
{% for property in container.properties %}
{{ 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.parameterSignature }}) {{ method.returnSignature }} {
return DefaultValueRegistry.defaultValue(for: {{method.returnType|genericSafe}}.self)
}
{% endfor %}
}
"""
}

View File

@ -0,0 +1,31 @@
//
// 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 %}
var {{property.name}}: Cuckoo.{{ property.stubType }}<{{ container.mockName }}, {{property.type|genericSafe}}> {
return .init(manager: cuckoo_manager, name: "{{property.name}}")
}
{% endfor %}
{% for method in container.methods %}
func {{method.name}}{{method.parameters|matchableGenericNames}}({{method.parameters|matchableParameterSignature}}) -> {{method.stubFunction}}<({{method.inputTypes|genericSafe}}){%if method.returnType != "Void" %}, {{method.returnType|genericSafe}}{%endif%}>{{method.parameters|matchableGenericWhere}} {
{{method.parameters|parameterMatchers}}
return .init(stub: cuckoo_manager.createStub(for: {{ container.mockName }}.self, method: "{{method.fullyQualifiedName}}", parameterMatchers: matchers))
}
{% endfor %}
}
"""
}

View File

@ -1,10 +1,17 @@
//
// 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: " ", count: times)
let indentation = String(repeating: "\t", count: times)
return self.components(separatedBy: CharacterSet.newlines).map {
indentation + $0

View File

@ -0,0 +1,38 @@
//
// 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 %}
var {{property.name}}: Cuckoo.Verify{% if property.isReadOnly %}ReadOnly{%endif%}Property<{{property.type|genericSafe}}> {
return .init(manager: cuckoo_manager, name: "{{property.name}}", callMatcher: callMatcher, sourceLocation: sourceLocation)
}
{% endfor %}
{% for method in container.methods %}
@discardableResult
func {{method.name}}{{method.parameters|matchableGenericNames}}({{method.parameters|matchableParameterSignature}}) -> Cuckoo.__DoNotUse<{{method.returnType|genericSafe}}>{{method.parameters|matchableGenericWhere}} {
{{method.parameters|parameterMatchers}}
return cuckoo_manager.verify("{{method.fullyQualifiedName}}", callMatcher: callMatcher, parameterMatchers: matchers, sourceLocation: sourceLocation)
}
{% endfor %}
}
"""
}

View File

@ -0,0 +1,269 @@
//
// Tokenizer.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
import SourceKittenFramework
public struct Tokenizer {
private let file: File
private let source: String
private let debugMode: Bool
public init(sourceFile: File, debugMode: Bool) {
self.file = sourceFile
self.debugMode = debugMode
source = sourceFile.contents
}
public func tokenize() -> FileRepresentation {
do {
let structure = try Structure(file: file)
let declarations = tokenize(structure.dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let imports = tokenize(imports: declarations)
return FileRepresentation(sourceFile: file, declarations: declarations + imports)
} catch {
print("Error")
}
return FileRepresentation(sourceFile: file, declarations: [])
}
private func tokenize(_ representables: [SourceKitRepresentable]) -> [Token] {
return representables.compactMap(tokenize)
}
internal static let nameNotSet = "name not set"
internal static let unknownType = "unknown type"
private func tokenize(_ representable: SourceKitRepresentable) -> Token? {
guard let dictionary = representable as? [String: SourceKitRepresentable] else { return nil }
// Common fields
let name = dictionary[Key.Name.rawValue] as? String ?? Tokenizer.nameNotSet
let kind = dictionary[Key.Kind.rawValue] as? String ?? Tokenizer.unknownType
// Inheritance
let inheritedTypes = dictionary[Key.InheritedTypes.rawValue] as? [SourceKitRepresentable] ?? []
let tokenizedInheritedTypes = inheritedTypes.compactMap { type -> InheritanceDeclaration in
guard let typeDict = type as? [String: SourceKitRepresentable] else {
return InheritanceDeclaration.empty
}
let name = typeDict[Key.Name.rawValue] as? String ?? Tokenizer.nameNotSet
return InheritanceDeclaration(name: name)
}
// Optional fields
let range = extractRange(from: dictionary, offset: .Offset, length: .Length)
let nameRange = extractRange(from: dictionary, offset: .NameOffset, length: .NameLength)
let bodyRange = extractRange(from: dictionary, offset: .BodyOffset, length: .BodyLength)
// Attributes
let attributes = (dictionary[Key.Attributes.rawValue] as? [SourceKitRepresentable] ?? [])
.compactMap { attribute -> Attribute? in
guard let attribute = attribute as? [String: SourceKitRepresentable],
let stringKind = attribute[Key.Attribute.rawValue] as? String,
let kind = Attribute.Kind(rawValue: stringKind),
let attributeRange = extractRange(from: attribute, offset: .Offset, length: .Length) else { return nil }
let startIndex = source.utf8.index(source.utf8.startIndex, offsetBy: attributeRange.lowerBound)
let endIndex = source.utf8.index(source.utf8.startIndex, offsetBy: attributeRange.upperBound)
guard let text = String(source.utf8[startIndex..<endIndex]) else { return nil }
return Attribute(kind: kind, text: text)
}
let accessibility = (dictionary[Key.Accessibility.rawValue] as? String).flatMap { Accessibility(rawValue: $0) }
let type = dictionary[Key.TypeName.rawValue] as? String
switch kind {
case Kinds.ProtocolDeclaration.rawValue:
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let initializers = subtokens.only(Initializer.self)
let children = subtokens.noneOf(Initializer.self)
return ProtocolDeclaration(
name: name,
accessibility: accessibility!,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
initializers: initializers,
children: children,
inheritedTypes: tokenizedInheritedTypes,
attributes: attributes)
case Kinds.ClassDeclaration.rawValue:
guard !attributes.map({ $0.kind }).contains(.final) else {
if debugMode {
fputs("Cuckoo: Ignoring mocking of class \(name) because it's marked `final`.\n", stdout)
}
return nil
}
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let initializers = subtokens.only(Initializer.self)
let children = subtokens.noneOf(Initializer.self).map { child -> Token in
if var property = child as? InstanceVariable {
property.overriding = true
return property
} else {
return child
}
}
return ClassDeclaration(
name: name,
accessibility: accessibility!,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
initializers: initializers,
children: children,
inheritedTypes: tokenizedInheritedTypes,
attributes: attributes)
case Kinds.ExtensionDeclaration.rawValue:
return ExtensionDeclaration(range: range!)
case Kinds.InstanceVariable.rawValue:
let setterAccessibility = (dictionary[Key.SetterAccessibility.rawValue] as? String).flatMap(Accessibility.init)
if String(source.utf8.dropFirst(range!.startIndex))?.takeUntil(occurence: name)?.trimmed.hasPrefix("let") == true {
return nil
}
if type == nil {
stderrPrint("Type of instance variable \(name) could not be inferred. Please specify it explicitly. (\(file.path ?? ""))")
}
return InstanceVariable(
name: name,
type: type ?? "__UnknownType",
accessibility: accessibility!,
setterAccessibility: setterAccessibility,
range: range!,
nameRange: nameRange!,
overriding: false,
attributes: attributes)
case Kinds.InstanceMethod.rawValue:
let parameters = tokenize(methodName: name, parameters: dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
var returnSignature: String
if let bodyRange = bodyRange {
returnSignature = source.utf8[nameRange!.endIndex..<bodyRange.startIndex].takeUntil(occurence: "{")?.trimmed ?? ""
} else {
returnSignature = source.utf8[nameRange!.endIndex..<range!.endIndex].trimmed
if returnSignature.isEmpty {
let untilThrows = String(source.utf8.dropFirst(nameRange!.endIndex))?
.takeUntil(occurence: "throws").map { $0 + "throws" }?
.trimmed
if let untilThrows = untilThrows, untilThrows == "throws" || untilThrows == "rethrows" {
returnSignature = "\(untilThrows)"
}
}
}
if returnSignature.isEmpty == false {
returnSignature = " " + returnSignature
}
// When bodyRange != nil, we need to create .ClassMethod instead of .ProtocolMethod
if let bodyRange = bodyRange {
return ClassMethod(
name: name,
accessibility: accessibility!,
returnSignature: returnSignature,
range: range!,
nameRange: nameRange!,
parameters: parameters,
bodyRange: bodyRange,
attributes: attributes)
} else {
return ProtocolMethod(
name: name,
accessibility: accessibility!,
returnSignature: returnSignature,
range: range!,
nameRange: nameRange!,
parameters: parameters,
attributes: attributes)
}
default:
// Do not log anything, until the parser contains all known cases.
// stderrPrint("Unknown kind. Dictionary: \(dictionary) \(file.path ?? "")")
return nil
}
}
private func tokenize(methodName: String, parameters: [SourceKitRepresentable]) -> [MethodParameter] {
// Takes the string between `(` and `)`
let parameterNames = methodName.components(separatedBy: "(").last?.dropLast(1).map { "\($0)" }.joined(separator: "")
var parameterLabels: [String?] = parameterNames?.components(separatedBy: ":").map { $0 != "_" ? $0 : nil } ?? []
// Last element is not type.
parameterLabels = Array(parameterLabels.dropLast())
// Substructure can contain some other information after the parameters.
let filteredParameters = parameters.filter {
let dictionary = $0 as? [String: SourceKitRepresentable]
let kind = dictionary?[Key.Kind.rawValue] as? String ?? ""
return kind == Kinds.MethodParameter.rawValue
}
return zip(parameterLabels, filteredParameters).compactMap(tokenize)
}
private func tokenize(parameterLabel: String?, parameter: SourceKitRepresentable) -> MethodParameter? {
guard let dictionary = parameter as? [String: SourceKitRepresentable] else { return nil }
let name = dictionary[Key.Name.rawValue] as? String ?? Tokenizer.nameNotSet
let kind = dictionary[Key.Kind.rawValue] as? String ?? Tokenizer.unknownType
let range = extractRange(from: dictionary, offset: .Offset, length: .Length)
let nameRange = extractRange(from: dictionary, offset: .NameOffset, length: .NameLength)
let type = dictionary[Key.TypeName.rawValue] as? String
switch kind {
case Kinds.MethodParameter.rawValue:
return MethodParameter(label: parameterLabel, name: name, type: type!, range: range!, nameRange: nameRange!)
default:
stderrPrint("Unknown method parameter. Dictionary: \(dictionary) \(file.path ?? "")")
return nil
}
}
private func tokenize(imports otherTokens: [Token]) -> [Token] {
let rangesToIgnore: [CountableRange<Int>] = otherTokens.compactMap { token in
switch token {
case let container as ContainerToken:
return container.range
case let extensionToken as ExtensionDeclaration:
return extensionToken.range
default:
return nil
}
}
do {
let regex = try NSRegularExpression(pattern: "(?:\\b|;)import(?:\\s|(?:\\/\\/.*\\n)|(?:\\/\\*.*\\*\\/))+([^\\s;\\/]+)", options: [])
let results = regex.matches(in: source, options: [], range: NSMakeRange(0, source.count))
return results.filter { result in
rangesToIgnore.filter { $0 ~= result.range.location }.isEmpty
}.map {
let libraryRange = $0.range(at: 1)
let fromIndex = source.index(source.startIndex, offsetBy: libraryRange.location)
let toIndex = source.index(fromIndex, offsetBy: libraryRange.length)
let library = String(source[fromIndex..<toIndex])
let range = $0.range.location..<($0.range.location + $0.range.length)
return Import(range: range, library: library)
}
} catch let error as NSError {
fatalError("Invalid regex:" + error.description)
}
}
}

View File

@ -1,3 +1,11 @@
//
// 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"
@ -24,25 +32,3 @@ public enum Accessibility: String {
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
}
}

View File

@ -1,7 +1,13 @@
import Foundation
//
// Attribute.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 2/25/17.
//
//
public struct Attribute: Hashable {
public enum Kind: String, Hashable {
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"
@ -26,26 +32,6 @@ public struct Attribute: Hashable {
return true
}
}
public var unavailablePlatform: String? {
guard kind == .available,
text.hasPrefix("@available(") else {
return nil
}
let parameters = text
.dropFirst("@available(".count)
.dropLast()
.split(separator: ",")
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
guard parameters.count >= 2,
parameters[1] == "unavailable" else {
return nil
}
return String(parameters[0])
}
}
extension Attribute: Token {

View File

@ -1,16 +1,23 @@
public struct ClassDeclaration: ContainerToken, HasAccessibility {
//
// ClassDeclaration.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct ClassDeclaration: ContainerToken {
public let name: String
public let accessibility: Accessibility
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let bodyRange: CountableRange<Int>
public let initializers: [Initializer]
public let children: [Token]
public let implementation: Bool = true
public var name: String
public var parent: Reference<ParentToken>?
public var accessibility: Accessibility
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var bodyRange: CountableRange<Int>
public var initializers: [Initializer]
public var children: [Token]
public var inheritedTypes: [InheritanceDeclaration]
public var attributes: [Attribute]
public var genericParameters: [GenericParameter]
public let inheritedTypes: [InheritanceDeclaration]
public let attributes: [Attribute]
public var hasNoArgInit: Bool {
return initializers.filter { $0.parameters.isEmpty }.isEmpty
}
@ -18,7 +25,6 @@ public struct ClassDeclaration: ContainerToken, HasAccessibility {
public func replace(children tokens: [Token]) -> ClassDeclaration {
return ClassDeclaration(
name: self.name,
parent: self.parent,
accessibility: self.accessibility,
range: self.range,
nameRange: self.nameRange,
@ -26,8 +32,7 @@ public struct ClassDeclaration: ContainerToken, HasAccessibility {
initializers: self.initializers,
children: tokens,
inheritedTypes: self.inheritedTypes,
attributes: self.attributes,
genericParameters: self.genericParameters)
attributes: self.attributes)
}
public func isEqual(to other: Token) -> Bool {

View File

@ -0,0 +1,24 @@
//
// ClassMethod.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct ClassMethod: Method {
public let name: String
public let accessibility: Accessibility
public let returnSignature: String
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let parameters: [MethodParameter]
public let bodyRange: CountableRange<Int>
public let attributes: [Attribute]
public var isOptional: Bool {
return false
}
public var isOverriding: Bool {
return true
}
}

View File

@ -0,0 +1,50 @@
//
// ContainerToken.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public protocol ContainerToken: Token {
var name: String { get }
var accessibility: Accessibility { get }
var range: CountableRange<Int> { get }
var nameRange: CountableRange<Int> { get }
var bodyRange: CountableRange<Int> { get }
var initializers: [Initializer] { get }
var children: [Token] { get }
var implementation: Bool { get }
var inheritedTypes: [InheritanceDeclaration] { get }
var attributes: [Attribute] { get }
}
extension ContainerToken {
public func serialize() -> [String : Any] {
let properties = children.compactMap { $0 as? InstanceVariable }
.filter { $0.accessibility.isAccessible }
.map { $0.serializeWithType() }
let methods = children.compactMap { $0 as? Method }
.filter { $0.accessibility.isAccessible && !$0.isInit && !$0.isDeinit }
.map { $0.serializeWithType() }
let initializers = children.compactMap { $0 as? Method }
.filter { $0.accessibility.isAccessible && $0.isInit && !$0.isDeinit }
.map { $0.serializeWithType() }
return [
"name": name,
"accessibility": accessibility.sourceName,
"isAccessible": accessibility.isAccessible,
"children": children.map { $0.serializeWithType() },
"properties": properties,
"methods": methods,
"initializers": implementation ? [] : initializers,
"isImplementation": implementation,
"mockName": "Mock\(name)",
"inheritedTypes": inheritedTypes,
"attributes": attributes.filter { $0.isSupported },
]
}
}

View File

@ -0,0 +1,17 @@
//
// ExtensionDeclaration.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 17.06.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct ExtensionDeclaration: Token {
// TODO Implement support for extensions
public let range: CountableRange<Int>
public func isEqual(to other: Token) -> Bool {
guard let other = other as? ExtensionDeclaration else { return false }
return self.range == other.range
}
}

View File

@ -0,0 +1,67 @@
//
// FileRepresentation.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
import SourceKittenFramework
public struct FileRepresentation {
public let sourceFile: File
public let declarations: [Token]
public init(sourceFile: File, declarations: [Token]) {
self.sourceFile = sourceFile
self.declarations = declarations
}
}
public extension FileRepresentation {
public func mergeInheritance(with files: [FileRepresentation]) -> FileRepresentation {
let tokens = self.declarations.reduce([Token]()) { list, token in
let mergeToken = token.mergeInheritance(with: files)
return list + [mergeToken]
}
return FileRepresentation(sourceFile: self.sourceFile, declarations: tokens)
}
}
internal extension Token {
internal func mergeInheritance(with files: [FileRepresentation]) -> Token {
guard let typeToken = self as? ContainerToken else {
return self
}
let inheritedRepresentations: [Token] = typeToken.inheritedTypes
.compactMap { Self.findToken(forClassOrProtocol: $0.name, in: files) }
.compactMap { $0.mergeInheritance(with: files) }
// Merge super declarations
let mergedTokens = inheritedRepresentations.filter { $0.isClassOrProtocolDefinition }
.map { $0 as! ContainerToken }
.flatMap { $0.children }
.reduce(typeToken.children) { tokens, inheritedToken in
if (tokens.contains { $0 == inheritedToken }) {
return tokens
}
return tokens + [inheritedToken]
}
switch typeToken {
case let classToken as ClassDeclaration:
return classToken.replace(children: mergedTokens)
case let protocolToken as ProtocolDeclaration:
return protocolToken.replace(children: mergedTokens)
default:
return typeToken
}
}
internal static func findToken(forClassOrProtocol name: String, in files: [FileRepresentation]) -> Token? {
return files.flatMap { $0.declarations }
.filter { $0.isClassOrProtocolDefinition }
.map { $0 as! ContainerToken }
.first { $0.name == name }
}
}

View File

@ -0,0 +1,17 @@
//
// Import.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 17.06.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct Import: Token {
public let range: CountableRange<Int>
public let library: String
public func isEqual(to other: Token) -> Bool {
guard let other = other as? Import else { return false }
return self.range == other.range && self.library == other.library
}
}

View File

@ -1,3 +1,11 @@
//
// InheritanceDeclaration.swift
// CuckooGenerator
//
// Created by Arjan Duijzer on 22/02/2017.
// Copyright (c) 2017 Brightify. All rights reserved.
//
public struct InheritanceDeclaration: Token {
public static let empty = InheritanceDeclaration(name: Tokenizer.nameNotSet)
public let name: String
@ -6,4 +14,4 @@ public struct InheritanceDeclaration: Token {
guard let other = other as? InheritanceDeclaration else { return false }
return self.name == other.name
}
}
}

View File

@ -0,0 +1,23 @@
//
// Initializer.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct Initializer: Method {
public let name: String
public let accessibility: Accessibility
public let returnSignature: String
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let parameters: [MethodParameter]
public let isOverriding: Bool
public let required: Bool
public let attributes: [Attribute]
public var isOptional: Bool {
return false
}
}

View File

@ -0,0 +1,43 @@
//
// InstanceVariable.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct InstanceVariable: Token {
public let name: String
public let type: String
public let accessibility: Accessibility
public let setterAccessibility: Accessibility?
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public var overriding: Bool
public let attributes: [Attribute]
public var readOnly: Bool {
if let setterAccessibility = setterAccessibility {
return !setterAccessibility.isAccessible
} else {
return true
}
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? InstanceVariable else { return false }
return self.name == other.name
}
public func serialize() -> [String : Any] {
let readOnlyString = readOnly ? "ReadOnly" : ""
return [
"name": name,
"type": type,
"accessibility": accessibility.sourceName,
"isReadOnly": readOnly,
"stubType": overriding ? "ClassToBeStubbed\(readOnlyString)Property" : "ProtocolToBeStubbed\(readOnlyString)Property",
"attributes": attributes.filter { $0.isSupported },
]
}
}

View File

@ -1,3 +1,11 @@
//
// Key.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public enum Key: String {
case Substructure = "key.substructure"
case Kind = "key.kind"

View File

@ -1,12 +1,16 @@
//
// Kinds.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public enum Kinds: String {
case ProtocolDeclaration = "source.lang.swift.decl.protocol"
case InstanceMethod = "source.lang.swift.decl.function.method.instance"
case MethodParameter = "source.lang.swift.decl.var.parameter"
case ClassDeclaration = "source.lang.swift.decl.class"
case StructDeclaration = "source.lang.swift.decl.struct"
case ExtensionDeclaration = "source.lang.swift.decl.extension"
case InstanceVariable = "source.lang.swift.decl.var.instance"
case GenericParameter = "source.lang.swift.decl.generic_type_param"
case AssociatedType = "source.lang.swift.decl.associatedtype"
case Optional = "source.decl.attribute.optional"
}

View File

@ -0,0 +1,125 @@
//
// Method.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public protocol Method: Token {
var name: String { get }
var accessibility: Accessibility { get }
var returnSignature: String { get }
var range: CountableRange<Int> { get }
var nameRange: CountableRange<Int> { get }
var parameters: [MethodParameter] { get }
var isOptional: Bool { get }
var isOverriding: Bool { get }
var hasClosureParams: Bool { get }
var attributes: [Attribute] { get }
}
public extension Method {
var rawName: String {
return name.takeUntil(occurence: "(") ?? ""
}
var isInit: Bool {
return rawName == "init"
}
var isDeinit: Bool {
return rawName == "deinit"
}
var fullyQualifiedName: String {
let parameterTypes = parameters.map { $0.type }
let nameParts = name.components(separatedBy: ":")
let lastNamePart = nameParts.last ?? ""
return zip(nameParts.dropLast(), parameterTypes)
.map { $0 + ": " + $1 }
.joined(separator: ", ") + lastNamePart + returnSignature
}
var isThrowing: Bool {
return returnSignature.trimmed.hasPrefix("throws")
}
var returnType: String {
if let range = returnSignature.range(of: "->") {
var type = String(returnSignature[range.upperBound...]).trimmed
while type.hasSuffix("?") {
type = "Optional<\(type[..<type.index(before: type.endIndex)])>"
}
return type
} else {
return "Void"
}
}
var hasClosureParams: Bool {
return parameters.filter { $0.isClosure }.count > 0
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? Method else { return false }
return self.name == other.name && self.parameters == other.parameters
}
public func serialize() -> [String : Any] {
let call = parameters.map {
if let label = $0.label {
return "\(label): \($0.name)"
} else {
return $0.name
}
}.joined(separator: ", ")
let stubFunctionPrefix = isOverriding ? "Class" : "Protocol"
let stubFunction: String
if isThrowing {
if returnType == "Void" {
stubFunction = "Cuckoo.\(stubFunctionPrefix)StubNoReturnThrowingFunction"
} else {
stubFunction = "Cuckoo.\(stubFunctionPrefix)StubThrowingFunction"
}
} else {
if returnType == "Void" {
stubFunction = "Cuckoo.\(stubFunctionPrefix)StubNoReturnFunction"
} else {
stubFunction = "Cuckoo.\(stubFunctionPrefix)StubFunction"
}
}
let escapingParameterNames = parameters.map { parameter in
if parameter.isClosure && !parameter.isEscaping {
return "escapingStub(for: \(parameter.name))"
} else {
return parameter.name
}
}.joined(separator: ", ")
return [
"name": rawName,
"accessibility": accessibility.sourceName,
"returnSignature": returnSignature,
"parameters": parameters,
"parameterNames": parameters.map { $0.name }.joined(separator: ", "),
"escapingParameterNames": escapingParameterNames,
"isInit": isInit,
"returnType": returnType,
"isThrowing": isThrowing,
"fullyQualifiedName": fullyQualifiedName,
"call": call,
"isOverriding": isOverriding,
"parameterSignature": parameters.map { "\($0.labelAndName): \($0.type)" }.joined(separator: ", "),
"parameterSignatureWithoutNames": parameters.map { "\($0.name): \($0.type)" }.joined(separator: ", "),
"stubFunction": stubFunction,
"inputTypes": parameters.map { $0.typeWithoutAttributes }.joined(separator: ", "),
"isOptional": isOptional,
"hasClosureParams": hasClosureParams,
"attributes": attributes.filter { $0.isSupported },
]
}
}

View File

@ -0,0 +1,56 @@
//
// MethodParameter.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct MethodParameter: Token, Equatable {
public let label: String?
public let name: String
public let type: String
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public var labelAndName: String {
if let label = label {
return label != name ? "\(label) \(name)" : name
} else {
return "_ \(name)"
}
}
public var typeWithoutAttributes: String {
return type.replacingOccurrences(of: "@escaping", with: "").replacingOccurrences(of: "@autoclosure", with: "").trimmed
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? MethodParameter else { return false }
return self.name == other.name && self.type == other.type && self.label == other.label
}
public var isClosure: Bool {
return typeWithoutAttributes.hasPrefix("(") && typeWithoutAttributes.range(of: "->") != nil
}
public var isEscaping: Bool {
return isClosure && (type.hasPrefix("@escaping") || type.hasSuffix(")?"))
}
public func serialize() -> [String : Any] {
return [
"label": label ?? "",
"name": name,
"type": type,
"labelAndName": labelAndName,
"typeWithoutAttributes": typeWithoutAttributes,
"isClosure": isClosure,
"isEscaping": isEscaping
]
}
}
public func ==(lhs: MethodParameter, rhs: MethodParameter) -> Bool {
return lhs.isEqual(to: rhs)
}

View File

@ -0,0 +1,38 @@
//
// ProtocolDeclaration.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct ProtocolDeclaration: ContainerToken {
public let name: String
public let accessibility: Accessibility
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let bodyRange: CountableRange<Int>
public let initializers: [Initializer]
public let children: [Token]
public let implementation: Bool = false
public let inheritedTypes: [InheritanceDeclaration]
public let attributes: [Attribute]
public func replace(children tokens: [Token]) -> ProtocolDeclaration {
return ProtocolDeclaration(
name: self.name,
accessibility: self.accessibility,
range: self.range,
nameRange: self.nameRange,
bodyRange: self.bodyRange,
initializers: self.initializers,
children: tokens,
inheritedTypes: self.inheritedTypes,
attributes: self.attributes)
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? ProtocolDeclaration else { return false }
return self.name == other.name
}
}

View File

@ -0,0 +1,24 @@
//
// ProtocolMethod.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct ProtocolMethod: Method {
public let name: String
public let accessibility: Accessibility
public let returnSignature: String
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let parameters: [MethodParameter]
public let attributes: [Attribute]
public var isOptional: Bool {
return attributes.map { $0.kind }.contains(.optional)
}
public var isOverriding: Bool {
return false
}
}

View File

@ -0,0 +1,49 @@
//
// Token.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 30.05.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public protocol Token {
func isEqual(to other: Token) -> Bool
func serialize() -> [String: Any]
}
public func ==(rhs: Token, lhs: Token) -> Bool {
return rhs.isEqual(to: lhs)
}
public extension Token {
func serialize() -> [String: Any] {
return [:]
}
func serializeWithType() -> [String: Any] {
var serialized = serialize()
serialized["@type"] = "\(type(of: self))"
return serialized
}
public var isClassOrProtocolDefinition: Bool {
switch self {
case _ as ProtocolDeclaration:
fallthrough
case _ as ClassDeclaration:
return true
default:
return false
}
}
public var isInheritanceDefinition: Bool {
switch self {
case _ as InheritanceDeclaration:
return true
default:
return false
}
}
}

View File

@ -0,0 +1,59 @@
//
// String+Utility.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
import SourceKittenFramework
extension String {
var trimmed: String {
return trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
func takeUntil(occurence: String) -> String? {
return components(separatedBy: occurence).first
}
subscript(range: Range<Int>) -> String {
let stringRange = index(startIndex, offsetBy: range.lowerBound)..<index(startIndex, offsetBy: range.upperBound)
return String(self[stringRange])
}
}
extension String.UTF8View {
subscript(range: Range<Int>) -> String {
let stringRange = index(startIndex, offsetBy: range.lowerBound)..<index(startIndex, offsetBy: range.upperBound)
let subsequence: String.UTF8View.SubSequence = self[stringRange]
return String(subsequence) ?? ""
}
}
extension Sequence {
#if !swift(>=4.1)
public func compactMap<O>(_ transform: (Element) -> O?) -> [O] {
return self.flatMap(transform)
}
#endif
func only<T>(_ type: T.Type) -> [T] {
return compactMap { $0 as? T }
}
func noneOf<T>(_ type: T.Type) -> [Iterator.Element] {
return filter { !($0 is T) }
}
}
internal func extractRange(from dictionary: [String: SourceKitRepresentable], offset: Key, length: Key) -> CountableRange<Int>? {
guard let
offset = (dictionary[offset.rawValue] as? Int64).map({ Int($0) }),
let length = (dictionary[length.rawValue] as? Int64).map({ Int($0) })
else { return nil }
return offset..<offset.advanced(by: length)
}

View File

@ -1,156 +0,0 @@
import Foundation
import Stencil
public struct Generator {
private let declarations: [Token]
public init(file: FileRepresentation) {
declarations = file.declarations
}
public func generate(debug: Bool = false) throws -> String {
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)
}
ext.registerFilter("escapeReservedKeywords") { (value: Any?) in
guard let name = value as? String else { return value }
return escapeReservedKeywords(for: name)
}
ext.registerFilter("removeClosureArgumentNames") { (value: Any?) in
guard let type = value as? String else { return value }
return self.removeClosureArgumentNames(for: type)
}
let environment = Environment(extensions: [ext])
let containers = declarations.compactMap { $0 as? ContainerToken }
.filter {
$0.parent?.areAllHierarchiesAccessible ?? true && $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 { index, parameter in
let name = escapeReservedKeywords(for: parameter.name)
return "wrap(matchable: \(name)) { $0\(parameters.count > 1 ? ".\(index)" : "") }"
}
.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 awaits = method.isAsync ? "await " : ""
let sugarizedReturnType = method.returnType.sugarized
let returnSignature: String
if sugarizedReturnType.isEmpty {
returnSignature = sugarizedReturnType
} else {
returnSignature = " -> \(sugarizedReturnType)"
}
fullString += "\(indents)return \(tries)\(awaits)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
}
private func removeClosureArgumentNames(for type: String) -> String {
type.replacingOccurrences(
of: "_\\s+?[_a-zA-Z]\\w*?\\s*?:",
with: "",
options: .regularExpression
)
}
}

View File

@ -1,19 +0,0 @@
import Foundation
@dynamicMemberLookup
public final class Reference<Value> {
private(set) var value: Value
public init(value: Value) {
self.value = value
}
public subscript<T>(dynamicMember keyPath: KeyPath<Value, T>) -> T {
value[keyPath: keyPath]
}
public subscript<T>(dynamicMember keyPath: WritableKeyPath<Value, T>) -> T {
get { value[keyPath: keyPath] }
set { value[keyPath: keyPath] = newValue }
}
}

View File

@ -1,153 +0,0 @@
import Foundation
extension Templates {
static let staticGenericParameter = "_CUCKOO$$GENERIC"
static let typeErasureClassName = "DefaultImplCaller"
static let mock = """
{% for container in containers %}
{% if debug %}
// {{ container }}
{% endif %}
{{ container.unavailablePlatformsCheck }}
{% for attribute in container.attributes %}
{{ attribute.text }}
{% endfor %}
{% if container.hasParent %}
extension {{ container.parentFullyQualifiedName }} {
{% endif %}
{{ container.accessibility }} class {{ container.mockName }}{{ container.genericParameters }}: {% if container.isNSObjectProtocol %}NSObject, {% endif %}{{ 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 %}
{{ property.unavailablePlatformsCheck }}
{% 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{% if property.isAsync %} async{% endif %}{% if property.isThrowing %} throws{% endif %} {
return {% if property.isThrowing %}try {% endif %}{% if property.isAsync %}await {% endif %}cuckoo_manager.getter{% if property.isThrowing %}Throws{% endif %}("{{ property.name }}",
superclassCall:
{% if container.isImplementation %}
{% if property.isThrowing %}try {% endif %}{% if property.isAsync %}await {% endif %}super.{{ property.name }}
{% else %}
Cuckoo.MockManager.crashOnProtocolSuperclassCall()
{% endif %},
defaultCall: {% if property.isThrowing %}try {% endif %}{% if property.isAsync %}await {% endif %} __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 %}
}
{% if property.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for initializer in container.initializers %}
{{ initializer.unavailablePlatformsCheck }}
{% 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 %}
}
{% if initializer.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for method in container.methods %}
{{ method.unavailablePlatformsCheck }}
{% 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|escapeReservedKeywords }}{{ method.genericParameters }}({{ method.parameterSignature }}) {{ method.returnSignature }} {
{{ method.self|openNestedClosure }}
return{% if method.isThrowing %} try{% endif %}{% if method.isAsync %} await{% endif %} cuckoo_manager.call{% if method.isThrowing %}{{ method.throwType|capitalize }}{% endif %}(
\"\"\"
{{method.fullyQualifiedName}}
\"\"\",
parameters: ({{method.parameterNames}}),
escapingParameters: ({{method.escapingParameterNames}}),
superclassCall:
{% if container.isImplementation %}
{% if method.isAsync %}await {% endif %}super.{{method.name}}({{method.call}})
{% else %}
Cuckoo.MockManager.crashOnProtocolSuperclassCall()
{% endif %},
defaultCall: {% if method.isAsync %}await {% endif %}__defaultImplStub!.{{method.name}}{%if method.isOptional %}!{%endif%}({{method.call}}))
{{ method.parameters|closeNestedClosure }}
}
{% if method.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
\(Templates.stubbingProxy.indented())
\(Templates.verificationProxy.indented())
}
\(Templates.noImplStub)
{% if container.hasParent %}
}
{% endif %}
{% if container.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
"""
}

View File

@ -1,57 +0,0 @@
extension Templates {
static let noImplStub = """
{% for attribute in container.attributes %}
{{ attribute.text }}
{% endfor %}
{{container.accessibility}} class {{ container.name }}Stub{{ container.genericParameters }}: {% if container.isNSObjectProtocol %}NSObject, {% endif %}{{ container.name }}{% if container.isImplementation %}{{ container.genericArguments }}{% endif %} {
{% for property in container.properties %}
{{ property.unavailablePlatformsCheck }}
{% if debug %}
// {{property}}
{% endif %}
{% 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|removeClosureArgumentNames}}).self)
}
{% ifnot property.isReadOnly %}
set { }
{% endif %}
}
{% if property.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for initializer in container.initializers %}
{{ initializer.unavailablePlatformsCheck }}
{{ 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 %}
}
{% if initializer.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for method in container.methods %}
{{ method.unavailablePlatformsCheck }}
{% if debug %}
// {{method}}
{% endif %}
{% for attribute in method.attributes %}
{{ attribute.text }}
{% endfor %}
{{ method.accessibility }}{% if container.@type == "ClassDeclaration" and method.isOverriding %} override{% endif %} func {{ method.name|escapeReservedKeywords }}{{ method.genericParameters }}({{ method.parameterSignature }}) {{ method.returnSignature }} {{ method.whereClause }} {
return DefaultValueRegistry.defaultValue(for: ({{method.returnType|genericSafe}}).self)
}
{% if method.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
}
"""
}

View File

@ -1,41 +0,0 @@
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 %}
{{ property.unavailablePlatformsCheck }}
{% 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}}")
}
{% if property.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for method in container.methods %}
{{ method.unavailablePlatformsCheck }}
{% for attribute in method.attributes %}
{{ attribute.text }}
{% endfor %}
func {{method.name|escapeReservedKeywords}}{{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))
}
{% if method.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
}
"""
}

View File

@ -1,42 +0,0 @@
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 }}) {% if method.isAsync %} async{% endif %} -> {{ method.returnType }}
{{ container.accessibility }} func {{ method.name|escapeReservedKeywords }}({{ method.parameterSignature }}) {{ method.returnSignature }} {
return {% if method.isAsync %} await{% endif %} _storage${{ forloop.counter }}${{ method.name }}({{ method.parameterNames }})
}
{% endfor %}
}
"""
}

View File

@ -1,48 +0,0 @@
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 %}
{{ property.unavailablePlatformsCheck }}
{% 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)
}
{% if property.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
{% for method in container.methods %}
{{ method.unavailablePlatformsCheck }}
{% for attribute in method.attributes %}
{{ attribute.text }}
{% endfor %}
@discardableResult
func {{method.name|escapeReservedKeywords}}{{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)
}
{% if method.hasUnavailablePlatforms %}
#endif
{% endif %}
{% endfor %}
}
"""
}

View File

@ -1,603 +0,0 @@
import Foundation
import SourceKittenFramework
public struct Tokenizer {
private let file: File
private let source: String
private let debugMode: Bool
public init(sourceFile: File, debugMode: Bool) {
self.file = sourceFile
self.debugMode = debugMode
source = sourceFile.contents
}
public func tokenize() -> FileRepresentation {
do {
let structure = try Structure(file: file)
let declarations = tokenize(structure.dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
.flatMap { declaration -> [Token] in
guard let parent = declaration as? ParentToken else { return [declaration] }
return [parent] + parent.adoptAllYoungerGenerations()
}
let imports = tokenize(imports: declarations)
return FileRepresentation(sourceFile: file, declarations: declarations + imports)
} catch {
print("Error")
}
return FileRepresentation(sourceFile: file, declarations: [])
}
private func tokenize(_ representables: [SourceKitRepresentable]) -> [Token] {
return representables.compactMap(tokenize)
}
internal static let nameNotSet = "name not set"
internal static let unknownType = "unknown type"
private func tokenize(_ representable: SourceKitRepresentable) -> Token? {
guard let dictionary = representable as? [String: SourceKitRepresentable] else { return nil }
// Common fields
let name = dictionary[Key.Name.rawValue] as? String ?? Tokenizer.nameNotSet
let kind = dictionary[Key.Kind.rawValue] as? String ?? Tokenizer.unknownType
// Inheritance
let inheritedTypes = dictionary[Key.InheritedTypes.rawValue] as? [SourceKitRepresentable] ?? []
let tokenizedInheritedTypes = inheritedTypes.compactMap { type -> InheritanceDeclaration in
guard let typeDict = type as? [String: SourceKitRepresentable] else {
return InheritanceDeclaration.empty
}
let name = typeDict[Key.Name.rawValue] as? String ?? Tokenizer.nameNotSet
return InheritanceDeclaration(name: name)
}
// Optional fields
let range = extractRange(from: dictionary, offset: .Offset, length: .Length)
let nameRange = extractRange(from: dictionary, offset: .NameOffset, length: .NameLength)
let bodyRange = extractRange(from: dictionary, offset: .BodyOffset, length: .BodyLength)
// Attributes
let attributes = (NSOrderedSet(array: (dictionary[Key.Attributes.rawValue] as? [SourceKitRepresentable] ?? [])
.compactMap { attribute -> Attribute? in
guard let attribute = attribute as? [String: SourceKitRepresentable],
let stringKind = attribute[Key.Attribute.rawValue] as? String,
let kind = Attribute.Kind(rawValue: stringKind),
let attributeRange = extractRange(from: attribute, offset: .Offset, length: .Length) else { return nil }
let startIndex = source.utf8.index(source.utf8.startIndex, offsetBy: attributeRange.lowerBound)
let endIndex = source.utf8.index(source.utf8.startIndex, offsetBy: attributeRange.upperBound)
guard let text = String(source.utf8[startIndex..<endIndex]) else { return nil }
return Attribute(kind: kind, text: text)
}
).array as? [Attribute]) ?? []
guard !attributes.map({ $0.kind }).contains(.final) else {
if debugMode {
fputs("Cuckoo: Ignoring mocking of '\(name)' because it's marked `final`.\n", stdout)
}
return nil
}
let accessibility = (dictionary[Key.Accessibility.rawValue] as? String).flatMap { Accessibility(rawValue: $0) } ?? .Internal
let type: WrappableType?
if let stringType = dictionary[Key.TypeName.rawValue] as? String {
type = WrappableType(parsing: stringType)
} else {
type = nil
}
switch kind {
case Kinds.ProtocolDeclaration.rawValue:
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let initializers = subtokens.only(Initializer.self)
let children = subtokens.noneOf(Initializer.self)
let genericParameters = subtokens.only(GenericParameter.self)
// FIXME: Remove when SourceKitten fixes the off-by-one error that includes the ending `>` in the last inherited type.
let fixedGenericParameters = fixSourceKittenLastGenericParameterBug(genericParameters)
return ProtocolDeclaration(
name: name,
accessibility: accessibility,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
initializers: initializers,
children: children,
inheritedTypes: tokenizedInheritedTypes,
attributes: attributes,
genericParameters: fixedGenericParameters,
isNSObjectProtocol: false
)
case Kinds.ClassDeclaration.rawValue:
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let initializers = subtokens.only(Initializer.self)
let children = subtokens.noneOf(Initializer.self).map { child -> Token in
if var property = child as? InstanceVariable {
property.overriding = true
return property
} else {
return child
}
}
let genericParameters = subtokens.only(GenericParameter.self)
// FIXME: Remove when SourceKitten fixes the off-by-one error that includes the ending `>` in the last inherited type.
let fixedGenericParameters = fixSourceKittenLastGenericParameterBug(genericParameters)
return ClassDeclaration(
name: name,
accessibility: accessibility,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
initializers: initializers,
children: children,
inheritedTypes: tokenizedInheritedTypes,
attributes: attributes,
genericParameters: fixedGenericParameters
)
case Kinds.StructDeclaration.rawValue:
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let children = subtokens.only(ContainerToken.self)
return StructDeclaration(
name: name,
accessibility: accessibility,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
attributes: attributes,
children: children
)
case Kinds.ExtensionDeclaration.rawValue:
let subtokens = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let children = subtokens.only(ContainerToken.self)
return ExtensionDeclaration(
name: name,
accessibility: accessibility,
range: range!,
nameRange: nameRange!,
bodyRange: bodyRange!,
attributes: attributes,
children: children
)
case Kinds.InstanceVariable.rawValue:
let setterAccessibility = (dictionary[Key.SetterAccessibility.rawValue] as? String).flatMap(Accessibility.init)
if String(source.utf8.dropFirst(range!.startIndex))?.takeUntil(occurence: name)?.trimmed.hasPrefix("let") == true {
return nil
}
let guessedType: WrappableType?
if type == nil {
guessedType = TypeGuesser.guessType(from: String(source.utf8[range!.startIndex..<range!.endIndex].drop(while: { $0 != "=" }).dropFirst()).trimmed).map { .type($0) }
} else {
guessedType = type
}
let effects: InstanceVariable.Effects
if let bodyRange = bodyRange {
effects = parseEffects(source: source.utf8[bodyRange])
} else {
effects = .init()
}
return InstanceVariable(
name: name,
type: guessedType ?? .type("__UnknownType"),
accessibility: accessibility,
setterAccessibility: setterAccessibility,
effects: effects,
range: range!,
nameRange: nameRange!,
overriding: false,
attributes: attributes
)
case Kinds.InstanceMethod.rawValue:
let genericParameters = tokenize(dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? []).only(GenericParameter.self)
let parameters = tokenize(methodName: name, parameters: dictionary[Key.Substructure.rawValue] as? [SourceKitRepresentable] ?? [])
let returnSignature: ReturnSignature
if let bodyRange = bodyRange {
returnSignature = parseReturnSignature(source: source.utf8[nameRange!.endIndex..<bodyRange.startIndex].takeUntil(occurence: "{")?.trimmed ?? "")
} else {
returnSignature = parseReturnSignature(source: source.utf8[nameRange!.endIndex..<range!.endIndex].trimmed)
}
// methods can specify an empty public name of a parameter without any private name - `fun(_: String)`
let namedParameters = parameters.enumerated().map { index, parameter -> MethodParameter in
if parameter.name == Tokenizer.nameNotSet {
var mutableParameter = parameter
mutableParameter.name = "parameter\(index)"
return mutableParameter
} else {
return parameter
}
}
// FIXME: Remove when SourceKit fixes the off-by-one error that includes the ending `>` in the last inherited type.
let fixedGenericParameters = fixSourceKittenLastGenericParameterBug(genericParameters)
// When bodyRange != nil, we need to create `ClassMethod` instead of `ProtocolMethod`
if let bodyRange = bodyRange {
return ClassMethod(
name: name,
accessibility: accessibility,
returnSignature: returnSignature,
range: range!,
nameRange: nameRange!,
parameters: namedParameters,
bodyRange: bodyRange,
attributes: attributes,
genericParameters: fixedGenericParameters
)
} else {
return ProtocolMethod(
name: name,
accessibility: accessibility,
returnSignature: returnSignature,
range: range!,
nameRange: nameRange!,
parameters: namedParameters,
attributes: attributes,
genericParameters: fixedGenericParameters
)
}
case Kinds.GenericParameter.rawValue:
return tokenize(parameterLabel: nil, parameter: representable)
case Kinds.AssociatedType.rawValue:
let regex = try! NSRegularExpression(pattern: "\\s*:\\s*([^\\s;\\/]+)")
guard let range = range else { return nil }
guard let inheritanceMatch = regex.firstMatch(
in: source,
range: NSMakeRange(range.startIndex, range.endIndex - range.startIndex)) else {
return GenericParameter(name: name, range: range, inheritedType: nil)
}
let inheritanceRange = inheritanceMatch.range(at: 1)
let fromIndex = source.index(source.startIndex, offsetBy: inheritanceRange.location)
let toIndex = source.index(fromIndex, offsetBy: inheritanceRange.length)
let inheritance = String(source[fromIndex..<toIndex])
let fullRange = range.lowerBound..<(range.upperBound + inheritanceMatch.range.length)
return GenericParameter(
name: name,
range: fullRange,
inheritedType: InheritanceDeclaration(name: inheritance)
)
default:
// Do not log anything, until the parser contains all known cases.
// stderrPrint("Unknown kind. Dictionary: \(dictionary) \(file.path ?? "")")
return nil
}
}
private func tokenize(methodName: String, parameters: [SourceKitRepresentable]) -> [MethodParameter] {
// Takes the string between `(` and `)`
let parameterNames = methodName.components(separatedBy: "(").last?.dropLast(1).map { "\($0)" }.joined(separator: "")
var parameterLabels: [String?] = parameterNames?.components(separatedBy: ":").map { $0 != "_" ? $0 : nil } ?? []
// Last element is not type.
parameterLabels = Array(parameterLabels.dropLast())
// Substructure can contain some other information after the parameters.
let filteredParameters = parameters.filter {
let dictionary = $0 as? [String: SourceKitRepresentable]
let kind = dictionary?[Key.Kind.rawValue] as? String ?? ""
return kind == Kinds.MethodParameter.rawValue
}
return zip(parameterLabels, filteredParameters).compactMap { tokenize(parameterLabel: $0, parameter: $1) as? MethodParameter }
}
private func tokenize(parameterLabel: String?, parameter: SourceKitRepresentable) -> Token? {
guard let dictionary = parameter as? [String: SourceKitRepresentable] else { return nil }
let name = dictionary[Key.Name.rawValue] as? String ?? Tokenizer.nameNotSet
let kind = dictionary[Key.Kind.rawValue] as? String ?? Tokenizer.unknownType
let range = extractRange(from: dictionary, offset: .Offset, length: .Length)
let nameRange = extractRange(from: dictionary, offset: .NameOffset, length: .NameLength)
let type = dictionary[Key.TypeName.rawValue] as? String
switch kind {
case Kinds.MethodParameter.rawValue:
// separate `inout` from the type and remember that the parameter is inout
let type = type!
// we want to remove `inout` and remember it, but we don't want to affect a potential `inout` closure parameter
let inoutSeparatedType: String
let isInout: Bool
if let inoutRange = type.range(of: "inout ") {
if let closureParenIndex = type.firstIndex(of: "("), closureParenIndex < inoutRange.upperBound {
inoutSeparatedType = type
isInout = false
} else {
var mutableString = type
mutableString.removeSubrange(inoutRange)
inoutSeparatedType = mutableString
isInout = true
}
} else {
inoutSeparatedType = type
isInout = false
}
let wrappableType = WrappableType(parsing: inoutSeparatedType)
return MethodParameter(label: parameterLabel, name: name, type: wrappableType, range: range!, nameRange: nameRange, isInout: isInout)
case Kinds.GenericParameter.rawValue:
let inheritedTypeElement = (dictionary[Key.InheritedTypes.rawValue] as? [SourceKitRepresentable] ?? []).first
let inheritedType = (inheritedTypeElement as? [String: SourceKitRepresentable] ?? [:])[Key.Name.rawValue] as? String
let inheritanceDeclaration: InheritanceDeclaration?
if let inheritedType = inheritedType {
inheritanceDeclaration = .init(name: inheritedType)
} else {
inheritanceDeclaration = nil
}
return GenericParameter(name: name, range: range!, inheritedType: inheritanceDeclaration)
default:
stderrPrint("Unknown method parameter. Dictionary: \(dictionary) \(file.path ?? "")")
return nil
}
}
private func tokenize(imports otherTokens: [Token]) -> [Token] {
let rangesToIgnore: [CountableRange<Int>] = otherTokens.compactMap { token in
switch token {
case let container as ContainerToken:
return container.range
case let extensionToken as ExtensionDeclaration:
return extensionToken.range
default:
return nil
}
}
do {
let baseRegex = "(?:\\b|;)import(?:\\s|(?:\\/\\/.*\\n)|(?:\\/\\*.*\\*\\/))+"
let identifierRegex = "[^\\s;\\/]+"
let libraryImportRegex = baseRegex + "(\(identifierRegex))(?:\\n|(?:\\s)*;)"
let componentImportRegex = baseRegex + "(\(identifierRegex))\\s+(\(identifierRegex))\\.(\(identifierRegex))"
let libraryRegex = try NSRegularExpression(pattern: libraryImportRegex)
let componentRegex = try NSRegularExpression(pattern: componentImportRegex)
let librariesRange = NSRange(location: 0, length: source.count)
let libraries = libraryRegex.matches(in: source, range: librariesRange)
.filter { result in
rangesToIgnore.filter { $0 ~= result.range.location }.isEmpty
}
.map { result -> Import in
let range = result.range.location..<(result.range.location + result.range.length)
let library = source.stringMatch(from: result, at: 1)
return Import(range: range, importee: .library(name: library))
}
let components = componentRegex.matches(in: source, range: NSRange(location: 0, length: source.count))
.filter { result in
rangesToIgnore.filter { $0 ~= result.range.location }.isEmpty
}
.map { result -> Import in
let componentRange = result.range(at: 1)
let componentType = componentRange.location == NSNotFound ? nil : source[componentRange]
let library = source[result.range(at: 2)]
let component = source[result.range(at: 3)]
let range = result.range.location..<(result.range.location + result.range.length)
return Import(range: range, importee: .component(componentType: componentType, library: library, name: component))
}
return libraries + components
} catch let error as NSError {
fatalError("Invalid regex:" + error.description)
}
}
private func getReturnType(source: String, index: inout String.Index) -> String {
var returnType = ""
var afterArrow = true
var parenLevel = 0
while index != source.endIndex {
let character = source[index]
switch character {
case "(", "<", "[":
parenLevel += 1
returnType.append(character)
index = source.index(after: index)
case ")", ">", "]":
parenLevel -= 1
returnType.append(character)
index = source.index(after: index)
case "-":
index = source.index(after: index)
// just a little sanity check
guard source[index] == ">" else { fatalError("Uhh, what.") }
index = source.index(after: index)
returnType.append(" -> ")
afterArrow = true
case " ":
index = source.index(after: index)
returnType.append(character)
case "w":
let previousCharacter = source[source.index(before: index)]
guard parenLevel == 0 && !afterArrow && previousCharacter == " " else {
returnType.append(character)
index = source.index(after: index)
continue
}
// we reached the "where" clause
return returnType
default:
afterArrow = false
returnType.append(character)
index = source.index(after: index)
}
}
return returnType
}
/// - returns: the where constraints parsed from the where clause
private func parseWhereClause(source: String, index: inout String.Index) -> [String] {
var whereConstraints = [] as [String]
var currentConstraint = ""
var parenLevel = 0
while index != source.endIndex {
let character = source[index]
switch character {
case "(", "<", "[":
parenLevel += 1
currentConstraint.append(character)
case ")", ">", "]":
parenLevel -= 1
currentConstraint.append(character)
case "," where parenLevel == 0:
currentConstraint = currentConstraint.trimmed
whereConstraints.append(currentConstraint)
currentConstraint = ""
default:
currentConstraint.append(character)
}
index = source.index(after: index)
}
if !currentConstraint.isEmpty {
currentConstraint = currentConstraint.trimmed
whereConstraints.append(currentConstraint)
}
return whereConstraints
}
/// - parameter source: A trimmed string containing only the method return signature excluding the trailing brace
/// - returns: ReturnSignature structure containing the parsed throwString, return type, and where constraints
private func parseReturnSignature(source: String) -> ReturnSignature {
var isAsync = false
var throwString = nil as String?
var returnType: WrappableType?
var whereConstraints = [] as [String]
var index = source.startIndex
parseLoop: while index != source.endIndex {
let character = source[index]
switch character {
case "a":
isAsync = true
let asyncString = "async"
index = source.index(index, offsetBy: asyncString.count)
continue
case "r" where returnType == nil:
throwString = "rethrows"
index = source.index(index, offsetBy: throwString!.count)
continue
case "t" where returnType == nil:
throwString = "throws"
index = source.index(index, offsetBy: throwString!.count)
continue
case "w":
index = source.index(index, offsetBy: "where".count)
whereConstraints = parseWhereClause(source: source, index: &index)
// the where clause is the last thing in method signature, so we'll just stop the parsing
break parseLoop
case "-":
index = source.index(after: index)
guard source[index] == ">" else { fatalError("Uhh, what.") }
index = source.index(after: index)
returnType = WrappableType(parsing: getReturnType(source: source, index: &index).trimmed)
continue
default:
break
}
index = source.index(after: index)
}
return ReturnSignature(isAsync: isAsync, throwString: throwString, returnType: returnType ?? WrappableType.type("Void"), whereConstraints: whereConstraints)
}
private func parseEffects(source: String) -> InstanceVariable.Effects {
var effects = InstanceVariable.Effects()
let trimmed = source.drop(while: { $0.isWhitespace })
guard trimmed.hasPrefix("get") else { return effects }
let afterGet = trimmed.dropFirst("get".count).drop(while: { $0.isWhitespace })
var index = afterGet.startIndex
parseLoop: while index != afterGet.endIndex {
let character = afterGet[index]
switch character {
case "a":
effects.isAsync = true
index = source.index(index, offsetBy: "async".count)
case "t":
effects.isThrowing = true
index = source.index(index, offsetBy: "throws".count)
case let c where c.isWhitespace:
break
default:
break parseLoop
}
index = source.index(after: index)
}
return effects
}
// FIXME: Remove when SourceKitten fixes the off-by-one error that includes the ending `>` in the last inherited type.
private func fixSourceKittenLastGenericParameterBug(_ genericParameters: [GenericParameter]) -> [GenericParameter] {
let fixedGenericParameters: [GenericParameter]
if let lastGenericParameter = genericParameters.last,
let inheritedType = lastGenericParameter.inheritedType,
inheritedType.name.hasSuffix(">>") == true {
fixedGenericParameters = genericParameters.dropLast() + [
GenericParameter(
name: lastGenericParameter.name,
range: lastGenericParameter.range.lowerBound..<lastGenericParameter.range.upperBound - 1,
inheritedType: InheritanceDeclaration(name: String(inheritedType.name.dropLast()))
)
]
} else {
fixedGenericParameters = genericParameters
}
return fixedGenericParameters
}
}
extension String {
subscript(range: NSRange) -> String {
let fromIndex = self.index(self.startIndex, offsetBy: range.location)
let toIndex = self.index(fromIndex, offsetBy: range.length)
return String(self[fromIndex..<toIndex])
}
}
extension String {
func stringMatch(from match: NSTextCheckingResult, at range: Int = 0) -> String {
let matchRange = match.range(at: range)
let fromIndex = index(startIndex, offsetBy: matchRange.location)
let toIndex = index(fromIndex, offsetBy: matchRange.length)
return String(self[fromIndex..<toIndex])
}
func removing(match: NSTextCheckingResult, at range: Int = 0) -> String {
let matchRange = match.range(at: range)
let fromIndex = index(startIndex, offsetBy: matchRange.location)
let toIndex = index(fromIndex, offsetBy: matchRange.length)
var mutableString = self
mutableString.removeSubrange(fromIndex..<toIndex)
return mutableString
}
}

View File

@ -1,5 +0,0 @@
import Foundation
public protocol ChildToken: Token {
var parent: Reference<ParentToken>? { get set }
}

View File

@ -1,17 +0,0 @@
public struct ClassMethod: Method {
public var name: String
public var accessibility: Accessibility
public var returnSignature: ReturnSignature
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var parameters: [MethodParameter]
public var bodyRange: CountableRange<Int>
public var attributes: [Attribute]
public var genericParameters: [GenericParameter]
public var isOptional: Bool {
return false
}
public var isOverriding: Bool {
return true
}
}

View File

@ -1,63 +0,0 @@
public protocol ContainerToken: ParentToken, ChildToken {
var initializers: [Initializer] { get }
var implementation: Bool { get }
var inheritedTypes: [InheritanceDeclaration] { get }
var genericParameters: [GenericParameter] { get }
}
extension ContainerToken {
public func serialize() -> [String : Any] {
func withAdjustedAccessibility(token: Token & HasAccessibility) -> Token & HasAccessibility {
// We only want to adjust tokens that are accessible and lower than the enclosing type
guard token.accessibility.isAccessible && token.accessibility < accessibility else { return token }
var mutableToken = token
mutableToken.accessibility = accessibility
return mutableToken
}
let accessibilityAdjustedChildren = children.map { child -> Token in
guard let childWithAccessibility = child as? HasAccessibility & Token else { return child }
return withAdjustedAccessibility(token: childWithAccessibility)
}
let properties = accessibilityAdjustedChildren.compactMap { $0 as? InstanceVariable }
.filter { $0.accessibility.isAccessible }
.map { $0.serializeWithType() }
let methods = accessibilityAdjustedChildren.compactMap { $0 as? Method }
.filter { $0.accessibility.isAccessible && !$0.isInit && !$0.isDeinit }
.map { $0.serializeWithType() }
let initializers = accessibilityAdjustedChildren.compactMap { $0 as? Method }
.filter { $0.accessibility.isAccessible && $0.isInit && !$0.isDeinit }
.map { $0.serializeWithType() }
let genericParametersString = genericParameters.map { $0.description }.joined(separator: ", ")
let genericArgumentsString = genericParameters.map { $0.name }.joined(separator: ", ")
let genericProtocolIdentity = genericParameters.map { "\(Templates.staticGenericParameter).\($0.name) == \($0.name)" }.joined(separator: ", ")
let isGeneric = !genericParameters.isEmpty
return [
"name": name,
"accessibility": accessibility.sourceName,
"isAccessible": accessibility.isAccessible,
"hasParent": parent != nil,
"parentFullyQualifiedName": parent?.fullyQualifiedName ?? "",
"children": accessibilityAdjustedChildren.map { $0.serializeWithType() },
"properties": properties,
"methods": methods,
"initializers": implementation ? [] : initializers,
"isImplementation": implementation,
"mockName": "Mock\(name)",
"inheritedTypes": inheritedTypes,
"attributes": attributes.filter { $0.isSupported },
"hasUnavailablePlatforms": hasUnavailablePlatforms,
"unavailablePlatformsCheck": unavailablePlatformsCheck,
"isGeneric": isGeneric,
"genericParameters": isGeneric ? "<\(genericParametersString)>" : "",
"genericArguments": isGeneric ? "<\(genericArgumentsString)>" : "",
"genericProtocolIdentity": genericProtocolIdentity,
"isNSObjectProtocol": (self as? ProtocolDeclaration)?.isNSObjectProtocol ?? false
]
}
}

View File

@ -1,15 +0,0 @@
public struct ExtensionDeclaration: ParentToken {
// TODO Implement support for extensions
public let name: String
public var accessibility: Accessibility
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let bodyRange: CountableRange<Int>
public var attributes: [Attribute]
public let children: [Token]
public func isEqual(to other: Token) -> Bool {
guard let other = other as? ExtensionDeclaration else { return false }
return self.range == other.range
}
}

View File

@ -1,71 +0,0 @@
import SourceKittenFramework
public struct FileRepresentation {
public let sourceFile: File
public let declarations: [Token]
public init(sourceFile: File, declarations: [Token]) {
self.sourceFile = sourceFile
self.declarations = declarations
}
}
public extension FileRepresentation {
func mergeInheritance(with files: [FileRepresentation]) -> FileRepresentation {
let tokens = self.declarations.reduce([Token]()) { list, token in
let mergeToken = token.mergeInheritance(with: files)
return list + [mergeToken]
}
return FileRepresentation(sourceFile: self.sourceFile, declarations: tokens)
}
func inheritNSObject(subjects: [ProtocolDeclaration]) -> FileRepresentation {
FileRepresentation(sourceFile: self.sourceFile, declarations: self.declarations.map { $0.inheritNSObject(subjects: subjects) })
}
}
extension Token {
func mergeInheritance(with files: [FileRepresentation]) -> Token {
guard let typeToken = self as? ContainerToken else {
return self
}
let inheritedRepresentations: [Token] = typeToken.inheritedTypes
.compactMap { Self.findToken(forClassOrProtocol: $0.name, in: files) }
.compactMap { $0.mergeInheritance(with: files) }
// Merge super declarations
let mergedTokens = inheritedRepresentations.filter { $0.isClassOrProtocolDeclaration }
.map { $0 as! ContainerToken }
.flatMap { $0.children }
.reduce(typeToken.children) { tokens, inheritedToken in
if tokens.contains(where: { $0 == inheritedToken }) {
return tokens
}
return tokens + [inheritedToken]
}
switch typeToken {
case let classToken as ClassDeclaration:
return classToken.replace(children: mergedTokens)
case let protocolToken as ProtocolDeclaration:
return protocolToken.replace(children: mergedTokens)
default:
return typeToken
}
}
func inheritNSObject(subjects: [ProtocolDeclaration]) -> Token {
guard let protocolToken = self as? ProtocolDeclaration else {
return self
}
return subjects.contains { $0.name == protocolToken.name } ? protocolToken.replace(isNSObjectProtocol: true) : self
}
static func findToken(forClassOrProtocol name: String, in files: [FileRepresentation]) -> Token? {
return files.flatMap { $0.declarations }
.filter { $0.isClassOrProtocolDeclaration }
.map { $0 as! ContainerToken }
.first { $0.name == name }
}
}

View File

@ -1,24 +0,0 @@
import Foundation
public struct GenericParameter: Token {
public let name: String
public let range: CountableRange<Int>
public let inheritedType: InheritanceDeclaration?
public var description: String {
let hasInheritedType = inheritedType != nil
return "\(name)\(hasInheritedType ? ": " : "")\(inheritedType?.name ?? "")"
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? GenericParameter else { return false }
return self.name == other.name && self.range == other.range && self.inheritedType?.name == other.inheritedType?.name
}
public func serialize() -> [String : Any] {
return [
"name": name,
"inheritedType": inheritedType,
]
}
}

View File

@ -1,5 +0,0 @@
import Foundation
public protocol HasAccessibility {
var accessibility: Accessibility { get set }
}

View File

@ -1,19 +0,0 @@
import Foundation
public protocol HasAttributes {
var attributes: [Attribute] { get }
}
extension HasAttributes {
private var unavailablePlatforms: [String] {
return attributes.lazy.compactMap { $0.unavailablePlatform }
}
var hasUnavailablePlatforms: Bool {
return !unavailablePlatforms.isEmpty
}
var unavailablePlatformsCheck: String {
return hasUnavailablePlatforms ? "#if !os(\(unavailablePlatforms.joined(separator: ") && !os(")))" : ""
}
}

View File

@ -1,30 +0,0 @@
public struct Import: Token {
public enum Importee: CustomStringConvertible {
case library(name: String)
case component(componentType: String?, library: String, name: String)
public var description: String {
switch self {
case .library(let name):
return name
case .component(let componentType, let library, let name):
return [componentType, "\(library).\(name)"].compactMap { $0 }.joined(separator: " ")
}
}
}
public let range: CountableRange<Int>
public let importee: Importee
public func isEqual(to other: Token) -> Bool {
guard let other = other as? Import, self.range == other.range else { return false }
switch (self.importee, other.importee) {
case (.library(let lhsName), .library(let rhsName)):
return lhsName == rhsName
case (.component(let lhsImportType, let lhsLibrary, let lhsName), .component(let rhsImportType, let rhsLibrary, let rhsName)):
return lhsImportType == rhsImportType && lhsLibrary == rhsLibrary && lhsName == rhsName
default:
return false
}
}
}

View File

@ -1,17 +0,0 @@
public struct Initializer: Method, HasAccessibility {
public var name: String
public var accessibility: Accessibility
public var returnType: WrappableType
public var returnSignature: ReturnSignature
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var parameters: [MethodParameter]
public var isOverriding: Bool
public var required: Bool
public var attributes: [Attribute]
public var genericParameters: [GenericParameter]
public var isOptional: Bool {
return false
}
}

View File

@ -1,51 +0,0 @@
public struct InstanceVariable: Token, HasAccessibility, HasAttributes {
public struct Effects {
public var isThrowing = false
public var isAsync = false
}
public var name: String
public var type: WrappableType
public var accessibility: Accessibility
public var setterAccessibility: Accessibility?
public var effects: Effects
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var overriding: Bool
public var attributes: [Attribute]
public var readOnly: Bool {
if let setterAccessibility = setterAccessibility {
return !setterAccessibility.isAccessible
} else {
return true
}
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? InstanceVariable else { return false }
return self.name == other.name
}
public func serialize() -> [String : Any] {
let readOnlyVerifyString = readOnly ? "ReadOnly" : ""
let readOnlyStubString = effects.isThrowing ? "" : readOnlyVerifyString
let optionalString = type.isOptional && !readOnly ? "Optional" : ""
let throwingString = effects.isThrowing ? "Throwing" : ""
return [
"name": name,
"type": type.sugarized,
"nonOptionalType": type.unoptionaled.sugarized,
"accessibility": accessibility.sourceName,
"isReadOnly": readOnly,
"isAsync": effects.isAsync,
"isThrowing": effects.isThrowing,
"stubType": (overriding ? "Class" : "Protocol") + "ToBeStubbed\(readOnlyStubString)\(optionalString)\(throwingString)Property",
"verifyType": "Verify\(readOnlyVerifyString)\(optionalString)Property",
"attributes": attributes.filter { $0.isSupported },
"hasUnavailablePlatforms": hasUnavailablePlatforms,
"unavailablePlatformsCheck": unavailablePlatformsCheck,
]
}
}

View File

@ -1,158 +0,0 @@
import Foundation
public protocol Method: Token, HasAccessibility, HasAttributes {
var name: String { get }
var returnSignature: ReturnSignature { get }
var range: CountableRange<Int> { get }
var nameRange: CountableRange<Int> { get }
var parameters: [MethodParameter] { get }
var isOptional: Bool { get }
var isOverriding: Bool { get }
var hasClosureParams: Bool { get }
var hasOptionalParams: Bool { get }
var genericParameters: [GenericParameter] { get }
}
public extension Method {
var rawName: String {
return name.takeUntil(occurence: "(") ?? ""
}
var isInit: Bool {
return rawName == "init"
}
var isDeinit: Bool {
return rawName == "deinit"
}
var fullyQualifiedName: String {
let parameterTypes = parameters.map { ($0.isInout ? "inout " : "") + $0.type.sugarized }
let nameParts = name.components(separatedBy: ":")
let lastNamePart = nameParts.last ?? ""
let returnSignatureDescription = returnSignature.description
let returnSignatureString = returnSignatureDescription.isEmpty ? "" : " \(returnSignatureDescription)"
return zip(nameParts.dropLast(), parameterTypes)
.map { $0 + ": " + $1 }
.joined(separator: ", ") + lastNamePart + returnSignatureString
}
var isAsync: Bool {
return returnSignature.isAsync
}
var isThrowing: Bool {
guard let throwType = returnSignature.throwType else { return false }
return throwType.isThrowing || throwType.isRethrowing
}
var returnType: WrappableType {
return returnSignature.returnType
}
var hasClosureParams: Bool {
return parameters.contains { $0.isClosure }
}
var hasOptionalParams: Bool {
return parameters.contains { $0.isOptional }
}
func isEqual(to other: Token) -> Bool {
guard let other = other as? Method else { return false }
return self.name == other.name && self.parameters == other.parameters && self.returnType == other.returnType
}
func serialize() -> [String : Any] {
let call = parameters.map {
let name = escapeReservedKeywords(for: $0.name)
let referencedName = "\($0.isInout ? "&" : "")\(name)\($0.isAutoClosure ? "()" : "")"
if let label = $0.label {
return "\(label): \(referencedName)"
} else {
return referencedName
}
}.joined(separator: ", ")
let stubFunctionPrefix = isOverriding ? "Class" : "Protocol"
let returnString = returnType.sugarized == "Void" ? "NoReturn" : ""
let throwingString = isThrowing ? "Throwing" : ""
let stubFunction = "Cuckoo.\(stubFunctionPrefix)Stub\(returnString)\(throwingString)Function"
let escapingParameterNames = parameters.map { parameter in
if parameter.isClosure && !parameter.isEscaping {
let parameterCount = parameter.closureParamCount
let parameterSignature = parameterCount > 0 ? (1...parameterCount).map { _ in "_" }.joined(separator: ", ") : "()"
// FIXME: Instead of parsing the closure return type here, Tokenizer should do it and pass the information in a data structure
let returnSignature: String
let closureReturnType = extractClosureReturnType(parameter: parameter.type.sugarized)
if let closureReturnType = closureReturnType, !closureReturnType.isEmpty && closureReturnType != "Void" {
returnSignature = " -> " + closureReturnType
} else {
returnSignature = ""
}
return "{ \(parameterSignature)\(returnSignature) in fatalError(\"This is a stub! It's not supposed to be called!\") }"
} else {
return escapeReservedKeywords(for: parameter.name)
}
}.joined(separator: ", ")
let genericParametersString = genericParameters.map { $0.description }.joined(separator: ", ")
let isGeneric = !genericParameters.isEmpty
return [
"self": self,
"name": rawName,
"accessibility": accessibility.sourceName,
"returnSignature": returnSignature.description,
"parameters": parameters,
"parameterNames": parameters.map { escapeReservedKeywords(for: $0.name) }.joined(separator: ", "),
"escapingParameterNames": escapingParameterNames,
"isInit": isInit,
"returnType": returnType.explicitOptionalOnly.sugarized,
"isAsync": isAsync,
"isThrowing": isThrowing,
"throwType": returnSignature.throwType?.description ?? "",
"fullyQualifiedName": fullyQualifiedName,
"call": call,
"isOverriding": isOverriding,
"parameterSignature": parameters.map { "\($0.labelAndName): \($0.isInout ? "inout " : "")\($0.type)" }.joined(separator: ", "),
"parameterSignatureWithoutNames": parameters.map { "\($0.name): \($0.type)" }.joined(separator: ", "),
"argumentSignature": parameters.map { $0.type.description }.joined(separator: ", "),
"stubFunction": stubFunction,
"inputTypes": parameters.map { $0.typeWithoutAttributes }.joined(separator: ", "),
"isOptional": isOptional,
"hasClosureParams": hasClosureParams,
"hasOptionalParams": hasOptionalParams,
"attributes": attributes.filter { $0.isSupported },
"hasUnavailablePlatforms": hasUnavailablePlatforms,
"unavailablePlatformsCheck": unavailablePlatformsCheck,
"genericParameters": isGeneric ? "<\(genericParametersString)>" : "",
]
}
private func extractClosureReturnType(parameter: String) -> String? {
var parenLevel = 0
for i in 0..<parameter.count {
let index = parameter.index(parameter.startIndex, offsetBy: i)
let character = parameter[index]
if character == "(" {
parenLevel += 1
} else if character == ")" {
parenLevel -= 1
if parenLevel == 0 {
let returnSignature = String(parameter[parameter.index(after: index)..<parameter.endIndex])
let regex = try! NSRegularExpression(pattern: "\\s*->\\s*(.*)\\s*")
guard let result = regex.matches(in: returnSignature, range: NSRange(location: 0, length: returnSignature.count)).first else { return nil }
return returnSignature[result.range(at: 1)]
}
}
}
return nil
}
}

View File

@ -1,83 +0,0 @@
public struct MethodParameter: Token, Equatable {
public var label: String?
public var name: String
public var type: WrappableType
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>?
public var isInout: Bool
public var labelAndName: String {
if let label = label {
return label != name ? "\(label) \(name)" : name
} else {
return "_ \(name)"
}
}
public var typeWithoutAttributes: String {
return type.withoutAttributes.sugarized.trimmed
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? MethodParameter else { return false }
return label == other.label && type == other.type
}
public var isClosure: Bool {
return typeWithoutAttributes.hasPrefix("(") && typeWithoutAttributes.range(of: "->") != nil
}
public var isAutoClosure: Bool {
type.containsAttribute(named: "@autoclosure")
}
public var isOptional: Bool {
return type.isOptional
}
public var closureParamCount: Int {
// make sure that the parameter is a closure and that it's not just an empty `() -> ...` closure
guard isClosure && !"^\\s*\\(\\s*\\)".regexMatches(typeWithoutAttributes) else { return 0 }
var parenLevel = 0
var parameterCount = 1
for character in typeWithoutAttributes {
switch character {
case "(", "<":
parenLevel += 1
case ")", ">":
parenLevel -= 1
case ",":
parameterCount += parenLevel == 1 ? 1 : 0
default:
break
}
if parenLevel == 0 {
break
}
}
return parameterCount
}
public var isEscaping: Bool {
return isClosure && (type.containsAttribute(named: "@escaping") || type.isOptional)
}
public func serialize() -> [String : Any] {
return [
"label": label ?? "",
"name": name,
"type": type,
"labelAndName": labelAndName,
"typeWithoutAttributes": typeWithoutAttributes,
"isClosure": isClosure,
"isOptional": isOptional,
"isEscaping": isEscaping
]
}
}
public func ==(lhs: MethodParameter, rhs: MethodParameter) -> Bool {
return lhs.isEqual(to: rhs)
}

View File

@ -1,43 +0,0 @@
import Foundation
public protocol ParentToken: Token, HasAccessibility, HasAttributes {
var name: String { get }
var range: CountableRange<Int> { get }
var nameRange: CountableRange<Int> { get }
var bodyRange: CountableRange<Int> { get }
var children: [Token] { get }
}
extension ParentToken {
var fullyQualifiedName: String {
var names = [name]
var parent: ParentToken? = (self as? ChildToken)?.parent?.value
while let p = parent {
names.insert(p.name, at: 0)
parent = (p as? ChildToken)?.parent?.value
}
return names.joined(separator: ".")
}
var areAllHierarchiesAccessible: Bool {
guard accessibility.isAccessible else { return false }
var parent: ParentToken? = (self as? ChildToken)?.parent?.value
while let p = parent {
guard p.accessibility.isAccessible else { return false }
parent = (p as? ChildToken)?.parent?.value
}
return true
}
func adoptAllYoungerGenerations() -> [ParentToken] {
let parentReference: Reference<ParentToken> = Reference(value: self)
return children
.compactMap { child -> ParentToken? in
guard var c = child as? ContainerToken else { return nil }
c.parent = parentReference
return c
}
.flatMap { [$0] + $0.adoptAllYoungerGenerations() }
}
}

View File

@ -1,54 +0,0 @@
public struct ProtocolDeclaration: ContainerToken, HasAccessibility {
public let implementation: Bool = false
public var name: String
public var parent: Reference<ParentToken>?
public var accessibility: Accessibility
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var bodyRange: CountableRange<Int>
public var initializers: [Initializer]
public var children: [Token]
public var inheritedTypes: [InheritanceDeclaration]
public var attributes: [Attribute]
public var genericParameters: [GenericParameter]
public var isNSObjectProtocol: Bool
public func replace(children tokens: [Token]) -> ProtocolDeclaration {
return ProtocolDeclaration(
name: self.name,
parent: self.parent,
accessibility: self.accessibility,
range: self.range,
nameRange: self.nameRange,
bodyRange: self.bodyRange,
initializers: self.initializers,
children: tokens,
inheritedTypes: self.inheritedTypes,
attributes: self.attributes,
genericParameters: self.genericParameters,
isNSObjectProtocol: self.isNSObjectProtocol
)
}
public func replace(isNSObjectProtocol: Bool) -> ProtocolDeclaration {
ProtocolDeclaration(
name: self.name,
parent: self.parent,
accessibility: self.accessibility,
range: self.range,
nameRange: self.nameRange,
bodyRange: self.bodyRange,
initializers: self.initializers,
children: self.children,
inheritedTypes: self.inheritedTypes,
attributes: self.attributes,
genericParameters: self.genericParameters,
isNSObjectProtocol: isNSObjectProtocol
)
}
public func isEqual(to other: Token) -> Bool {
guard let other = other as? ProtocolDeclaration else { return false }
return self.name == other.name
}
}

View File

@ -1,17 +0,0 @@
public struct ProtocolMethod: Method {
public var name: String
public var accessibility: Accessibility
public var returnSignature: ReturnSignature
public var range: CountableRange<Int>
public var nameRange: CountableRange<Int>
public var parameters: [MethodParameter]
public var attributes: [Attribute]
public var genericParameters: [GenericParameter]
public var isOptional: Bool {
return attributes.map { $0.kind }.contains(.optional)
}
public var isOverriding: Bool {
return false
}
}

View File

@ -1,29 +0,0 @@
import Foundation
public struct ReturnSignature {
public var isAsync: Bool
public var throwType: ThrowType?
public var returnType: WrappableType
public var whereConstraints: [String]
public init(isAsync: Bool, throwString: String?, returnType: WrappableType, whereConstraints: [String]) {
self.isAsync = isAsync
if let throwString = throwString {
throwType = ThrowType(string: throwString)
} else {
throwType = nil
}
self.returnType = returnType
self.whereConstraints = whereConstraints
}
}
extension ReturnSignature: CustomStringConvertible {
public var description: String {
let asyncString = isAsync ? "async" : nil
let trimmedReturnType = returnType.explicitOptionalOnly.sugarized.trimmed
let returnString = trimmedReturnType.isEmpty || trimmedReturnType == "Void" ? nil : "-> \(returnType)"
let whereString = whereConstraints.isEmpty ? nil : "where \(whereConstraints.joined(separator: ", "))"
return [asyncString, throwType?.description, returnString, whereString].compactMap { $0 }.joined(separator: " ")
}
}

View File

@ -1,17 +0,0 @@
import Foundation
public struct StructDeclaration: ParentToken {
// NOTE: Purely for supporting nested classes, could be any generic parent (like extensions)
public let name: String
public var accessibility: Accessibility
public let range: CountableRange<Int>
public let nameRange: CountableRange<Int>
public let bodyRange: CountableRange<Int>
public var attributes: [Attribute]
public let children: [Token]
public func isEqual(to other: Token) -> Bool {
guard let other = other as? StructDeclaration else { return false }
return self.name == other.name
}
}

View File

@ -1,31 +0,0 @@
public enum ThrowType: CustomStringConvertible {
case throwing
case rethrowing
public init?(string: String) {
if string.trimmed.hasPrefix("throws") {
self = .throwing
} else if string.trimmed.hasPrefix("rethrows") {
self = .rethrowing
} else {
return nil
}
}
public var isThrowing: Bool {
return self == .throwing
}
public var isRethrowing: Bool {
return self == .rethrowing
}
public var description: String {
switch self {
case .throwing:
return "throws"
case .rethrowing:
return "rethrows"
}
}
}

View File

@ -1,29 +0,0 @@
public protocol Token {
func isEqual(to other: Token) -> Bool
func serialize() -> [String: Any]
}
public func ==(rhs: Token, lhs: Token) -> Bool {
return rhs.isEqual(to: lhs)
}
public extension Token {
func serialize() -> [String: Any] {
return [:]
}
func serializeWithType() -> [String: Any] {
var serialized = serialize()
serialized["@type"] = "\(type(of: self))"
return serialized
}
var isClassOrProtocolDeclaration: Bool {
return self is ProtocolDeclaration || self is ClassDeclaration
}
var isInheritanceDeclaration: Bool {
return self is InheritanceDeclaration
}
}

View File

@ -1,148 +0,0 @@
public enum WrappableType {
indirect case optional(WrappableType)
indirect case implicitlyUnwrappedOptional(WrappableType)
indirect case attributed(WrappableType, attributes: [String])
case type(String)
public var sugarized: String {
switch self {
case .optional(let wrapped):
return "\(wrapped.sugarized)?"
case .implicitlyUnwrappedOptional(let wrapped):
return "\(wrapped.sugarized)!"
case .attributed(let wrapped, let attributes):
return "\(attributes.joined(separator: " ")) \(wrapped.sugarized)"
case .type(let type):
return type
}
}
public var desugarized: String {
switch self {
case .optional(let wrapped), .implicitlyUnwrappedOptional(let wrapped):
return "Optional<\(wrapped.desugarized)>"
case .attributed(let wrapped, let attributes):
return "\(attributes.joined(separator: " ")) \(wrapped.desugarized)"
case .type(let type):
return type
}
}
public var explicitOptionalOnly: WrappableType {
switch self {
case .optional(let wrapped), .implicitlyUnwrappedOptional(let wrapped):
return .optional(wrapped.explicitOptionalOnly)
case .attributed(let wrapped, let attributes):
return .attributed(wrapped.explicitOptionalOnly, attributes: attributes)
case .type:
return self
}
}
public var unoptionaled: WrappableType {
switch self {
case .optional(let wrapped), .implicitlyUnwrappedOptional(let wrapped):
return wrapped.unoptionaled
case .attributed(let wrapped, let attributes):
return .attributed(wrapped.unoptionaled, attributes: attributes)
case .type:
return self
}
}
public var unwrapped: WrappableType {
switch self {
case .optional(let wrapped), .implicitlyUnwrappedOptional(let wrapped):
return wrapped
case .attributed(let wrapped, let attributes):
return .attributed(wrapped.unwrapped, attributes: attributes)
case .type:
return self
}
}
public var withoutAttributes: WrappableType {
switch self {
case .optional(let wrapped):
return .optional(wrapped.withoutAttributes)
case .implicitlyUnwrappedOptional(let wrapped):
return .implicitlyUnwrappedOptional(wrapped.withoutAttributes)
case .attributed(let wrapped, _):
return wrapped
case .type:
return self
}
}
public var isOptional: Bool {
switch self {
case .optional, .implicitlyUnwrappedOptional:
return true
case .attributed(let wrapped, _):
return wrapped.isOptional
case .type:
return false
}
}
public init(parsing value: String) {
let trimmedValue = value.trimmed
let optionalPrefix = "Optional<"
if trimmedValue.hasPrefix("@") {
let (attributes, resultString) = ["@autoclosure", "@escaping", "@noescape"]
.reduce(([], trimmedValue)) { acc, next -> ([String], String) in
var (attributes, resultString) = acc
guard let range = resultString.range(of: next) else { return acc }
resultString.removeSubrange(range)
attributes.append(next)
return (attributes, resultString)
}
self = .attributed(WrappableType(parsing: resultString), attributes: attributes)
} else if trimmedValue.hasSuffix("?") {
if trimmedValue.contains("->") && !trimmedValue.hasSuffix(")?") {
self = .type(trimmedValue)
} else {
self = .optional(WrappableType(parsing: String(trimmedValue.dropLast())))
}
} else if trimmedValue.hasPrefix(optionalPrefix) {
self = .optional(WrappableType(parsing: String(trimmedValue.dropFirst(optionalPrefix.count).dropLast())))
} else if trimmedValue.hasSuffix("!") {
self = .implicitlyUnwrappedOptional(WrappableType(parsing: String(trimmedValue.dropLast())))
} else {
self = .type(trimmedValue)
}
}
public func containsAttribute(named attribute: String) -> Bool {
switch self {
case .optional(let wrapped), .implicitlyUnwrappedOptional(let wrapped):
return wrapped.containsAttribute(named: attribute)
case .attributed(_, let attributes):
return attributes.contains(attribute.trimmed)
case .type:
return false
}
}
}
extension WrappableType: CustomStringConvertible {
public var description: String {
return sugarized
}
}
extension WrappableType: Equatable {
public static func ==(lhs: WrappableType, rhs: WrappableType) -> Bool {
switch (lhs, rhs) {
case (.optional(let lhsWrapped), .optional(let rhsWrapped)),
(.implicitlyUnwrappedOptional(let lhsWrapped), .implicitlyUnwrappedOptional(let rhsWrapped)):
return lhsWrapped == rhsWrapped
case (.attributed(let lhsWrapped, let lhsAttributes), .attributed(let rhsWrapped, let rhsAttributes)):
return lhsWrapped == rhsWrapped && lhsAttributes == rhsAttributes
case (.type(let lhsType), .type(let rhsType)):
return lhsType.components(separatedBy: .whitespacesAndNewlines).joined() == rhsType.components(separatedBy: .whitespacesAndNewlines).joined()
default:
return false
}
}
}

View File

@ -1,53 +0,0 @@
import Foundation
struct TypeGuesser {
static func guessType(from value: String) -> String? {
let value = value.trimmed
guard !value.isEmpty else { return nil }
let casting = checkCasting(from: value)
guard casting == nil else { return casting }
let character = value[value.startIndex]
switch character {
case "-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9":
return guessNumberType(from: value)
case "_":
return guessIdentifier(from: value)
case "\"":
return "String"
default:
let identifier = guessIdentifier(from: value)
return identifier == "true" || identifier == "false" ? "Bool" : identifier
}
}
private static func guessNumberType(from value: String) -> String {
var iterator = value.makeIterator()
while let character = iterator.next() {
if character == "." || character == "e" {
return "Double"
}
}
return "Int"
}
private static func guessIdentifier(from value: String) -> String? {
var identifier = ""
var iterator = value.makeIterator()
while let character = iterator.next() {
guard character != "(" else { break }
identifier.append(character)
}
return identifier
}
private static func checkCasting(from value: String) -> String? {
let regex = try! NSRegularExpression(pattern: " as (.*)$")
let range = NSRange(location: 0, length: value.count)
guard let casting = regex.firstMatch(in: value, range: range) else { return nil }
let foundRange = casting.range(at: 1)
guard foundRange.location != NSNotFound else { return nil }
return value[foundRange]
}
}

View File

@ -1,76 +0,0 @@
import Foundation
import SourceKittenFramework
extension String {
var trimmed: String {
return trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
func takeUntil(occurence: String) -> String? {
return components(separatedBy: occurence).first
}
subscript(range: Range<Int>) -> String {
let stringRange = index(startIndex, offsetBy: range.lowerBound)..<index(startIndex, offsetBy: range.upperBound)
return String(self[stringRange])
}
}
extension String.UTF8View {
subscript(range: Range<Int>) -> String {
let stringRange = index(startIndex, offsetBy: range.lowerBound)..<index(startIndex, offsetBy: range.upperBound)
let subsequence: String.UTF8View.SubSequence = self[stringRange]
return String(subsequence) ?? ""
}
}
extension String {
func regexMatches(_ source: String) -> Bool {
let regex = try! NSRegularExpression(pattern: self)
return regex.firstMatch(in: source, range: NSRange(location: 0, length: source.count)) != nil
}
}
extension Sequence {
#if !swift(>=4.1)
public func compactMap<O>(_ transform: (Element) -> O?) -> [O] {
return self.flatMap(transform)
}
#endif
func only<T>(_ type: T.Type) -> [T] {
return compactMap { $0 as? T }
}
func noneOf<T>(_ type: T.Type) -> [Iterator.Element] {
return filter { !($0 is T) }
}
}
/// Reserved keywords that are not allowed as function names, function parameters, or local variables, etc.
fileprivate let reservedKeywordsNotAllowed: Set = [
// Keywords used in declarations:
"associatedtype", "class", "deinit", "enum", "extension", "fileprivate", "func", "import", "init", "inout",
"internal", "let", "operator", "private", "precedencegroup", "protocol", "public", "rethrows", "static",
"struct", "subscript", "typealias", "var",
// Keywords used in statements:
"break", "case", "catch", "continue", "default", "defer", "do", "else", "fallthrough", "for", "guard", "if", "in",
"repeat", "return", "throw", "switch", "where", "while",
// Keywords used in expressions and types:
"Any", "as", "catch", "false", "is", "nil", "rethrows", "self", "super", "throw", "throws", "true", "try",
// Keywords used in patterns:
"_",
]
/// Utility function for escaping reserved keywords for a symbol name.
internal func escapeReservedKeywords(for name: String) -> String {
reservedKeywordsNotAllowed.contains(name) ? "`\(name)`" : name
}
internal func extractRange(from dictionary: [String: SourceKitRepresentable], offset: Key, length: Key) -> CountableRange<Int>? {
guard let offset = (dictionary[offset.rawValue] as? Int64).map(Int.init),
let length = (dictionary[length.rawValue] as? Int64).map(Int.init) else { return nil }
return offset..<offset.advanced(by: length)
}

View File

@ -1,3 +1,11 @@
//
// CuckooGeneratorError.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 13/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
import FileKit

View File

@ -1,8 +1,17 @@
import Foundation
//
// GenerateMocksCommand.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Commandant
import Result
import SourceKittenFramework
import FileKit
import CuckooGeneratorFramework
import Foundation
private func curry<P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R>
(_ f: @escaping (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R)
@ -29,7 +38,7 @@ public struct GenerateMocksCommand: CommandProtocol {
}
let inputFiles = inputPathValues.map { File(path: $0) }.compactMap { $0 }
let tokens = inputFiles.map { Tokenizer(sourceFile: $0, debugMode: options.debugMode).tokenize() }
let tokensWithInheritance = options.noInheritance ? tokens : inheritNSObject(mergeInheritance(tokens))
let tokensWithInheritance = options.noInheritance ? tokens : mergeInheritance(tokens)
// filter classes/protocols based on the settings passed to the generator
var typeFilters = [] as [(Token) -> Bool]
@ -54,7 +63,7 @@ public struct GenerateMocksCommand: CommandProtocol {
do {
if outputPath.isDirectory {
let inputPaths = inputFiles.compactMap { $0.path }.map { Path($0) }
let inputPaths = inputPathValues.map { Path($0) }
for (inputPath, outputText) in zip(inputPaths, mergedFiles) {
let fileName = options.filePrefix + inputPath.fileName
let outputFile = TextFile(path: outputPath + fileName)
@ -77,43 +86,6 @@ public struct GenerateMocksCommand: CommandProtocol {
return filesRepresentation.compactMap { $0.mergeInheritance(with: filesRepresentation) }
}
private func inheritNSObject(_ filesRepresentation: [FileRepresentation]) -> [FileRepresentation] {
func containsRecursively(name: String) -> Bool {
if let protocolDeclaration = protocolDeclarationDictionary[name] {
let collapsedInheritedTypesName = protocolDeclaration
.inheritedTypes
.map({ $0.name.components(separatedBy: "&") })
.joined()
.map({ $0.trimmingCharacters(in: .whitespaces) })
if collapsedInheritedTypesName.contains(where: { $0 == "NSObjectProtocol" }) {
return true
} else {
return protocolDeclaration.inheritedTypes.contains(where: { inheritanceType in
containsRecursively(name: inheritanceType.name)
})
}
} else {
return false
}
}
let protocolDeclarationDictionary: [String: ProtocolDeclaration] = Dictionary(
uniqueKeysWithValues: filesRepresentation.flatMap { file in
file.declarations.compactMap { token -> (name: String, protocolDeclaration: ProtocolDeclaration)? in
guard let protocolDeclaration = token as? ProtocolDeclaration else { return nil }
return (name: protocolDeclaration.name, protocolDeclaration: protocolDeclaration)
}
}
)
let nsObjectProtocols: [ProtocolDeclaration] = protocolDeclarationDictionary.values.reduce(into: []) { accumulator, protocolDeclaration in
guard containsRecursively(name: protocolDeclaration.name) else { return }
accumulator.append(protocolDeclaration)
}
return filesRepresentation.map { $0.inheritNSObject(subjects: nsObjectProtocols) }
}
private func removeTypes(from files: [FileRepresentation], using filters: [(Token) -> Bool]) -> [FileRepresentation] {
// Only keep those that pass all filters
let filter: (Token) -> Bool = { token in
@ -170,20 +142,19 @@ public struct GenerateMocksCommand: CommandProtocol {
let globEnabled: Bool
let regex: String
public init(
output: String,
testableFrameworks: String,
exclude: String,
noHeader: Bool,
noTimestamp: Bool,
noInheritance: Bool,
filePrefix: String,
noClassMocking: Bool,
debugMode: Bool,
globEnabled: Bool,
regex: String,
files: [String]
) {
public init(output: String,
testableFrameworks: String,
exclude: String,
noHeader: Bool,
noTimestamp: Bool,
noInheritance: Bool,
filePrefix: String,
noClassMocking: Bool,
debugMode: Bool,
globEnabled: Bool,
regex: String,
files: [String]) {
self.output = output
self.testableFrameworks = testableFrameworks.components(separatedBy: ",").filter { !$0.isEmpty }
self.exclude = exclude.components(separatedBy: ",").filter { !$0.isEmpty }.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }

View File

@ -1,6 +1,12 @@
//
// Created by Eric Firestone on 3/22/16.
// Copyright © 2016 Square, Inc. All rights reserved.
// Released under the Apache v2 License.
//
// Adapted from https://gist.github.com/blakemerryman/76312e1cbf8aec248167
import Foundation
// Adapted from https://gist.github.com/blakemerryman/76312e1cbf8aec248167
/**
Finds files on the file system using pattern matching.
*/

View File

@ -0,0 +1,47 @@
<?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">
<array>
<dict>
<key>BundleIsVersionChecked</key>
<false/>
<key>BundleOverwriteAction</key>
<string>upgrade</string>
<key>ChildBundles</key>
<array>
<dict>
<key>BundleOverwriteAction</key>
<string></string>
<key>RootRelativeBundlePath</key>
<string>/usr/local/Frameworks/CuckooGeneratorFramework.framework/Versions/A/Frameworks/FileKit.framework</string>
</dict>
<dict>
<key>BundleOverwriteAction</key>
<string></string>
<key>RootRelativeBundlePath</key>
<string>/usr/local/Frameworks/CuckooGeneratorFramework.framework/Versions/A/Frameworks/Result.framework</string>
</dict>
<dict>
<key>BundleOverwriteAction</key>
<string></string>
<key>RootRelativeBundlePath</key>
<string>/usr/local/Frameworks/CuckooGeneratorFramework.framework/Versions/A/Frameworks/Commandant.framework</string>
</dict>
<dict>
<key>BundleOverwriteAction</key>
<string></string>
<key>RootRelativeBundlePath</key>
<string>/usr/local/Frameworks/CuckooGeneratorFramework.framework/Versions/A/Frameworks/SWXMLHash.framework</string>
</dict>
<dict>
<key>BundleOverwriteAction</key>
<string></string>
<key>RootRelativeBundlePath</key>
<string>/usr/local/Frameworks/CuckooGeneratorFramework.framework/Versions/A/Frameworks/SourceKittenFramework.framework</string>
</dict>
</array>
<key>RootRelativeBundlePath</key>
<string>/usr/local/Frameworks/CuckooGeneratorFramework.framework</string>
</dict>
</array>
</plist>

View File

@ -3,9 +3,11 @@
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
@ -13,12 +15,18 @@
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>0.8.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright ©. All rights reserved.</string>
<string>Copyright © 2016 Brightify. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

Some files were not shown because too many files have changed in this diff Show More