Compare commits

...

228 Commits

Author SHA1 Message Date
Kelvin Harron 088c021cbc Update README.md for SPM instructions
specified the target to select as part of the SPM installation process
2023-06-03 14:39:07 +02:00
Matyáš Kříž 475322e609 Bump version. 2023-04-17 17:30:01 +02:00
nanashiki 9676d7e374 Up DEPLOYMENT_TARGETs 2023-04-17 17:27:11 +02:00
nanashiki 75a209c3ba Fix String index is out of bounds crash 2023-04-17 17:24:25 +02:00
Matyáš Kříž e29f10eac6 Bump version. 2023-04-11 23:19:48 +02:00
Kabir Oberai 4148a6a7ff Support effectful properties 2023-04-11 23:17:51 +02:00
Kabir Oberai a71ba74df1 Fix generator crashing for property wrappers with empty type. 2023-04-01 10:19:13 +02:00
Matyáš Kříž fa66b6a0a0 Bump version. 2023-03-26 14:22:04 +02:00
Seth Deckard 6f41152d83
Fix Generator to handle reserved keywords
Allows for reserved keywords to be used as argument labels / parameters
in mocked types.

Fixes: https://github.com/Brightify/Cuckoo/issues/452

- Moves escaping util function to Utils and makes it internal since it
  now needs to be used outside of `Generator`.
- Uses escaping function in areas affected by the bug.
- Renames set of reserved names to reflect the larger scope (no longer
  used for just function names).
- Some minor clean-up to style and formatting on areas touched.

Example of the problem:

Generate a mock for a protocol with the function:

```
func escape(for: String) -> String
```

This would result in `for` as a function parameter used within the body
of the function for the mock and verification and stubbing proxies,
which would not compile. This change escapes keywords like this so the
generated code will compile.

