muack 1.2.0 → 1.5.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 +5 -5
- data/.travis.yml +19 -9
- data/CHANGES.md +47 -0
- data/Gemfile +0 -4
- data/README.md +162 -65
- data/Rakefile +3 -4
- data/lib/muack.rb +43 -11
- data/lib/muack/block.rb +4 -15
- data/lib/muack/block_26.rb +16 -0
- data/lib/muack/block_27.rb +16 -0
- data/lib/muack/coat.rb +1 -1
- data/lib/muack/definition.rb +4 -3
- data/lib/muack/error.rb +6 -0
- data/lib/muack/failure.rb +5 -4
- data/lib/muack/mock.rb +134 -68
- data/lib/muack/satisfying.rb +195 -0
- data/lib/muack/spy.rb +18 -4
- data/lib/muack/stub.rb +7 -6
- data/lib/muack/test.rb +42 -11
- data/lib/muack/version.rb +1 -1
- data/muack.gemspec +65 -55
- data/task/README.md +8 -8
- data/task/gemgem.rb +34 -7
- data/test/test_any_instance_of.rb +16 -2
- data/test/test_from_readme.rb +6 -8
- data/test/test_keyargs.rb +111 -0
- data/test/test_modifier.rb +6 -6
- data/test/test_prepend.rb +121 -0
- data/test/test_proxy.rb +19 -4
- data/test/{test_satisfy.rb → test_satisfying.rb} +216 -80
- data/test/test_spy.rb +149 -0
- data/test/test_stub.rb +0 -95
- data/test/test_visibility.rb +120 -0
- metadata +20 -11
- data/lib/muack/satisfy.rb +0 -100
data/task/README.md
CHANGED
@@ -4,16 +4,16 @@
|
|
4
4
|
|
5
5
|
Provided tasks:
|
6
6
|
|
7
|
-
rake clean #
|
7
|
+
rake clean # Trash ignored files
|
8
8
|
rake gem:build # Build gem
|
9
9
|
rake gem:install # Install gem
|
10
10
|
rake gem:release # Release gem
|
11
11
|
rake gem:spec # Generate gemspec
|
12
|
-
rake test # Run tests
|
12
|
+
rake test # Run tests
|
13
13
|
|
14
14
|
## REQUIREMENTS:
|
15
15
|
|
16
|
-
* Tested with MRI (official CRuby)
|
16
|
+
* Tested with MRI (official CRuby) and JRuby.
|
17
17
|
|
18
18
|
## INSTALLATION:
|
19
19
|
|
@@ -23,13 +23,13 @@ And in Rakefile:
|
|
23
23
|
|
24
24
|
``` ruby
|
25
25
|
begin
|
26
|
-
require "#{
|
26
|
+
require "#{__dir__}/task/gemgem"
|
27
27
|
rescue LoadError
|
28
|
-
sh 'git submodule update --init'
|
28
|
+
sh 'git submodule update --init --recursive'
|
29
29
|
exec Gem.ruby, '-S', $PROGRAM_NAME, *ARGV
|
30
30
|
end
|
31
31
|
|
32
|
-
Gemgem.init(
|
32
|
+
Gemgem.init(__dir__, :submodules => %w[your-dep]) do |s|
|
33
33
|
s.name = 'your-gem'
|
34
34
|
s.version = '0.1.0'
|
35
35
|
end
|
@@ -37,9 +37,9 @@ end
|
|
37
37
|
|
38
38
|
## LICENSE:
|
39
39
|
|
40
|
-
Apache License 2.0
|
40
|
+
Apache License 2.0 (Apache-2.0)
|
41
41
|
|
42
|
-
Copyright (c) 2011-
|
42
|
+
Copyright (c) 2011-2019, Lin Jen-Shin (godfat)
|
43
43
|
|
44
44
|
Licensed under the Apache License, Version 2.0 (the "License");
|
45
45
|
you may not use this file except in compliance with the License.
|
data/task/gemgem.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
module Gemgem
|
3
3
|
class << self
|
4
|
-
attr_accessor :dir, :spec, :spec_create
|
4
|
+
attr_accessor :dir, :spec, :submodules, :spec_create
|
5
5
|
end
|
6
6
|
|
7
7
|
module_function
|
@@ -11,12 +11,14 @@ module Gemgem
|
|
11
11
|
def pkg_dir ; "#{dir}/pkg" ; end
|
12
12
|
def escaped_dir; @escaped_dir ||= Regexp.escape(dir); end
|
13
13
|
|
14
|
-
def init dir, &block
|
14
|
+
def init dir, options={}, &block
|
15
15
|
self.dir = dir
|
16
|
-
$LOAD_PATH.unshift("#{dir}/lib")
|
17
16
|
ENV['RUBYLIB'] = "#{dir}/lib:#{ENV['RUBYLIB']}"
|
18
17
|
ENV['PATH'] = "#{dir}/bin:#{ENV['PATH']}"
|
18
|
+
self.submodules = options[:submodules] || []
|
19
19
|
self.spec_create = block
|
20
|
+
|
21
|
+
$LOAD_PATH.unshift("#{dir}/lib", *submodules_libs)
|
20
22
|
end
|
21
23
|
|
22
24
|
def create
|
@@ -26,7 +28,7 @@ module Gemgem
|
|
26
28
|
|
27
29
|
s.description = description.join
|
28
30
|
s.summary = description.first
|
29
|
-
s.license =
|
31
|
+
s.license = license
|
30
32
|
|
31
33
|
s.date = Time.now.strftime('%Y-%m-%d')
|
32
34
|
s.files = gem_files
|
@@ -79,6 +81,11 @@ module Gemgem
|
|
79
81
|
end
|
80
82
|
|
81
83
|
def gem_check
|
84
|
+
unless git('status', '--porcelain').empty?
|
85
|
+
puts("\e[35mWorking copy is not clean.\e[0m")
|
86
|
+
exit(3)
|
87
|
+
end
|
88
|
+
|
82
89
|
ver = spec.version.to_s
|
83
90
|
|
84
91
|
if ENV['VERSION'].nil?
|
@@ -110,6 +117,7 @@ module Gemgem
|
|
110
117
|
SimpleCov.start do
|
111
118
|
add_filter('test/')
|
112
119
|
add_filter('test.rb')
|
120
|
+
submodules_libs.each(&method(:add_filter))
|
113
121
|
end
|
114
122
|
end
|
115
123
|
|
@@ -154,11 +162,15 @@ module Gemgem
|
|
154
162
|
end
|
155
163
|
|
156
164
|
def strip_home_path path
|
157
|
-
path.sub(ENV['HOME']
|
165
|
+
path.sub(/\A#{Regexp.escape(ENV['HOME'])}\//, '~/')
|
158
166
|
end
|
159
167
|
|
160
168
|
def strip_cwd_path path
|
161
|
-
path.sub(Dir.pwd
|
169
|
+
path.sub(/\A#{Regexp.escape(Dir.pwd)}\//, '')
|
170
|
+
end
|
171
|
+
|
172
|
+
def submodules_libs
|
173
|
+
submodules.map{ |path| "#{dir}/#{path}/lib" }
|
162
174
|
end
|
163
175
|
|
164
176
|
def git *args
|
@@ -196,6 +208,11 @@ module Gemgem
|
|
196
208
|
@description ||= (readme['DESCRIPTION']||'').sub(/.+\n\n/, '').lines.to_a
|
197
209
|
end
|
198
210
|
|
211
|
+
def license
|
212
|
+
readme['LICENSE'].sub(/.+\n\n/, '').lines.first.
|
213
|
+
split(/[()]/).map(&:strip).reject(&:empty?).last
|
214
|
+
end
|
215
|
+
|
199
216
|
def all_files
|
200
217
|
@all_files ||= fold_files(glob).sort
|
201
218
|
end
|
@@ -216,7 +233,8 @@ module Gemgem
|
|
216
233
|
|
217
234
|
def gem_files
|
218
235
|
@gem_files ||= all_files.reject{ |f|
|
219
|
-
f =~
|
236
|
+
f =~ submodules_pattern ||
|
237
|
+
(f =~ ignored_pattern && !git_files.include?(f))
|
220
238
|
}
|
221
239
|
end
|
222
240
|
|
@@ -248,6 +266,15 @@ module Gemgem
|
|
248
266
|
end
|
249
267
|
end
|
250
268
|
|
269
|
+
def submodules_pattern
|
270
|
+
@submodules_pattern ||= if submodules.empty?
|
271
|
+
/^$/
|
272
|
+
else
|
273
|
+
Regexp.new(submodules.map{ |path|
|
274
|
+
"^#{Regexp.escape(path)}/" }.join('|'))
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
251
278
|
def expand_patterns pathes
|
252
279
|
# http://git-scm.com/docs/gitignore
|
253
280
|
pathes.flat_map{ |path|
|
@@ -57,8 +57,8 @@ describe Muack::AnyInstanceOf do
|
|
57
57
|
end
|
58
58
|
|
59
59
|
would 'mock with multiple any_instance_of call' do
|
60
|
-
any_instance_of(klass){ |inst| mock(inst).f(is_a(
|
61
|
-
any_instance_of(klass){ |inst| mock(inst).f(is_a(
|
60
|
+
any_instance_of(klass){ |inst| mock(inst).f(is_a(Integer)){ |i| i+1 } }
|
61
|
+
any_instance_of(klass){ |inst| mock(inst).f(is_a(Integer)){ |i| i+2 } }
|
62
62
|
obj = klass.new
|
63
63
|
obj.f(2).should.eq 3
|
64
64
|
obj.f(2).should.eq 4
|
@@ -112,4 +112,18 @@ describe Muack::AnyInstanceOf do
|
|
112
112
|
|
113
113
|
obj.f.should.eq 0
|
114
114
|
end
|
115
|
+
|
116
|
+
# Brought from rspec-mocks and it's currently failing on rspec-mocks
|
117
|
+
would 'stub any_instance_of on module extending it self' do
|
118
|
+
mod = Module.new {
|
119
|
+
extend self
|
120
|
+
def hello; :hello; end
|
121
|
+
}
|
122
|
+
|
123
|
+
any_instance_of(mod){ |inst| stub(inst).hello{ :stub } }
|
124
|
+
|
125
|
+
expect(mod.hello).eq(:stub)
|
126
|
+
expect(Muack.verify)
|
127
|
+
expect(mod.hello).eq(:hello)
|
128
|
+
end
|
115
129
|
end
|
data/test/test_from_readme.rb
CHANGED
@@ -2,8 +2,7 @@
|
|
2
2
|
require 'muack/test'
|
3
3
|
|
4
4
|
describe 'from README.md' do
|
5
|
-
readme = File.read(
|
6
|
-
"#{File.dirname(File.expand_path(__FILE__))}/../README.md")
|
5
|
+
readme = File.read("#{__dir__}/../README.md")
|
7
6
|
codes = readme.scan(/``` ruby(.+?)```/m).map(&:first)
|
8
7
|
|
9
8
|
after{ Muack.reset }
|
@@ -12,21 +11,20 @@ describe 'from README.md' do
|
|
12
11
|
include Muack::API
|
13
12
|
|
14
13
|
def describe desc, &block
|
15
|
-
@
|
16
|
-
|
14
|
+
@suite.describe(desc, &block)
|
15
|
+
Pork::Executor.execute(:stat => @stat, :suite => @suite)
|
17
16
|
end
|
18
17
|
|
19
18
|
def results; @results ||= []; end
|
20
19
|
def p res ; results << res ; end
|
21
20
|
|
22
21
|
def verify expects
|
23
|
-
return if results.empty?
|
24
22
|
results.zip(expects).each do |(res, exp)|
|
25
23
|
next if exp == 'ok'
|
26
24
|
if exp.start_with?('raise')
|
27
25
|
res.should.kind_of? eval(exp.sub('raise', ''))
|
28
26
|
else
|
29
|
-
res.should.eq
|
27
|
+
res.should.eq instance_eval(exp)
|
30
28
|
end
|
31
29
|
end
|
32
30
|
end
|
@@ -34,10 +32,10 @@ describe 'from README.md' do
|
|
34
32
|
|
35
33
|
codes.each.with_index do |code, index|
|
36
34
|
would 'pass from README.md #%02d' % index do
|
37
|
-
|
35
|
+
suite, stat = Class.new(self.class){ init }, pork_stat
|
38
36
|
context = Module.new do
|
39
37
|
extend Context
|
40
|
-
@
|
38
|
+
@suite, @stat = suite, stat
|
41
39
|
end
|
42
40
|
begin
|
43
41
|
context.instance_eval(code, 'README.md', 0)
|
@@ -0,0 +1,111 @@
|
|
1
|
+
|
2
|
+
require 'muack/test'
|
3
|
+
|
4
|
+
describe Muack::Mock do
|
5
|
+
after do
|
6
|
+
Muack.verify.should.eq true
|
7
|
+
Muack::EnsureReset.call
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'passing single hash' do
|
11
|
+
would 'hash literal' do
|
12
|
+
mock(Obj).say({}, &:itself)
|
13
|
+
|
14
|
+
expect(Obj.say({})).eq({})
|
15
|
+
end
|
16
|
+
|
17
|
+
would 'hash literal with satisfying check' do
|
18
|
+
mock(Obj).say(is_a(Hash), &:itself)
|
19
|
+
|
20
|
+
expect(Obj.say({})).eq({})
|
21
|
+
end
|
22
|
+
|
23
|
+
would 'hash value' do
|
24
|
+
arg = {}
|
25
|
+
|
26
|
+
mock(Obj).say(arg, &:itself)
|
27
|
+
|
28
|
+
expect(Obj.say(arg)).eq(arg)
|
29
|
+
end
|
30
|
+
|
31
|
+
would 'non-empty hash' do
|
32
|
+
mock(Obj).say(a: 0, &:itself)
|
33
|
+
|
34
|
+
expect(Obj.say(a: 0)).eq(a: 0)
|
35
|
+
end
|
36
|
+
|
37
|
+
would 'non-empty hash with satisfying check' do
|
38
|
+
mock(Obj).say(where(a: 0), &:itself)
|
39
|
+
|
40
|
+
expect(Obj.say(a: 0)).eq(a: 0)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'keyargs mock' do
|
45
|
+
copy :tests do
|
46
|
+
would 'local block' do
|
47
|
+
mock(obj).say.with_any_args.returns{ |a:| a }
|
48
|
+
|
49
|
+
expect(instance.say(a: 0)).eq(0)
|
50
|
+
end
|
51
|
+
|
52
|
+
would 'instance method' do
|
53
|
+
mock(obj).bonjour(a: 0, b: 1)
|
54
|
+
|
55
|
+
expect(instance.bonjour(a: 0, b: 1)).eq([0, 1])
|
56
|
+
end
|
57
|
+
|
58
|
+
would 'prepended method' do
|
59
|
+
mock(obj).prepend_bonjour(a: 0, b: 1)
|
60
|
+
|
61
|
+
expect(instance.prepend_bonjour(a: 0, b: 1)).eq([0, 1])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe 'with direct mock' do
|
66
|
+
def obj
|
67
|
+
Obj
|
68
|
+
end
|
69
|
+
|
70
|
+
def instance
|
71
|
+
obj
|
72
|
+
end
|
73
|
+
|
74
|
+
paste :tests
|
75
|
+
|
76
|
+
would 'singleton method' do
|
77
|
+
mock(obj).single_bonjour(a: 0, b: 1)
|
78
|
+
|
79
|
+
expect(instance.single_bonjour(a: 0, b: 1)).eq([0, 1])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'with any_instance_of' do
|
84
|
+
def obj
|
85
|
+
any_instance_of(Cls)
|
86
|
+
end
|
87
|
+
|
88
|
+
def instance
|
89
|
+
@instance ||= Cls.new
|
90
|
+
end
|
91
|
+
|
92
|
+
paste :tests
|
93
|
+
end
|
94
|
+
|
95
|
+
would 'peek_args' do
|
96
|
+
mock(Obj).say.with_any_args.
|
97
|
+
peek_args{ |a:| [{a: a.succ}] }.
|
98
|
+
returns{ |a:| a.succ }
|
99
|
+
|
100
|
+
expect(Obj.say(a: 0)).eq(2)
|
101
|
+
end
|
102
|
+
|
103
|
+
would 'peek_args with instance_exec' do
|
104
|
+
mock(Obj).say.with_any_args.
|
105
|
+
peek_args(instance_exec: true){ |a:| [{a: object_id}] }.
|
106
|
+
returns{ |a:| a }
|
107
|
+
|
108
|
+
expect(Obj.say(a: 0)).eq(Obj.object_id)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/test/test_modifier.rb
CHANGED
@@ -37,19 +37,19 @@ describe Muack::Modifier do
|
|
37
37
|
|
38
38
|
describe 'peek_args' do
|
39
39
|
would 'with lexical scope' do
|
40
|
-
str = 'ff'
|
40
|
+
str = String.new('ff')
|
41
41
|
stub(str).to_i.peek_args{16}
|
42
42
|
str.to_i.should.eq 255
|
43
43
|
end
|
44
44
|
|
45
45
|
would 'with dynamic scope' do
|
46
|
-
str = '16'
|
46
|
+
str = String.new('16')
|
47
47
|
stub(str).to_i.peek_args(:instance_exec => true){Integer(self)}
|
48
48
|
str.to_i.should.eq 22
|
49
49
|
end
|
50
50
|
|
51
51
|
would 'modify' do
|
52
|
-
str = 'ff'
|
52
|
+
str = String.new('ff')
|
53
53
|
stub(str).to_i(is_a(Integer)).peek_args{ |radix| radix * 2 }
|
54
54
|
str.to_i(8).should.eq 255
|
55
55
|
end
|
@@ -82,19 +82,19 @@ describe Muack::Modifier do
|
|
82
82
|
|
83
83
|
describe 'peek_return' do
|
84
84
|
would 'with lexical scope' do
|
85
|
-
str = 'ff'
|
85
|
+
str = String.new('ff')
|
86
86
|
stub(str).to_i.peek_return{16}
|
87
87
|
str.to_i.should.eq 16
|
88
88
|
end
|
89
89
|
|
90
90
|
would 'with dynamic scope' do
|
91
|
-
str = '16'
|
91
|
+
str = String.new('16')
|
92
92
|
stub(str).to_i.peek_return(:instance_exec => true){Integer(self)+1}
|
93
93
|
str.to_i.should.eq 17
|
94
94
|
end
|
95
95
|
|
96
96
|
would 'modify' do
|
97
|
-
str = 'ff'
|
97
|
+
str = String.new('ff')
|
98
98
|
stub(str).to_i(is_a(Integer)).peek_return{ |result| result * 2 }
|
99
99
|
str.to_i(16).should.eq 510
|
100
100
|
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
|
2
|
+
require 'muack/test'
|
3
|
+
|
4
|
+
describe 'mock with prepend' do
|
5
|
+
after do
|
6
|
+
verify
|
7
|
+
end
|
8
|
+
|
9
|
+
def verify
|
10
|
+
expect(Muack.verify).eq true
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate
|
14
|
+
klass = Class.new do
|
15
|
+
def greet
|
16
|
+
'hi'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
mod = Module.new do
|
21
|
+
def greet
|
22
|
+
hello
|
23
|
+
end
|
24
|
+
|
25
|
+
def hello
|
26
|
+
'hello'
|
27
|
+
end
|
28
|
+
private :hello
|
29
|
+
end
|
30
|
+
|
31
|
+
perform(klass, mod)
|
32
|
+
end
|
33
|
+
|
34
|
+
copy :test do
|
35
|
+
would 'mock' do
|
36
|
+
obj = generate
|
37
|
+
|
38
|
+
mock(obj).hello{'mocked'}
|
39
|
+
|
40
|
+
expect(obj.greet).eq 'mocked'
|
41
|
+
verify
|
42
|
+
expect(obj.greet).eq 'hello'
|
43
|
+
expect(obj).not.respond_to? :hello
|
44
|
+
end
|
45
|
+
|
46
|
+
would 'mock proxy' do
|
47
|
+
obj = generate
|
48
|
+
|
49
|
+
mock(obj).hello
|
50
|
+
|
51
|
+
expect(obj.greet).eq 'hello'
|
52
|
+
verify
|
53
|
+
expect(obj.greet).eq 'hello'
|
54
|
+
expect(obj).not.respond_to? :hello
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'prepend on class mock with object' do
|
59
|
+
def perform(klass, mod)
|
60
|
+
klass.prepend(mod)
|
61
|
+
klass.new
|
62
|
+
end
|
63
|
+
|
64
|
+
paste :test
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'extend on object mock with object' do
|
68
|
+
def perform(klass, mod)
|
69
|
+
obj = klass.new
|
70
|
+
obj.extend(mod)
|
71
|
+
obj
|
72
|
+
end
|
73
|
+
|
74
|
+
paste :test
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'include on singleton_class mock with object' do
|
78
|
+
def perform(klass, mod)
|
79
|
+
obj = klass.new
|
80
|
+
obj.singleton_class.include(mod)
|
81
|
+
obj
|
82
|
+
end
|
83
|
+
|
84
|
+
paste :test
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'prepend on singleton_class mock with object' do
|
88
|
+
def perform(klass, mod)
|
89
|
+
obj = klass.new
|
90
|
+
obj.singleton_class.prepend(mod)
|
91
|
+
obj
|
92
|
+
end
|
93
|
+
|
94
|
+
paste :test
|
95
|
+
end
|
96
|
+
|
97
|
+
# Brought from rspec-mocks and it's currently failing on rspec-mocks
|
98
|
+
# See https://github.com/rspec/rspec-mocks/pull/1218
|
99
|
+
would "handle stubbing prepending methods that were only defined on the prepended module" do
|
100
|
+
to_be_prepended = Module.new do
|
101
|
+
def value
|
102
|
+
"#{super}_prepended".to_sym
|
103
|
+
end
|
104
|
+
|
105
|
+
def value_without_super
|
106
|
+
:prepended
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
object = Object.new
|
111
|
+
object.singleton_class.send(:prepend, to_be_prepended)
|
112
|
+
expect(object.value_without_super).eq :prepended
|
113
|
+
|
114
|
+
stub(object).value_without_super{ :stubbed }
|
115
|
+
|
116
|
+
expect(object.value_without_super).eq :stubbed
|
117
|
+
|
118
|
+
expect(Muack.verify)
|
119
|
+
expect(object.value_without_super).eq :prepended
|
120
|
+
end
|
121
|
+
end
|