motion-spec 0.1.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 +15 -0
- data/.codeclimate.yml +12 -0
- data/.gitignore +38 -0
- data/.rubocop.yml +58 -0
- data/.travis.yml +18 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +21 -0
- data/LICENSE +23 -0
- data/README.md +37 -0
- data/Rakefile +14 -0
- data/app/app_delegate.rb +6 -0
- data/lib/motion-spec.rb +77 -0
- data/lib/motion-spec/context.rb +135 -0
- data/lib/motion-spec/core.rb +92 -0
- data/lib/motion-spec/error.rb +11 -0
- data/lib/motion-spec/extensions/boolean.rb +13 -0
- data/lib/motion-spec/extensions/exception.rb +2 -0
- data/lib/motion-spec/extensions/kernel.rb +13 -0
- data/lib/motion-spec/extensions/numeric.rb +6 -0
- data/lib/motion-spec/extensions/object.rb +6 -0
- data/lib/motion-spec/extensions/proc.rb +25 -0
- data/lib/motion-spec/output/colorized.rb +36 -0
- data/lib/motion-spec/output/fast.rb +21 -0
- data/lib/motion-spec/output/knock.rb +23 -0
- data/lib/motion-spec/output/ruby_mine.rb +86 -0
- data/lib/motion-spec/output/spec_dox.rb +30 -0
- data/lib/motion-spec/output/tap.rb +39 -0
- data/lib/motion-spec/output/test_unit.rb +24 -0
- data/lib/motion-spec/platform.rb +8 -0
- data/lib/motion-spec/should.rb +88 -0
- data/lib/motion-spec/specification.rb +208 -0
- data/lib/motion-spec/version.rb +5 -0
- data/motion-spec.gemspec +21 -0
- metadata +104 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module MotionSpec
|
3
|
+
class Should
|
4
|
+
# Kills ==, ===, =~, eql?, equal?, frozen?, instance_of?, is_a?,
|
5
|
+
# kind_of?, nil?, respond_to?, tainted?
|
6
|
+
#
|
7
|
+
# The reason that these methods are killed is so that method_missing
|
8
|
+
# will catch them and push them through `satisfy`. The satisfy method
|
9
|
+
# handles chaining and negation (eg .should.not.eq).
|
10
|
+
instance_methods.each { |name| undef_method name if name =~ /\?|^\W+$/ }
|
11
|
+
|
12
|
+
def initialize(object)
|
13
|
+
@object = object
|
14
|
+
@negated = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def not(*args, &block)
|
18
|
+
@negated = !@negated
|
19
|
+
|
20
|
+
return self if args.empty?
|
21
|
+
|
22
|
+
be(*args, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def be(*args, &block)
|
26
|
+
return self if args.empty?
|
27
|
+
|
28
|
+
block = args.shift unless block_given?
|
29
|
+
satisfy(*args, &block)
|
30
|
+
end
|
31
|
+
alias_method :a, :be
|
32
|
+
alias_method :an, :be
|
33
|
+
|
34
|
+
def satisfy(*args, &block)
|
35
|
+
if args.size == 1 && String === args.first
|
36
|
+
description = args.shift
|
37
|
+
else
|
38
|
+
description = ''
|
39
|
+
end
|
40
|
+
|
41
|
+
result = yield(@object, *args)
|
42
|
+
|
43
|
+
if Counter[:depth] > 0
|
44
|
+
Counter[:requirements] += 1
|
45
|
+
flunk(description) unless @negated ^ result
|
46
|
+
result
|
47
|
+
else
|
48
|
+
@negated ? !result : !!result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing(name, *args, &block)
|
53
|
+
name = "#{name}?" if name.to_s =~ /\w[^?]\z/
|
54
|
+
|
55
|
+
desc = @negated ? 'not ' : ''
|
56
|
+
desc << @object.inspect << '.' << name.to_s
|
57
|
+
desc << '(' << args.map(&:inspect).join(', ') << ') failed'
|
58
|
+
|
59
|
+
satisfy(desc) do |object|
|
60
|
+
object.__send__(name, *args, &block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def equal(value)
|
65
|
+
self == value
|
66
|
+
end
|
67
|
+
alias_method :eq, :equal
|
68
|
+
|
69
|
+
def match(value)
|
70
|
+
self =~ value
|
71
|
+
end
|
72
|
+
|
73
|
+
def identical_to(value)
|
74
|
+
self.equal? value
|
75
|
+
end
|
76
|
+
alias_method :same_as, :identical_to
|
77
|
+
|
78
|
+
# TODO: This was in the MacBacon specs and kept for backwards compatibilty
|
79
|
+
# but I've never seen this used before so possibly kill this.
|
80
|
+
def eq=(value)
|
81
|
+
self === value
|
82
|
+
end
|
83
|
+
|
84
|
+
def flunk(reason = 'Flunked')
|
85
|
+
raise Error.new(:failed, reason)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module MotionSpec
|
3
|
+
class Specification
|
4
|
+
MULTIPLE_POSTPONES_ERROR_MESSAGE =
|
5
|
+
"Only one indefinite `wait' block at the same time is allowed!"
|
6
|
+
|
7
|
+
attr_reader :description
|
8
|
+
|
9
|
+
def initialize(context, description, block, before_filters, after_filters)
|
10
|
+
@context = context
|
11
|
+
@description = description
|
12
|
+
@block = block
|
13
|
+
@before_filters = before_filters.dup
|
14
|
+
@after_filters = after_filters.dup
|
15
|
+
|
16
|
+
@postponed_blocks_count = 0
|
17
|
+
@ran_spec_block = false
|
18
|
+
@ran_after_filters = false
|
19
|
+
@exception_occurred = false
|
20
|
+
@error = ''
|
21
|
+
end
|
22
|
+
|
23
|
+
def postponed?
|
24
|
+
@postponed_blocks_count != 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def run_before_filters
|
28
|
+
execute_block { @before_filters.each { |f| @context.instance_eval(&f) } }
|
29
|
+
end
|
30
|
+
|
31
|
+
def run_spec_block
|
32
|
+
@ran_spec_block = true
|
33
|
+
# If an exception occurred, we definitely don't need to perform the actual spec anymore
|
34
|
+
unless @exception_occurred
|
35
|
+
execute_block { @context.instance_eval(&@block) }
|
36
|
+
end
|
37
|
+
finish_spec unless postponed?
|
38
|
+
end
|
39
|
+
|
40
|
+
def run_after_filters
|
41
|
+
@ran_after_filters = true
|
42
|
+
execute_block { @after_filters.each { |f| @context.instance_eval(&f) } }
|
43
|
+
end
|
44
|
+
|
45
|
+
def run
|
46
|
+
MotionSpec.handle_requirement_begin(@description)
|
47
|
+
Counter[:depth] += 1
|
48
|
+
run_before_filters
|
49
|
+
@number_of_requirements_before = Counter[:requirements]
|
50
|
+
run_spec_block unless postponed?
|
51
|
+
end
|
52
|
+
|
53
|
+
def schedule_block(seconds, &block)
|
54
|
+
# If an exception occurred, we definitely don't need to schedule any more blocks
|
55
|
+
return if @exception_occurred
|
56
|
+
|
57
|
+
@postponed_blocks_count += 1
|
58
|
+
if Platform.android?
|
59
|
+
sleep seconds
|
60
|
+
run_postponed_block(block)
|
61
|
+
else
|
62
|
+
performSelector('run_postponed_block:', withObject: block, afterDelay: seconds)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def postpone_block(timeout = 1, &block)
|
67
|
+
# If an exception occurred, we definitely don't need to schedule any more blocks
|
68
|
+
return if @exception_occurred
|
69
|
+
raise MULTIPLE_POSTPONES_ERROR_MESSAGE if @postponed_block
|
70
|
+
|
71
|
+
@postponed_blocks_count += 1
|
72
|
+
@postponed_block = block
|
73
|
+
|
74
|
+
return performSelector(
|
75
|
+
'postponed_block_timeout_exceeded',
|
76
|
+
withObject: nil,
|
77
|
+
afterDelay: timeout
|
78
|
+
) unless Platform.android?
|
79
|
+
|
80
|
+
sleep timeout
|
81
|
+
postponed_block_timeout_exceeded
|
82
|
+
end
|
83
|
+
|
84
|
+
def postpone_block_until_change(object_to_observe, key_path, timeout = 1, &block)
|
85
|
+
# If an exception occurred, we definitely don't need to schedule any more blocks
|
86
|
+
return if @exception_occurred
|
87
|
+
raise MULTIPLE_POSTPONES_ERROR_MESSAGE if @postponed_block
|
88
|
+
|
89
|
+
@postponed_blocks_count += 1
|
90
|
+
@postponed_block = block
|
91
|
+
@observed_object_and_key_path = [object_to_observe, key_path]
|
92
|
+
object_to_observe.addObserver(self, forKeyPath: key_path, options: 0, context: nil)
|
93
|
+
|
94
|
+
return performSelector(
|
95
|
+
'postponed_change_block_timeout_exceeded',
|
96
|
+
withObject: nil,
|
97
|
+
afterDelay: timeout
|
98
|
+
) unless Platform.android?
|
99
|
+
|
100
|
+
sleep timeout
|
101
|
+
postponed_change_block_timeout_exceeded
|
102
|
+
end
|
103
|
+
|
104
|
+
def observeValueForKeyPath(key_path, ofObject:object, change:_, context:__)
|
105
|
+
resume
|
106
|
+
end
|
107
|
+
|
108
|
+
def postponed_change_block_timeout_exceeded
|
109
|
+
remove_observer!
|
110
|
+
postponed_block_timeout_exceeded
|
111
|
+
end
|
112
|
+
|
113
|
+
def remove_observer!
|
114
|
+
if @observed_object_and_key_path
|
115
|
+
object, key_path = @observed_object_and_key_path
|
116
|
+
object.removeObserver(self, forKeyPath: key_path)
|
117
|
+
@observed_object_and_key_path = nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def postponed_block_timeout_exceeded
|
122
|
+
cancel_scheduled_requests!
|
123
|
+
execute_block { raise Error.new(:failed, "timeout exceeded: #{@context.name} - #{@description}") }
|
124
|
+
@postponed_blocks_count = 0
|
125
|
+
finish_spec
|
126
|
+
end
|
127
|
+
|
128
|
+
def resume
|
129
|
+
unless Platform.android?
|
130
|
+
NSObject.cancelPreviousPerformRequestsWithTarget(self, selector: 'postponed_block_timeout_exceeded', object: nil)
|
131
|
+
NSObject.cancelPreviousPerformRequestsWithTarget(self, selector: 'postponed_change_block_timeout_exceeded', object: nil)
|
132
|
+
end
|
133
|
+
remove_observer!
|
134
|
+
block, @postponed_block = @postponed_block, nil
|
135
|
+
run_postponed_block(block)
|
136
|
+
end
|
137
|
+
|
138
|
+
def run_postponed_block(block)
|
139
|
+
# If an exception occurred, we definitely don't need execute any more blocks
|
140
|
+
execute_block(&block) unless @exception_occurred
|
141
|
+
@postponed_blocks_count -= 1
|
142
|
+
unless postponed?
|
143
|
+
if @ran_after_filters
|
144
|
+
exit_spec
|
145
|
+
elsif @ran_spec_block
|
146
|
+
finish_spec
|
147
|
+
else
|
148
|
+
run_spec_block
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def finish_spec
|
154
|
+
if !@exception_occurred && Counter[:requirements] == @number_of_requirements_before
|
155
|
+
# the specification did not contain any requirements, so it flunked
|
156
|
+
execute_block { raise Error.new(:missing, "empty specification: #{@context.name} #{@description}") }
|
157
|
+
end
|
158
|
+
run_after_filters
|
159
|
+
exit_spec unless postponed?
|
160
|
+
end
|
161
|
+
|
162
|
+
def cancel_scheduled_requests!
|
163
|
+
unless Platform.android?
|
164
|
+
NSObject.cancelPreviousPerformRequestsWithTarget(@context)
|
165
|
+
NSObject.cancelPreviousPerformRequestsWithTarget(self)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def exit_spec
|
170
|
+
cancel_scheduled_requests!
|
171
|
+
Counter[:depth] -= 1
|
172
|
+
MotionSpec.handle_requirement_end(@error)
|
173
|
+
@context.specification_did_finish(self)
|
174
|
+
end
|
175
|
+
|
176
|
+
def execute_block
|
177
|
+
yield
|
178
|
+
rescue Object => e
|
179
|
+
@exception_occurred = true
|
180
|
+
|
181
|
+
if e.is_a?(Exception)
|
182
|
+
ErrorLog << "#{e.class}: #{e.message}\n"
|
183
|
+
lines = $DEBUG ? e.backtrace : e.backtrace.find_all { |line| line !~ /bin\/macbacon|\/mac_bacon\.rb:\d+/ }
|
184
|
+
lines.each_with_index { |line, i|
|
185
|
+
ErrorLog << "\t#{line}#{i == 0 ? ": #{@context.name} - #{@description}" : ''}\n"
|
186
|
+
}
|
187
|
+
ErrorLog << "\n"
|
188
|
+
else
|
189
|
+
if defined?(NSException)
|
190
|
+
# Pure NSException.
|
191
|
+
ErrorLog << "#{e.name}: #{e.reason}\n"
|
192
|
+
else
|
193
|
+
# Pure Java exception.
|
194
|
+
ErrorLog << "#{e.class.toString} : #{e.getMessage}"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
@error =
|
199
|
+
if e.is_a? Error
|
200
|
+
Counter[e.count_as] += 1
|
201
|
+
"#{e.count_as.to_s.upcase} - #{e}"
|
202
|
+
else
|
203
|
+
Counter[:errors] += 1
|
204
|
+
"ERROR: #{e.class} - #{e}"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
data/motion-spec.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path('../lib/motion-spec/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = 'motion-spec'
|
6
|
+
spec.version = MotionSpec::VERSION
|
7
|
+
spec.authors = ['Jonathan Bender']
|
8
|
+
spec.email = ['jlbender@gmail.com']
|
9
|
+
|
10
|
+
spec.summary = 'RubyMotion derivative of Bacon, which is a derivative of RSpec'
|
11
|
+
spec.description = 'RubyMotion derivative of Bacon, which is a derivative of RSpec'
|
12
|
+
spec.homepage = 'https://github.com/jbender/motion-spec'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.require_paths = ['lib']
|
17
|
+
|
18
|
+
spec.add_dependency 'motion-require', '>= 0.0.6'
|
19
|
+
|
20
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: motion-spec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jonathan Bender
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: motion-require
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.6
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.6
|
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
|
+
description: RubyMotion derivative of Bacon, which is a derivative of RSpec
|
42
|
+
email:
|
43
|
+
- jlbender@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .codeclimate.yml
|
49
|
+
- .gitignore
|
50
|
+
- .rubocop.yml
|
51
|
+
- .travis.yml
|
52
|
+
- Gemfile
|
53
|
+
- Gemfile.lock
|
54
|
+
- LICENSE
|
55
|
+
- README.md
|
56
|
+
- Rakefile
|
57
|
+
- app/app_delegate.rb
|
58
|
+
- lib/motion-spec.rb
|
59
|
+
- lib/motion-spec/context.rb
|
60
|
+
- lib/motion-spec/core.rb
|
61
|
+
- lib/motion-spec/error.rb
|
62
|
+
- lib/motion-spec/extensions/boolean.rb
|
63
|
+
- lib/motion-spec/extensions/exception.rb
|
64
|
+
- lib/motion-spec/extensions/kernel.rb
|
65
|
+
- lib/motion-spec/extensions/numeric.rb
|
66
|
+
- lib/motion-spec/extensions/object.rb
|
67
|
+
- lib/motion-spec/extensions/proc.rb
|
68
|
+
- lib/motion-spec/output/colorized.rb
|
69
|
+
- lib/motion-spec/output/fast.rb
|
70
|
+
- lib/motion-spec/output/knock.rb
|
71
|
+
- lib/motion-spec/output/ruby_mine.rb
|
72
|
+
- lib/motion-spec/output/spec_dox.rb
|
73
|
+
- lib/motion-spec/output/tap.rb
|
74
|
+
- lib/motion-spec/output/test_unit.rb
|
75
|
+
- lib/motion-spec/platform.rb
|
76
|
+
- lib/motion-spec/should.rb
|
77
|
+
- lib/motion-spec/specification.rb
|
78
|
+
- lib/motion-spec/version.rb
|
79
|
+
- motion-spec.gemspec
|
80
|
+
homepage: https://github.com/jbender/motion-spec
|
81
|
+
licenses:
|
82
|
+
- MIT
|
83
|
+
metadata: {}
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ! '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
requirements: []
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 2.4.8
|
101
|
+
signing_key:
|
102
|
+
specification_version: 4
|
103
|
+
summary: RubyMotion derivative of Bacon, which is a derivative of RSpec
|
104
|
+
test_files: []
|