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 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
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in swiftfake.gemspec
4
+ gemspec
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
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/swiftfake ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "swiftfake"
4
+
5
+ input_file = ARGF.argv[0]
6
+ runner = Swiftfake::Runner.new(args: {input: input_file})
7
+ puts runner.run
@@ -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,11 @@
1
+ module Swiftfake
2
+ class SwiftClass
3
+ attr_reader :name, :access, :functions
4
+
5
+ def initialize(name:, access:, functions:)
6
+ @name = name
7
+ @access = access
8
+ @functions = functions
9
+ end
10
+ end
11
+ 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
@@ -0,0 +1,3 @@
1
+ module Swiftfake
2
+ VERSION = "0.2.0"
3
+ 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: []