swiftfake 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/swiftfake +7 -0
- data/lib/swiftfake/function_decorator.rb +88 -0
- data/lib/swiftfake/presenter.rb +31 -0
- data/lib/swiftfake/renderer.rb +17 -0
- data/lib/swiftfake/source_kit_parser.rb +100 -0
- data/lib/swiftfake/source_reader.rb +12 -0
- data/lib/swiftfake/swift_class.rb +11 -0
- data/lib/swiftfake/swift_function.rb +15 -0
- data/lib/swiftfake/version.rb +3 -0
- data/lib/swiftfake.rb +31 -0
- data/lib/template.erb +25 -0
- data/swiftfake.gemspec +23 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 51fa9134419ca08be43c69842e6d78ccb6ccbb0c
|
4
|
+
data.tar.gz: 2c153d41523de881c41ff5957d53bf6f6364260a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1b05f5f61147a2e31445941b2dcae2dfe8ae47a40596a8d2fa11326cf57c82dc7042072c248e34aef2ea9c2a8a75df6eb44d5750fcfa9f14e54ef8353e8f1193
|
7
|
+
data.tar.gz: b5b81a272d6d494782bb709478de6a87213b18d110254dfb2123241fc05512ad338d796a6294ca089139bb6bfe666e0054c6fa0c0f2f6427310a7cd9d83edaaa
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Swiftfake
|
2
|
+
|
3
|
+
Generate test fakes from Swift code. The fakes allow you to:
|
4
|
+
|
5
|
+
- Verify how many times a function was called
|
6
|
+
- Verify what arguments were received
|
7
|
+
- Return a canned value
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
Pass a Swift file path and the fake will be printed to STDOUT:
|
12
|
+
|
13
|
+
```bash
|
14
|
+
swiftfake ./app/MySwiftClass.swift
|
15
|
+
```
|
16
|
+
|
17
|
+
You could then pipe the output:
|
18
|
+
|
19
|
+
```bash
|
20
|
+
# To clipboard
|
21
|
+
swiftfake ./app/MySwiftClass.swift | pbcopy
|
22
|
+
|
23
|
+
# To a file
|
24
|
+
swiftfake ./app/MySwiftClass.swift > ./test/FakeMySwiftClass.swift
|
25
|
+
```
|
26
|
+
|
27
|
+
## Requirements
|
28
|
+
|
29
|
+
- Ruby 2.1+ (run `ruby -v` to check)
|
30
|
+
- (SourceKitten)[https://github.com/jpsim/SourceKitten] (`brew install sourcekitten`)
|
31
|
+
|
32
|
+
## Notes
|
33
|
+
|
34
|
+
This gem is still in an alpha state.
|
35
|
+
|
36
|
+
Roadmap:
|
37
|
+
|
38
|
+
- Copy across @import statements from source
|
39
|
+
- Fake Protocol implementations
|
40
|
+
- Implement Bright Futures support
|
41
|
+
- Handling multiple classes/protocols in the Swift source file
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "swiftfake"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/swiftfake
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
module Swiftfake
|
2
|
+
module FunctionDecorator
|
3
|
+
def signature
|
4
|
+
"override #{full_name}"
|
5
|
+
end
|
6
|
+
|
7
|
+
def call_count
|
8
|
+
"#{name}CallCount"
|
9
|
+
end
|
10
|
+
|
11
|
+
def call_count_declaration
|
12
|
+
"#{var_prefix} #{call_count} = 0"
|
13
|
+
end
|
14
|
+
|
15
|
+
def has_args?
|
16
|
+
arguments.any?
|
17
|
+
end
|
18
|
+
|
19
|
+
def args_store_append
|
20
|
+
store_name = args_store_name
|
21
|
+
|
22
|
+
if arguments.count == 1
|
23
|
+
"#{store_name}.append(#{arguments.first.name})"
|
24
|
+
else
|
25
|
+
arg_names = arguments.map(&:name).join(", ")
|
26
|
+
tuple = "(#{arg_names})"
|
27
|
+
"#{store_name}.append(#{tuple})"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def args_store_declaration
|
32
|
+
"#{var_prefix} #{args_store_name} = [#{args_store_type}]()"
|
33
|
+
end
|
34
|
+
|
35
|
+
def returns?
|
36
|
+
!return_value.nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
def return_value_declaration
|
40
|
+
"return #{return_value_var_name}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def return_value_store_declaration
|
44
|
+
"#{var_prefix} #{return_value_var_name} = #{return_value_initialized}"
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def var_prefix
|
50
|
+
full_name.include?("class") ? "static var" : "var"
|
51
|
+
end
|
52
|
+
|
53
|
+
def args_store_type
|
54
|
+
if arguments.count == 1
|
55
|
+
arguments.first.type
|
56
|
+
else
|
57
|
+
args_as_tuple
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def args_store_name
|
62
|
+
"#{name}ArgsForCall"
|
63
|
+
end
|
64
|
+
|
65
|
+
def args_as_tuple
|
66
|
+
args = arguments
|
67
|
+
.map{ |a| "#{a.name}: #{a.type}" }
|
68
|
+
.join(', ')
|
69
|
+
"(#{args})"
|
70
|
+
end
|
71
|
+
|
72
|
+
def return_value_var_name
|
73
|
+
"#{name}ReturnValue"
|
74
|
+
end
|
75
|
+
|
76
|
+
def return_value_initialized
|
77
|
+
return "#{return_value}()" unless return_value_is_tuple?
|
78
|
+
|
79
|
+
tuple = return_value.gsub(')', '())')
|
80
|
+
tuple.gsub(',', '(),')
|
81
|
+
end
|
82
|
+
|
83
|
+
def return_value_is_tuple?
|
84
|
+
reg = /^\(.+,.+\)$/
|
85
|
+
(return_value =~ reg) == 0
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'swiftfake/function_decorator'
|
2
|
+
|
3
|
+
module Swiftfake
|
4
|
+
class Presenter
|
5
|
+
attr_reader :swift_class
|
6
|
+
|
7
|
+
def initialize(swift_class)
|
8
|
+
@swift_class = swift_class
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_binding
|
12
|
+
binding()
|
13
|
+
end
|
14
|
+
|
15
|
+
def fake_class_signature
|
16
|
+
"#{swift_class.access} class Fake#{swift_class.name}: #{swift_class.name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def functions
|
20
|
+
@functions ||= swift_class.functions.map { |f| f.extend(FunctionDecorator) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def functions_with_args
|
24
|
+
functions.select(&:has_args?)
|
25
|
+
end
|
26
|
+
|
27
|
+
def functions_with_return_value
|
28
|
+
functions.select(&:returns?)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Swiftfake
|
4
|
+
class Renderer
|
5
|
+
def output(presenter)
|
6
|
+
erb = ERB.new(template, nil, '-')
|
7
|
+
erb.result(presenter.get_binding)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def template
|
13
|
+
path = File.expand_path("../../template.erb", __FILE__)
|
14
|
+
File.read(path)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'swiftfake/swift_class'
|
3
|
+
require 'swiftfake/swift_function'
|
4
|
+
|
5
|
+
module Swiftfake
|
6
|
+
class SourceKitParser
|
7
|
+
|
8
|
+
attr_reader :source_file, :structure_json
|
9
|
+
|
10
|
+
def parse(source_file, raw_structure_json)
|
11
|
+
@source_file = source_file
|
12
|
+
@structure_json = JSON.parse(raw_structure_json)
|
13
|
+
|
14
|
+
SwiftClass.new(
|
15
|
+
name: class_name,
|
16
|
+
access: access_level,
|
17
|
+
functions: parse_functions
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def class_name
|
24
|
+
first_entity.fetch("key.name", nil)
|
25
|
+
end
|
26
|
+
|
27
|
+
def access_level
|
28
|
+
raw_accessibility = first_entity.fetch("key.accessibility", nil)
|
29
|
+
raw_accessibility.split('.').last unless raw_accessibility.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
def first_entity
|
33
|
+
structure_json
|
34
|
+
.fetch("key.substructure", [])
|
35
|
+
.fetch(0, {})
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_functions
|
39
|
+
function_declarations = first_entity
|
40
|
+
.fetch("key.substructure", [])
|
41
|
+
.select{ |part| part.fetch("key.kind", "").include? "function.method" }
|
42
|
+
.map{|method| find_raw_method_decl(method) }
|
43
|
+
.compact
|
44
|
+
|
45
|
+
function_declarations
|
46
|
+
.map {|f| FunctionParser.new.parse(f) }
|
47
|
+
.compact
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_raw_method_decl(method)
|
51
|
+
start_offset = method["key.offset"]
|
52
|
+
end_offset = method["key.bodyoffset"]
|
53
|
+
|
54
|
+
return nil if start_offset.nil? || end_offset.nil?
|
55
|
+
|
56
|
+
end_offset -= 1
|
57
|
+
source_file[start_offset...end_offset]
|
58
|
+
end
|
59
|
+
|
60
|
+
class FunctionParser
|
61
|
+
|
62
|
+
def parse(function_line)
|
63
|
+
return nil unless can_override?(function_line)
|
64
|
+
|
65
|
+
/func (?<name>.*)\(/ =~ function_line
|
66
|
+
/(?<access>public|internal|private)/ =~ function_line
|
67
|
+
/->\s(?<return_value>.+)$/ =~ function_line
|
68
|
+
|
69
|
+
return_value.strip! unless return_value.nil?
|
70
|
+
|
71
|
+
SwiftFunction.new(
|
72
|
+
full_name: function_line.strip,
|
73
|
+
name: name,
|
74
|
+
access: access,
|
75
|
+
arguments: parse_args(function_line),
|
76
|
+
return_value: return_value
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def can_override?(function_line)
|
83
|
+
!function_line.include?('final') && !function_line.include?('private')
|
84
|
+
end
|
85
|
+
|
86
|
+
def parse_args(function_line)
|
87
|
+
/func .*\((?<raw_args>.+)\)/ =~ function_line
|
88
|
+
return [] if raw_args.nil?
|
89
|
+
|
90
|
+
raw_args
|
91
|
+
.split(",")
|
92
|
+
.map { |raw|
|
93
|
+
/(?<name>[\w]+):\s(?<type>.+)/ =~ raw
|
94
|
+
SwiftFunction::Argument.new(name.strip, type.strip)
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Swiftfake
|
4
|
+
class SourceReader
|
5
|
+
def read_file(source_file)
|
6
|
+
source = File.read(source_file)
|
7
|
+
structure_json, status = Open3.capture2("sourcekitten structure --file #{source_file}")
|
8
|
+
|
9
|
+
[source, structure_json] if status.success?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Swiftfake
|
2
|
+
class SwiftFunction
|
3
|
+
Argument = Struct.new(:name, :type)
|
4
|
+
|
5
|
+
attr_reader :full_name, :name, :access, :arguments, :return_value
|
6
|
+
|
7
|
+
def initialize(full_name:, name:, access:, arguments:, return_value:)
|
8
|
+
@full_name = full_name
|
9
|
+
@name = name
|
10
|
+
@access = access
|
11
|
+
@arguments = arguments
|
12
|
+
@return_value = return_value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/swiftfake.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'swiftfake/version'
|
2
|
+
require 'swiftfake/source_reader'
|
3
|
+
require 'swiftfake/source_kit_parser'
|
4
|
+
require 'swiftfake/presenter'
|
5
|
+
require 'swiftfake/renderer'
|
6
|
+
|
7
|
+
module Swiftfake
|
8
|
+
|
9
|
+
Config = Struct.new(:source_reader, :parser_klass, :presenter_klass, :renderer) do
|
10
|
+
def self.create(source_reader: SourceReader.new, parser_klass: SourceKitParser, presenter_klass: Presenter, renderer: Renderer.new)
|
11
|
+
self.new(source_reader, parser_klass, presenter_klass, renderer)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Runner
|
16
|
+
attr_reader :args, :config
|
17
|
+
|
18
|
+
def initialize(args:, config: Config.create)
|
19
|
+
@args = args
|
20
|
+
@config = config
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
source_file, structure_json = config.source_reader.read_file(args[:input])
|
25
|
+
parser = config.parser_klass.new
|
26
|
+
swift_class = parser.parse(source_file, structure_json)
|
27
|
+
presenter = config.presenter_klass.new(swift_class)
|
28
|
+
config.renderer.output(presenter)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/template.erb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
<%= fake_class_signature %> {
|
2
|
+
|
3
|
+
<% functions.each do |f| -%>
|
4
|
+
<%= f.signature %> {
|
5
|
+
<%= f.call_count %> += 1
|
6
|
+
<% if f.has_args? %><%= f.args_store_append %><% end %>
|
7
|
+
<% if f.returns? %><%= f.return_value_declaration %><% end %>
|
8
|
+
}
|
9
|
+
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
// MARK: - Fake Helpers
|
13
|
+
|
14
|
+
<% functions.each do |f| -%>
|
15
|
+
<%= f.call_count_declaration %>
|
16
|
+
<% end %>
|
17
|
+
|
18
|
+
<% functions_with_args.each do |f| -%>
|
19
|
+
<%= f.args_store_declaration %>
|
20
|
+
<% end %>
|
21
|
+
|
22
|
+
<% functions_with_return_value.each do |f| -%>
|
23
|
+
<%= f.return_value_store_declaration %>
|
24
|
+
<% end %>
|
25
|
+
}
|
data/swiftfake.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'swiftfake/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "swiftfake"
|
8
|
+
spec.version = Swiftfake::VERSION
|
9
|
+
spec.authors = ["odlp"]
|
10
|
+
spec.email = ["oliverp@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = "Generate test fakes from Swift code."
|
13
|
+
spec.homepage = "https://github.com/odlp/swiftfake"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.bindir = "exe"
|
17
|
+
spec.executables = ["swiftfake"]
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: swiftfake
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- odlp
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- oliverp@gmail.com
|
58
|
+
executables:
|
59
|
+
- swiftfake
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- ".rspec"
|
65
|
+
- ".travis.yml"
|
66
|
+
- Gemfile
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- bin/console
|
70
|
+
- bin/setup
|
71
|
+
- exe/swiftfake
|
72
|
+
- lib/swiftfake.rb
|
73
|
+
- lib/swiftfake/function_decorator.rb
|
74
|
+
- lib/swiftfake/presenter.rb
|
75
|
+
- lib/swiftfake/renderer.rb
|
76
|
+
- lib/swiftfake/source_kit_parser.rb
|
77
|
+
- lib/swiftfake/source_reader.rb
|
78
|
+
- lib/swiftfake/swift_class.rb
|
79
|
+
- lib/swiftfake/swift_function.rb
|
80
|
+
- lib/swiftfake/version.rb
|
81
|
+
- lib/template.erb
|
82
|
+
- swiftfake.gemspec
|
83
|
+
homepage: https://github.com/odlp/swiftfake
|
84
|
+
licenses: []
|
85
|
+
metadata: {}
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 2.5.1
|
103
|
+
signing_key:
|
104
|
+
specification_version: 4
|
105
|
+
summary: Generate test fakes from Swift code.
|
106
|
+
test_files: []
|