See `TestedProtocol.withReservedKeywords` for a test example. An example
was added to `TestedClass` as well, but the real motivation for this is
protocols since conforming types can and often do use a different
parameter name so the argument label like `for` would never be used
within the body of the function.
2023-03-23 08:31:12 -04:00
Matyáš Kříž a9469909b7 Bump version. 2023-03-17 16:26:12 +01:00
Matyáš Kříž 7d51beb752 Update CI. 2023-03-17 16:23:34 +01:00
Matyáš Kříž 41a70422fb Update pbxproj. 2023-03-17 16:21:39 +01:00
David Steinacher f0f77397a5 add missing () when closure is an autoclosure 2023-02-12 16:30:21 +01:00
Matyáš Kříž 835c61246a Completely convert project to Tuist. 2023-01-07 18:58:48 +01:00
Farid Mammadov cbb5f5d416 Add support async/await in Protocol with Associated Type. 2023-01-03 16:46:43 +01:00
Matyáš Kříž 102c9b3a10 Add forgotten file to the project. 2023-01-03 16:39:53 +01:00
sk409 8d7b893bc2 Support NSObjectProtocol inheritance for protocol. 2022-09-19 17:39:55 +02:00
Matyáš Kříž 64b1681e37 Bump version. 2022-09-01 17:04:31 +02:00
sk409 5a9301b97c Remove closure argument name. 2022-09-01 17:00:52 +02:00
Matyáš Kříž 4b5a397f58 Bump version. 2022-08-16 19:40:27 +02:00
Antonio J Pallares 55149ddee9 test: add tests with `OCMQuantifier`s 2022-08-16 16:49:01 +02:00
Antonio J Pallares a84978a0c1 feat: enable OCMock's OCMQuantifier for objcVerify function. Fixes #366 2022-08-16 16:48:58 +02:00
Yoji Ueda 0ff327d675 Remove unnecessary await expression in MockTemplate. 2022-08-16 16:42:21 +02:00
Matyáš Kříž 3df9608f5a Bump version. 2022-07-24 16:18:58 +02:00
Roland Kakonyi 1b0af7cc4a Fix missing attributes for Stubs. 2022-07-24 16:14:15 +02:00
Matyáš Kříž efa47f2afa Update Fastlane. 2022-06-20 21:43:35 +02:00
Matyáš Kříž d3de8c0bfd Bump version. 2022-06-20 21:29:45 +02:00
Tyler Thompson 5d52a7f75b Add support for nested classes. 2022-06-17 19:06:07 +02:00
sk409 3f6e9a84e8 Add support for multiline method signatures. 2022-06-17 18:23:07 +02:00
Roland Kakonyi a3c4ad13f7 Add support for unavailable platforms from available attributes. 2022-06-17 17:49:03 +02:00
Matyáš Kříž 6af1b4d09f Remove duplicate attributes. 2022-05-30 20:28:33 +02:00
Matyáš Kříž 69c1c9aee6 Bump version. 2022-03-18 13:03:35 +01:00
Matyáš Kříž de28aa5f3c Revert "Add SPM support for Cuckoo+OCMock." because of SPM instability.
This reverts commit e4a0153485.
2022-03-18 13:01:22 +01:00
Matyáš Kříž 084f5dbdc3 Fix automatic generator binary upload. 2022-03-17 21:14:20 +01:00
Matyáš Kříž 83b5c4e83b Bump version. 2022-03-17 18:55:22 +01:00
Matyáš Kříž dd8db792ba Don't require a name range for method parameters. 2022-03-17 18:40:48 +01:00
Antonio J Pallares e4a0153485 Add SPM support for Cuckoo+OCMock. 2022-03-10 19:53:38 +01:00
sk409 b1da946216 Ignore missing input paths. 2022-02-23 20:53:08 +01:00
sk409 2775830d13 Escape reserved keywords. 2022-02-13 17:46:04 +01:00
Matyáš Kříž fe7b56381c Bump version. 2022-01-19 17:28:56 +01:00
Matyáš Kříž 21a4fc39df Update `.gitignore`. 2022-01-19 17:22:52 +01:00
Matyáš Kříž 249aba0930 Update `Podfile.lock`. 2022-01-19 17:22:09 +01:00
Ailton Vieira Pinto Filho 53b8ff4428 Add support for concurrency mocking. 2022-01-18 19:37:21 +01:00
Matyáš Kříž 6c6c7fa83c Update PathKit, Spectre, Stencil. 2021-11-29 13:29:29 +01:00
Matyáš Kříž 6473adac0d Make deployed binaries work for both Intel and M1 Macs. 2021-11-29 13:29:14 +01:00
Matyáš Kříž 79b13331e5 Bump version. 2021-11-19 08:05:56 +01:00
Matyáš Kříž b7ff314d6b Raise minimum deployment targets.
iOS to 9.0; Mac OS to 11.0
2021-11-19 08:04:03 +01:00
Matyáš Kříž df8847df2d Update gem dependencies. 2021-11-19 07:55:56 +01:00
Boris Spinner 7da80b2321 Skip cuckoo for SwiftUI preview builds 2021-11-19 07:47:25 +01:00
Akihiro Sakahara eb1502c657 Bump Stencil version to 0.14.2. 2021-11-18 17:10:30 +01:00
Tadeas Kriz 8f1d1f1d09
Add check for index build to the example run script. 2021-10-08 12:49:55 +02:00
Matyáš Kříž d266c2bc68 Update `README.md`. 2021-07-27 13:12:45 +02:00
Matyáš Kříž 0259cd8730 Remove unnecessary whitespace in tests. 2021-07-22 16:24:45 +02:00
Matyáš Kříž 10b480320c Update OCMock to `3.8.1`. 2021-07-22 14:50:00 +02:00
Matyáš Kříž 39e05cdf56 Bump version. 2021-05-25 17:12:33 +02:00
Andreas Ganske 6c59599fc0 Use constant action 2021-05-19 14:37:43 +02:00
Andreas Ganske 798ea3fcdc Make stubbing more thread safe 2021-05-19 14:37:43 +02:00
Tyler Poland 7ca53fb53c Disable bitcode compiler translation to fix linking for Carthage distribution 2021-04-29 10:43:21 -05:00
Tyler Poland 96ff0d11d0 Fix local build failure for Cuckoo-iOS, -macOS, and -tvOS targets 2021-04-28 16:33:15 -05:00
Matyáš Kříž 71d7423add Bump version. 2021-04-28 09:49:19 +02:00
Winnie Teichmann 531f4d403a Update podspec to fix library not found for -lswiftXCTestin Xocde 12.5 2021-04-27 20:57:51 +02:00
Matyáš Kříž 58ec81375b Bump version. 2021-04-15 14:27:47 +02:00
Matty Cross d85e2dd8e7
Remove Slack from `README.md` for now. 2020-08-22 18:49:55 +02:00
dependabot[bot] 1d31d53b04 Bump json from 2.2.0 to 2.3.0
Bumps [json](https://github.com/flori/json) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/flori/json/releases)
- [Changelog](https://github.com/flori/json/blob/master/CHANGES.md)
- [Commits](https://github.com/flori/json/compare/v2.2.0...v2.3.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-31 19:34:15 +02:00
Michael Gray 6373c81dd2
Update README.md
Co-authored-by: Matty Cross <kriz.matyas8.10@gmail.com>
2020-07-31 13:30:40 -04:00
Michael Gray cacef807d2
Update README.md
Co-authored-by: Matty Cross <kriz.matyas8.10@gmail.com>
2020-07-31 13:25:28 -04:00
Mike Gray bc085e23cf Add example for verify generic return to README.md 2020-07-31 13:14:37 -04:00
Mike Gray f6429bb757 Add tests for verify generic return 2020-07-31 13:14:32 -04:00
Mike Gray 60f2cc0006 Include GeneratedMocks in macOS and tvOS test targets 2020-07-31 11:49:31 -04:00
Mike Gray 403b225770 Allow verify to specify return type for generic functions 2020-07-31 11:39:12 -04:00
Matyáš Kříž 4d8e999f37 Update `.xcodeproj`. 2020-07-31 17:33:39 +02:00
Matyáš Kříž a508ab347f Fix tuist configuration. 2020-07-31 08:11:22 +02:00
Matty Cross bf197c4ccb
Update CHANGELOG.md 2020-07-08 10:49:19 +02:00
Matty Cross 296c14b610
Update syntax highlighting in examples. 2020-07-08 10:47:03 +02:00
Matty Cross 1d302ede70
Use Swift syntax highlight in ObjC example. 2020-07-08 10:45:22 +02:00
Matyáš Kříž 0ef473893c Bump version. 2020-07-08 10:25:36 +02:00
Matyáš Kříž a0a7c82526 Sidestep `SourceKitten`'s off-by-one bug when parsing generic parameter inheritance. 2020-06-28 16:45:16 +02:00
Matyáš Kříž e2bb73412f Fix incorrect `where` clause parsing. 2020-06-28 16:44:48 +02:00
Matyáš Kříž b3bec5f8ff Clean up project. 2020-06-28 16:43:22 +02:00
Matyáš Kříž 8abe3f2091 Add a note about "Input Files". 2020-06-28 13:22:08 +02:00
Matty Cross 07b4a701a4
Update CHANGELOG.md 2020-06-18 20:40:06 +02:00
Matyáš Kříž a732857b79 Bump version. 2020-06-18 20:32:58 +02:00
Matyáš Kříž bfc893657e Add a note about build parallelization. 2020-06-18 20:13:29 +02:00
Matyáš Kříž 3b533e15a0 Add type inference information to README. 2020-06-18 20:03:25 +02:00
Matyáš Kříž c2ff12851f Clean up `MockManager`. 2020-06-18 19:41:58 +02:00
Matyáš Kříž db48119479 Add generic closure test. 2020-06-18 19:41:47 +02:00
Matyáš Kříž 5a1f5f35d9 Add tvOS targets and schemes. 2020-04-01 15:07:38 +02:00
Matyáš Kříž 5afae85bbe Integrate `tuist`. 2020-04-01 15:07:38 +02:00
Matyáš Kříž b1c7c77dbb Fix type equality check to rule out whitespace inconsistencies. 2020-04-01 15:07:13 +02:00
Matty Cross ce2de127e6
Update README.md 2020-03-10 19:38:20 +01:00
Matyáš Kříž 24f743f950 Add `then` with `return` to the tests.
Add missing space.
2020-03-09 19:34:07 +01:00
nisin 826e5c4427 Compare on arrays containing values (OCMock test) 2020-03-09 19:34:07 +01:00
nisin 972f0594f7 add test Sequence Type result by OCMock extention 2020-03-09 19:34:07 +01:00
nisin f81c04b504 add Sequence OCMock extension 2020-03-09 19:34:07 +01:00
Matty Cross 264d2ebb50
Update .travis.yml 2020-03-09 15:01:28 +01:00
Matty Cross d16b4e6635
Remove obsolete sentence from README. 2020-02-29 19:37:52 +01:00
Matty Cross ee447e93c0
Add SwiftPM badge to README. 2020-02-29 19:35:24 +01:00
Matty Cross 9cc13cc195
Update CHANGELOG.md 2020-02-29 19:29:14 +01:00
Matty Cross 6cb87a423f
Update CI badge. 2020-02-29 19:17:51 +01:00
Matyáš Kříž bbdd3304b0 Bump version. 2020-02-23 22:25:53 +01:00
Matyáš Kříž 7fa3d75aee Fix `image not found` error for iOS 13 and beyond. 2020-02-23 22:21:50 +01:00
dependabot[bot] 01fb627626 Bump excon from 0.68.0 to 0.71.0
Bumps [excon](https://github.com/excon/excon) from 0.68.0 to 0.71.0.
- [Release notes](https://github.com/excon/excon/releases)
- [Changelog](https://github.com/excon/excon/blob/master/changelog.txt)
- [Commits](https://github.com/excon/excon/compare/v0.68.0...v0.71.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-23 20:07:59 +01:00
Matyáš Kříž 9f6f3e04e6 Bump version. 2020-02-23 19:52:59 +01:00
Matyáš Kříž f3dcdb0edc Update SwiftPM info in README. 2020-02-22 22:22:03 +01:00
Matyáš Kříž 5600ca3d96 Drastically shorten needed command to get the `run` script. 2020-02-22 21:13:01 +01:00
Matyáš Kříž 9e8cdfde9a Add instructions on how to obtain the `run` script. 2020-02-22 21:13:01 +01:00
Matyáš Kříž 4a51ddd885 Remove unwanted space. 2020-02-22 21:13:01 +01:00
Matyáš Kříž 5d68a586de Add SwiftPM section to the `README.md`. 2020-02-22 21:13:01 +01:00
Matyáš Kříž caf0db428f Add Swift Package Manager support. 2020-02-22 21:13:01 +01:00
dependabot[bot] 491ff8dff8 Bump ffi from 1.9.10 to 1.9.25 in /Generator/Tests
Bumps [ffi](https://github.com/ffi/ffi) from 1.9.10 to 1.9.25.
- [Release notes](https://github.com/ffi/ffi/releases)
- [Changelog](https://github.com/ffi/ffi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ffi/ffi/compare/1.9.10...1.9.25)

Signed-off-by: dependabot[bot] <support@github.com>
2020-02-20 21:07:47 +01:00
Matyáš Kříž 1ef420cad8 Add tests to `run` scheme of tvOS. 2020-02-20 21:06:40 +01:00
Rodrigo F. Fernandes 60525cc68f Adds tvOS target 2020-02-20 21:06:40 +01:00
Tadeas Kriz fb9e2ea421 Fix compilation issue introduced by a wrong merge. 2020-02-20 21:05:29 +01:00
Tadeas Kriz 619d8c73dd Adjust accessibility to match enclosing container. 2020-02-20 21:05:29 +01:00
Kevin Wojniak f49e47ee7b Fail immediately if any command fails
I was having issues where the `swift build` command was failing but the script itself wouldn't fail. Using `set -x` will cause the shell script to immediately stop and exit if any command in the script itself fails.
2020-02-20 21:05:18 +01:00
Matyáš Kříž 0a3b667e4f Restructure README. 2020-02-20 21:03:06 +01:00
Matyáš Kříž e763706be7 Ignore `Pods` folder. 2020-02-20 21:00:31 +01:00
Matyáš Kříž 54a86c37da Reduce sentence complexity about unsupportable code structures. 2019-12-31 13:26:19 +01:00
Matyáš Kříž cd58a7f09e Bump version. 2019-11-06 14:33:08 +01:00
Matyáš Kříž 81e1cb59a5 Update Fastlane. 2019-11-06 14:13:43 +01:00
Matyáš Kříž 6a2f9596fb Update `README.md`. 2019-11-06 14:11:05 +01:00
Matyáš Kříž ed1e947e1f Improve code stylistically. 2019-11-06 13:45:16 +01:00
Matyáš Kříž 674ecd9de6 Fix ObjC warning. 2019-11-06 13:42:16 +01:00
Matyáš Kříž cc5afef8f7 Add test cases for closures. 2019-11-06 13:42:04 +01:00
Matyáš Kříž 44dedd3859 Improve code stylistically. 2019-11-05 16:00:49 +01:00
Pubbus 808a19f920 Code cleaning 2019-11-05 16:00:49 +01:00
Pubbus ff4c627d42 Add tests for closure return type extraction. 2019-11-05 16:00:49 +01:00
Pubbus fc91ef5e1c Refactor and fix closure return type matcher 2019-11-05 16:00:49 +01:00
Pubbus 69facb06bb Fix closure generation, return type is required in `withoutActuallyEscaping` from swift 5.1 (XCode11) 2019-11-05 16:00:49 +01:00
Artyom Y. Razinov 67f91fdad6 Switch swift_version to 5.0 and add LIBRARY_SEARCH_PATHS in xcconfig
It is a minimal required fix to avoid a runtime linking error while running tests on iOS versions prior to 13 after building with Xcode 11
2019-11-05 15:59:57 +01:00
Matyáš Kříž e1682c7888 Ignore 32b workspace check flag. 2019-11-01 09:58:22 +01:00
Matyáš Kříž b0e46c78e2 Bump version. 2019-09-11 15:56:34 +02:00
Matyáš Kříž 7eb5517b27 After-rebase fix. 2019-09-11 15:54:13 +02:00
Matyáš Kříž b49d23a6ce Update some gems. 2019-09-11 15:41:47 +02:00
Matyáš Kříž c3af210aff Fix a word. 2019-09-11 15:25:03 +02:00
Matyáš Kříž ba7f7049f4 Add documentation to ObjC matchers and functions. 2019-09-11 11:47:11 +02:00
Matyáš Kříž 60d8aa1597 Add example `Cuckoo/OCMock` usage to README.md. 2019-09-11 11:43:41 +02:00
Matyáš Kříž 308cb86548 Finalize ObjC mocking. 2019-09-11 11:43:41 +02:00
Matyáš Kříž 2228a9bea7 Change buildable name. 2019-09-11 11:43:41 +02:00
Matyáš Kříž f10f174357 Add macOS target for CI testing. 2019-09-11 11:43:41 +02:00
Matyáš Kříž 30c664f744 Add a test with Swift class with dynamic method. 2019-09-11 11:43:41 +02:00
Matyáš Kříž d2f83928ba Add possibility to use `objectiveAny` with String.
by @TadeasKriz
2019-09-11 11:43:41 +02:00
Matyáš Kříž d42fc414f6 Remove useless `Info.plist` files. 2019-09-11 11:43:41 +02:00
Matyáš Kříž 3a42272199 Add initial ObjC mocking functionality utilizing OCMock. 2019-09-11 11:43:41 +02:00
Matyáš Kříž d48f2ab5cc Add swiftdoc for convenience matchers as well as some info to README.md. 2019-09-10 17:44:23 +02:00
Matyáš Kříž c5f6d0cd25 Add verifications for the concrete value matching test. 2019-09-06 10:38:34 +02:00
Matyáš Kříž 0d786501bc Add most common matchers for sequences and dictionaries. 2019-09-05 18:22:12 +02:00
Matyáš Kříž b0c66e7afa Add initial convenience matchers for `Array`, `Dictionary`, and `Set` types. 2019-09-04 17:25:36 +02:00
Matyáš Kříž 4e8abcace6 Bump version. 2019-07-30 16:25:00 +02:00
Matyáš Kříž 9947868805 Add container accessibility to appropriate places in templates. 2019-07-30 16:18:39 +02:00
Matyáš Kříž 18ab4be1d8 Bump version. 2019-07-22 13:15:08 +02:00
Matyáš Kříž 989680cebe Update README with info about a simple type inferrer. 2019-07-22 13:10:48 +02:00
Matyáš Kříž 62befddc20 Add tests for type guesser verification. 2019-07-22 13:10:48 +02:00
Matyáš Kříž 39257d9cc8 Add initial implementation of a type guesser. 2019-07-22 13:10:48 +02:00
Matyáš Kříž e405dbfe0a Copy protocol's accessibility to the implementation caller class. 2019-07-22 13:09:53 +02:00
Matyáš Kříž 86432954a9 Revert accessibility inheritance. 2019-07-22 13:09:53 +02:00
Matthew Cross 76c303797f
Use `master` for reporting CI status. 2019-06-19 17:14:12 +02:00
Matyáš Kříž b5c472b4a2 Remove forgotten return value. 2019-05-29 20:16:56 +02:00
Matyáš Kříž c6965b30ff Bump version. 2019-05-29 20:10:36 +02:00
Matyáš Kříž b3fd0dacfd Some more cleaning along with one more compile test case. 2019-05-29 20:06:10 +02:00
Matyáš Kříž fe3af6709e Clean up and fix a bug where adding a private name to a function made it generate twice and fail the compilation. 2019-05-29 19:44:49 +02:00
Matyáš Kříž 0cb6417067 Make it clear that the `run script` should be above the `Compile Sources` build phase. 2019-05-26 19:13:29 +02:00
Matyáš Kříž 4493b12144 Bump version. 2019-05-26 15:41:07 +02:00
Matyáš Kříž 694bd9c8be Fix generic protocol generation and type erasure with multiple methods of same name. 2019-05-26 15:37:40 +02:00
Matyáš Kříž d69bcd5bdd Fix a bug concerning empty public name methods with no private ones. 2019-05-26 15:36:12 +02:00
Matyáš Kříž ec8c77bd17 Exit `run` script with error if it fails to get generator download URL. 2019-05-20 10:50:37 +02:00
James Hughes efddb1848d use absolute path for cuckoo_generator download location 2019-05-17 10:05:09 -06:00
Matyáš Kříž bbe20871e7 Bump version. 2019-05-17 12:22:38 +02:00
Matyáš Kříž 8412e772fa Don't use `realpath` command not available by default on Mac OS. 2019-05-17 12:18:02 +02:00
Matyáš Kříž 922889ca32 Remove redundant optional classes. 2019-05-16 19:10:40 +02:00
Matyáš Kříž 32b2e3ebf1 Bump version. 2019-05-16 18:53:09 +02:00
Matyáš Kříž e16fb3a383 Add support for optional read only properties. 2019-05-16 18:51:03 +02:00
Dotor, Dr. Alexander f9e035475c Add tests for stubbing optional read only properties. 2019-05-16 18:51:03 +02:00
Matyáš Kříž dda4e2eab6 Bump version. 2019-05-16 13:41:22 +02:00
Matyáš Kříž d0182512e0 Fix `where` that doesn't work in Swift 4. 2019-05-16 13:39:29 +02:00
Matyáš Kříž 0c3333e8d7 Add a future API improvement (it's ambiguous at the moment). 2019-05-16 13:32:38 +02:00
Matyáš Kříž 7a41a928fc Fix some `run` script bugs. 2019-05-16 13:29:44 +02:00
Matyáš Kříž ec10e6bf00 Bump version. 2019-05-15 17:50:57 +02:00
Matyáš Kříž b4c2bbe5b7 Fix incorrect test and simplify two ternary operators. 2019-05-15 17:48:16 +02:00
Matyáš Kříž 449b71e3db Fix `any()` not working anymore by itself with optional parameters in functions. 2019-05-15 17:44:34 +02:00
Matyáš Kříž f3d28b70a9 Fill in changelog and improve readme. 2019-05-15 13:15:32 +02:00
Matyáš Kříž b039aa697f Update fastlane.
It's annoying to see the changelogs every time fastlane is ran.
2019-05-14 19:00:23 +02:00
Matyáš Kříž f4e9608375 Allow warnings of Cocoapods. 2019-05-14 18:57:36 +02:00
Matyáš Kříž 59ac461c98 Bump version. 2019-05-14 18:46:44 +02:00
Matyáš Kříž 1cc26c2ee1 Add `Dictionary` matching. 2019-05-14 18:44:44 +02:00
Matyáš Kříž 51353539bf Revert podspec if it fails validation. 2019-05-14 18:42:51 +02:00
Henrique Gouveia dc0ec1242d Change build_generator to work with swift 5 2019-05-14 17:42:41 +02:00
Matyáš Kříž 935afbfd6a Fix wrong interpolation of github auth token.
Thanks to @jamezilla for noticing this.
2019-05-14 17:41:00 +02:00
Matyáš Kříž 083a6b5c17 Add support for rethrowing functions. 2019-05-14 17:39:15 +02:00
Davide Mazzoni 14afe0a125 Add @available-annotated property to TestedClass 2019-05-14 14:55:20 +02:00
Davide Mazzoni 1b71fcfd2e Add property attributes to stubs and proxies 2019-05-14 14:54:51 +02:00
Matyáš Kříž 2305ec2221 Add necessary types and fix errors. 2019-05-14 14:53:42 +02:00
Matyáš Kříž ec41a4ff97 Add a test class with generic methods to be generated. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 446590b81d Add `GeneratedMocks.swift` to output files for Xcode 10. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 0e894e2b7d Don’t use `UIKit` in tests so that it can be tested on Mac OS. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 732b4f3fed Remove unused gems and add new file to macOS target for tests. 2019-05-14 14:07:54 +02:00
Matyáš Kříž bd28aad894 Use Xcode 10 for testing. 2019-05-14 14:07:54 +02:00
Matyáš Kříž bf8033643c Address PR comments. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 6e869257d3 Make `Optional` tests functional. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 803bd38b58 Add throwing and rethrowing stub functionality. 2019-05-14 14:07:54 +02:00
Matyáš Kříž ffc33ba6db Make closures with no parameters work. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 7cd237c212 Add possibility to mock functions with closures containing up to 7 parameters. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 505da8310f Fix known problems with return type containing “w”. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 58deea7542 Fix optional closure matching. 2019-05-14 14:07:54 +02:00
Matyáš Kříž ae4d42b8d5 After-rebase fixes. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 7d07b486ac Finish generic where clause functionality. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 79c7961e68 Add support for inheritance generic parameters in methods. 2019-05-14 14:07:54 +02:00
Matyáš Kříž ffa15c5a84 Move collision classes to different file, so that they don’t get mocked. 2019-05-14 14:07:54 +02:00
Matyáš Kříž a4e6e5fae6 Remove unnecessary file. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 1118272598 Add documentation. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 31a5a40c0b Fix tests. 2019-05-14 14:07:54 +02:00
Matyáš Kříž 14eaaf945f Add some basic tests for generic class and generic protocol mocks. 2019-05-14 14:07:54 +02:00
Matyáš Kříž f4124a6802 Add generic protocol capability. 2019-05-14 14:07:54 +02:00
Matyáš Kříž f2b492e60e Add generic protocol parsing and generation.
It doesn’t compile yet, though.
2019-05-14 14:07:54 +02:00
Matyáš Kříž b739903983 Add generic class support. 2019-05-14 14:04:47 +02:00
Tadeas Kriz 93a42ccacc Fix not being able to put Optional into functions accepting Optionals. 2019-05-14 14:04:47 +02:00
Matyáš Kříž edb170175e WIP allow non-optional values to be passed as matchers for optionals. 2019-05-14 14:04:47 +02:00
Matyáš Kříž 6affe4029b Add support for inout method parameters. 2019-05-14 14:04:47 +02:00
Matyáš Kříž 170f1b4004 Ignore everything final. 2019-05-14 14:04:47 +02:00
Matyáš Kříž 98559606f8 Generate public variables and functions in public classes. 2019-05-14 14:04:47 +02:00
Tadeas Kriz f70a59169f Bump version. 2019-05-06 10:31:34 +02:00
Tadeas Kriz 9bce92c6bf Update cocoapods. 2019-05-06 10:29:47 +02:00
Tadeas Kriz fa8177c81c Revert version bump. 2019-05-06 10:18:57 +02:00
Tadeas Kriz 5ce0538170 Bump version and remove the deprecated .swift-version file. 2019-05-06 09:59:12 +02:00
Dylan Maryk 6c5501792b Fix warnings when using Swift 5.0 2019-05-06 09:45:50 +02:00
Dylan Maryk 773bc177d4 Fix errors when using Swift 5.0 2019-05-06 09:45:41 +02:00
Tadeas Kriz 18f399097d Improve Xcode 10.2 compatibility. 2019-05-06 09:44:08 +02:00
Davide Mazzoni 31798c0f76 Update FileKit reference 2019-04-08 18:10:08 +02:00
245 changed files with 14176 additions and 6007 deletions

View File

@ -33,13 +33,11 @@ platform :mac do
def upload_release release_type
# Building Cuckoo Generator
Dir.chdir('../Generator') do
sh('../build_generator')
end
sh('../build_generator')
# 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'
@ -51,10 +49,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
@ -65,12 +63,18 @@ platform :mac do
auth_string = "#{username}:#{access_token}"
changelog = create_changelog.gsub(/`/, '``').gsub(/'/, '&#39;').gsub(/"/, '&#34;').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
creation_body = "'{\"tag_name\":\"#{version}\",\"target_commitish\":\"master\", \"name\":\"New Release #{version}\", \"body\":\"#{changelog}\", \"draft\":false, \"prerelease\":false}'"
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_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.*}/, '')
@ -79,8 +83,7 @@ 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_lib_lint
pod_push(path: 'Cuckoo.podspec')
pod_push(path: 'Cuckoo.podspec', allow_warnings: true)
UI.success "All done!\nYou can now visit #{creation_response['url']} (command+click) and release the thing."
end
@ -95,10 +98,11 @@ platform :mac do
end
after_all do
reset_git_repo
reset_git_repo(disregard_gitignore: false)
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,5 +1,4 @@
# Xcode
#
.build/
build/
*.pbxuser
@ -19,26 +18,33 @@ DerivedData
*.xcuserstate
# CocoaPods
#
# 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/
Pods
# Carthage
#
# 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
Generator/CuckooGenerator.xcodeproj
.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

104
.package.resolved Normal file
View File

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

View File

@ -1 +0,0 @@
4.0

View File

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

1
.tuist-version Normal file
View File

@ -0,0 +1 @@
3.11.0

View File

@ -1,89 +1,172 @@
# Changelog
## 0.11.0
## 1.4.1
- Sidestep `SourceKit`'s off-by-one bug when parsing generic parameter inheritance.
- Fix incorrect `where` clause parsing.
* 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)
## 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)
## 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 = "0.12.1"
s.version = "1.10.3"
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 = '8.0'
s.osx.deployment_target = '10.9'
s.ios.deployment_target = '9.0'
s.osx.deployment_target = '11.0'
#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,5 +28,27 @@ 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' }
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
end

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -1,108 +0,0 @@
<?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 = "0910"
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -14,7 +14,7 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A29A1FBB1D400058FEC5"
BlueprintIdentifier = "1DC56D8212E4351521A57BD1"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
@ -26,37 +26,24 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A2A61FBB20830058FEC5"
BlueprintIdentifier = "DBDB2DD4E7BE487EEB257CC8"
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"
@ -66,14 +53,12 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A29A1FBB1D400058FEC5"
BlueprintIdentifier = "1DC56D8212E4351521A57BD1"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@ -84,7 +69,7 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "18E2A29A1FBB1D400058FEC5"
BlueprintIdentifier = "1DC56D8212E4351521A57BD1"
BuildableName = "Cuckoo.framework"
BlueprintName = "Cuckoo-macOS"
ReferencedContainer = "container:Cuckoo.xcodeproj">

View File

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

View File

@ -0,0 +1,22 @@
<?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

@ -0,0 +1,22 @@
<?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

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

View File

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

View File

@ -0,0 +1,22 @@
<?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

@ -0,0 +1,22 @@
<?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

@ -0,0 +1,22 @@
<?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

@ -0,0 +1,22 @@
<?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

@ -0,0 +1,24 @@
<?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>
<key>NSHumanReadableCopyright</key>
<string>Copyright ©. All rights reserved.</string>
</dict>
</plist>

View File

@ -0,0 +1,24 @@
<?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

@ -0,0 +1,22 @@
<?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

@ -0,0 +1,22 @@
<?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,192 +1,280 @@
GEM
remote: https://rubygems.org/
specs:
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)
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)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.4.0)
cocoapods-deintegrate (>= 1.0.2, < 2.0)
cocoapods-downloader (>= 1.1.3, < 2.0)
cocoapods-core (= 1.11.2)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 1.4.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-stats (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.3.0, < 2.0)
cocoapods-trunk (>= 1.4.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (~> 2.0.1)
fourflusher (>= 2.3.0, < 3.0)
gh_inspector (~> 1.0)
molinillo (~> 0.6.4)
molinillo (~> 0.8.0)
nap (~> 1.0)
ruby-macho (~> 1.1)
xcodeproj (>= 1.5.4, < 2.0)
cocoapods-core (1.4.0)
activesupport (>= 4.0.2, < 6)
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)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
cocoapods-deintegrate (1.0.2)
cocoapods-downloader (1.1.3)
netrc (~> 0.11)
public_suffix (~> 4.0)
typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.5)
cocoapods-downloader (1.5.1)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-stats (1.0.0)
cocoapods-trunk (1.3.0)
cocoapods-search (1.0.1)
cocoapods-trunk (1.6.0)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.1.0)
cocoapods-try (1.2.0)
colored (1.2)
colored2 (3.1.2)
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)
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)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.2.1)
dotenv (2.7.6)
emoji_regex (3.2.3)
escape (0.0.4)
excon (0.58.0)
faraday (0.13.1)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.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)
http-cookie (~> 1.0.0)
faraday_middleware (0.12.2)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.0)
fastlane (2.84.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)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
bundler (>= 1.12.0, < 2.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)
colored
commander-fastlane (>= 4.4.6, < 5.0.0)
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
excon (>= 0.45.0, < 1.0.0)
faraday (~> 0.9)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 0.9)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.13.1, < 0.14.0)
highline (>= 1.7.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
json (< 3.0.0)
mini_magick (~> 4.5.1)
multi_json
multi_xml (~> 0.5)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0)
naturally (~> 2.2)
optparse (~> 0.1.1)
plist (>= 3.1.0, < 4.0.0)
public_suffix (~> 2.0.0)
rubyzip (>= 1.1.0, < 2.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3)
slack-notifier (>= 2.0.0, < 3.0.0)
terminal-notifier (>= 1.6.2, < 2.0.0)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.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.5.2, < 2.0.0)
xcpretty (>= 0.2.4, < 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fourflusher (2.0.1)
ffi (1.15.4)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-api-client (0.13.1)
google-apis-androidpublisher_v3 (0.22.0)
google-apis-core (>= 0.5, < 2.a)
google-apis-core (0.6.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.5)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
googleauth (0.5.3)
faraday (~> 0.12)
jwt (~> 1.4)
logging (~> 2.0)
memoist (~> 0.12)
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)
multi_json (~> 1.11)
os (~> 0.9)
signet (~> 0.7)
highline (1.7.8)
http-cookie (1.0.3)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.5)
domain_name (~> 0.5)
httpclient (2.8.3)
i18n (0.9.1)
i18n (1.8.11)
concurrent-ruby (~> 1.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)
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)
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.2.3)
nanaimo (0.3.0)
nap (1.1.0)
naturally (2.2.1)
netrc (0.11.0)
os (0.9.6)
plist (3.3.0)
public_suffix (2.0.5)
representable (3.0.4)
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)
declarative-option (< 0.2.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.1)
retriable (3.1.2)
rexml (3.2.5)
rouge (2.0.7)
ruby-macho (1.1.0)
rubyzip (1.2.1)
ruby-macho (2.5.1)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
signet (0.7.3)
addressable (~> 2.3)
faraday (~> 0.9)
jwt (~> 1.5)
signet (0.16.1)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.0)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
slack-notifier (2.3.2)
terminal-notifier (1.8.0)
simctl (1.6.8)
CFPropertyList
naturally
terminal-notifier (2.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
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)
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)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.4)
unicode-display_width (1.3.0)
unf_ext (0.0.8.2)
unicode-display_width (1.8.0)
webrick (1.7.0)
word_wrap (1.0.0)
xcodeproj (1.5.4)
CFPropertyList (~> 2.3.3)
xcodeproj (1.21.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.2.3)
xcpretty (0.2.8)
nanaimo (~> 0.3.0)
rexml (~> 3.2.4)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (0.0.4)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
zeitwerk (2.5.1)
PLATFORMS
ruby
arm64-darwin-21
DEPENDENCIES
cocoapods
@ -194,4 +282,4 @@ DEPENDENCIES
json
BUNDLED WITH
1.16.1
2.2.32

View File

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

View File

@ -1 +0,0 @@
4.1

View File

@ -0,0 +1,22 @@
<?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>

View File

@ -1,72 +0,0 @@
{
"autoPin": true,
"pins": [
{
"package": "Clang_C",
"reason": null,
"repositoryURL": "https://github.com/norio-nomura/Clang_C.git",
"version": "1.0.2"
},
{
"package": "Commandant",
"reason": null,
"repositoryURL": "https://github.com/Carthage/Commandant.git",
"version": "0.12.0"
},
{
"package": "FileKit",
"reason": null,
"repositoryURL": "https://github.com/TadeasKriz/FileKit.git",
"version": "4.0.2"
},
{
"package": "PathKit",
"reason": null,
"repositoryURL": "https://github.com/kylef/PathKit.git",
"version": "0.8.0"
},
{
"package": "Result",
"reason": null,
"repositoryURL": "https://github.com/antitypical/Result.git",
"version": "3.2.1"
},
{
"package": "SWXMLHash",
"reason": null,
"repositoryURL": "https://github.com/drmohundro/SWXMLHash.git",
"version": "3.0.4"
},
{
"package": "SourceKit",
"reason": null,
"repositoryURL": "https://github.com/norio-nomura/SourceKit.git",
"version": "1.0.1"
},
{
"package": "SourceKitten",
"reason": null,
"repositoryURL": "https://github.com/jpsim/SourceKitten.git",
"version": "0.17.2"
},
{
"package": "Spectre",
"reason": null,
"repositoryURL": "https://github.com/kylef/Spectre.git",
"version": "0.7.2"
},
{
"package": "Stencil",
"reason": null,
"repositoryURL": "https://github.com/kylef/Stencil.git",
"version": "0.8.0"
},
{
"package": "Yams",
"reason": null,
"repositoryURL": "https://github.com/jpsim/Yams.git",
"version": "0.3.2"
}
],
"version": 1
}

View File

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

View File

@ -1,24 +0,0 @@
// 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"]),
]
)

56
Generator/Project.swift Normal file
View File

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

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

View File

@ -1,17 +1,8 @@
//
// GenerateMocksCommand.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
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)
@ -38,7 +29,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 : mergeInheritance(tokens)
let tokensWithInheritance = options.noInheritance ? tokens : inheritNSObject(mergeInheritance(tokens))
// filter classes/protocols based on the settings passed to the generator
var typeFilters = [] as [(Token) -> Bool]
@ -63,7 +54,7 @@ public struct GenerateMocksCommand: CommandProtocol {
do {
if outputPath.isDirectory {
let inputPaths = inputPathValues.map { Path($0) }
let inputPaths = inputFiles.compactMap { $0.path }.map { Path($0) }
for (inputPath, outputText) in zip(inputPaths, mergedFiles) {
let fileName = options.filePrefix + inputPath.fileName
let outputFile = TextFile(path: outputPath + fileName)
@ -86,6 +77,43 @@ 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
@ -142,19 +170,20 @@ 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,12 +1,6 @@
//
// 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

@ -1,11 +1,3 @@
//
// VersionCommand.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 17/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
import Commandant
import Result

View File

@ -1,12 +1,3 @@
//#!/usr/bin/swift -F Carthage/Build/Mac
//
// main.swift
// SwiftMockGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
import Commandant

View File

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

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

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

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

@ -1,301 +0,0 @@
//
// 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 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 libraryRange = result.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 = result.range.location..<(result.range.location + result.range.length)
print(library)
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)
print(library, component)
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)
}
}
}
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])
}
}

View File

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

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

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

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

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

@ -1,43 +0,0 @@
//
// 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,125 +0,0 @@
//
// 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 && self.returnType == other.returnType
}
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

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

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

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

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

@ -1,59 +0,0 @@
//
// 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,11 +1,3 @@
//
// FileHeaderHandler.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 12/01/16.
// Copyright © 2016 Brightify. All rights reserved.
//
import Foundation
import FileKit

View File

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

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

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

@ -0,0 +1,41 @@
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,17 +1,10 @@
//
// Templates.swift
// CuckooGeneratorFramework
//
// Created by Tadeas Kriz on 11/14/17.
//
import Foundation
struct Templates { }
extension String {
func indented(times: Int = 1) -> String {
let indentation = String(repeating: "\t", count: times)
let indentation = String(repeating: " ", count: times)
return self.components(separatedBy: CharacterSet.newlines).map {
indentation + $0

View File

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

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

@ -0,0 +1,603 @@
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,11 +1,3 @@
//
// 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"
@ -32,3 +24,25 @@ 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,13 +1,7 @@
//
// Attribute.swift
// CuckooGenerator
//
// Created by Tadeas Kriz on 2/25/17.
//
//
import Foundation
public struct Attribute {
public enum Kind: String {
public struct Attribute: Hashable {
public enum Kind: String, Hashable {
case objc = "source.decl.attribute.objc"
case optional = "source.decl.attribute.optional"
case lazy = "source.decl.attribute.lazy"
@ -32,6 +26,26 @@ public struct Attribute {
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

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

View File

@ -1,23 +1,16 @@
//
// 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 struct ClassDeclaration: ContainerToken, HasAccessibility {
public let implementation: Bool = true
public let inheritedTypes: [InheritanceDeclaration]
public let attributes: [Attribute]
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 hasNoArgInit: Bool {
return initializers.filter { $0.parameters.isEmpty }.isEmpty
}
@ -25,6 +18,7 @@ public struct ClassDeclaration: ContainerToken {
public func replace(children tokens: [Token]) -> ClassDeclaration {
return ClassDeclaration(
name: self.name,
parent: self.parent,
accessibility: self.accessibility,
range: self.range,
nameRange: self.nameRange,
@ -32,7 +26,8 @@ public struct ClassDeclaration: ContainerToken {
initializers: self.initializers,
children: tokens,
inheritedTypes: self.inheritedTypes,
attributes: self.attributes)
attributes: self.attributes,
genericParameters: self.genericParameters)
}
public func isEqual(to other: Token) -> Bool {

View File

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

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

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

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

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

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

View File

@ -0,0 +1,19 @@
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,11 +1,3 @@
//
// Import.swift
// CuckooGenerator
//
// Created by Filip Dolnik on 17.06.16.
// Copyright © 2016 Brightify. All rights reserved.
//
public struct Import: Token {
public enum Importee: CustomStringConvertible {
case library(name: String)

View File

@ -1,11 +1,3 @@
//
// 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
@ -14,4 +6,4 @@ public struct InheritanceDeclaration: Token {
guard let other = other as? InheritanceDeclaration else { return false }
return self.name == other.name
}
}
}

View File

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

@ -0,0 +1,51 @@
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,11 +1,3 @@
//
// 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,16 +1,12 @@
//
// 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,158 @@
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

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

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

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

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

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

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

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

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

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

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

@ -0,0 +1,76 @@
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)
}

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