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.
@@ -0,0 +1,195 @@
1
+
2
+ require 'muack/error'
3
+
4
+ module Muack
5
+ class Satisfying < Struct.new(:api_args, :block)
6
+ def initialize args=nil, &block
7
+ super(args, block)
8
+ end
9
+
10
+ def match actual_arg
11
+ !!block.call(actual_arg)
12
+ end
13
+
14
+ def | rhs; Satisfying::Disj.new(self, rhs); end
15
+ def & rhs; Satisfying::Conj.new(self, rhs); end
16
+
17
+ class Disj < Satisfying
18
+ def initialize lhs, rhs
19
+ @lhs, @rhs = lhs, rhs
20
+ end
21
+
22
+ def match actual_arg
23
+ @lhs.match(actual_arg) || @rhs.match(actual_arg)
24
+ end
25
+
26
+ def to_s; "#{@lhs} | #{@rhs}"; end
27
+ alias_method :inspect, :to_s
28
+ end
29
+
30
+ class Conj < Satisfying
31
+ def initialize lhs, rhs
32
+ @lhs, @rhs = lhs, rhs
33
+ end
34
+
35
+ def match actual_arg
36
+ @lhs.match(actual_arg) && @rhs.match(actual_arg)
37
+ end
38
+
39
+ def to_s; "#{@lhs} & #{@rhs}"; end
40
+ alias_method :inspect, :to_s
41
+ end
42
+
43
+ def to_s
44
+ "Muack::API.#{api_name}(#{api_args.map(&:inspect).join(', ')})"
45
+ end
46
+ alias_method :inspect, :to_s
47
+
48
+ def api_name
49
+ (self.class.name || 'Unknown')[/(::)*(\w+)$/, 2].
50
+ gsub(/([A-Z][a-z]*)+?(?=[A-Z][a-z]*)/, '\\1_').downcase
51
+ end
52
+
53
+ def api_args
54
+ super || [block || method(:match)]
55
+ end
56
+ end
57
+
58
+ class Anything < Satisfying
59
+ def initialize
60
+ super([])
61
+ end
62
+
63
+ def match _
64
+ true
65
+ end
66
+ end
67
+
68
+ class IsA < Satisfying
69
+ def initialize klass
70
+ super([klass])
71
+ end
72
+
73
+ def match actual_arg
74
+ actual_arg.kind_of?(api_args.first)
75
+ end
76
+ end
77
+
78
+ class Matching < Satisfying
79
+ def initialize regexp
80
+ super([regexp])
81
+ end
82
+
83
+ def match actual_arg
84
+ api_args.first.match(actual_arg)
85
+ end
86
+ end
87
+
88
+ class Including < Satisfying
89
+ def initialize element
90
+ super([element])
91
+ end
92
+
93
+ def match actual_arg
94
+ actual_arg.include?(api_args.first)
95
+ end
96
+ end
97
+
98
+ class Within < Satisfying
99
+ def initialize range_or_array
100
+ super([range_or_array])
101
+ end
102
+
103
+ def match actual_arg
104
+ api_args.first.include?(actual_arg)
105
+ end
106
+ end
107
+
108
+ class RespondingTo < Satisfying
109
+ def initialize *messages
110
+ super(messages)
111
+ end
112
+
113
+ def match actual_arg
114
+ api_args.all?{ |msg| actual_arg.respond_to?(msg) }
115
+ end
116
+ end
117
+
118
+ class Where < Satisfying
119
+ None = Object.new
120
+ def initialize spec
121
+ super([spec])
122
+ end
123
+
124
+ def match actual_arg, spec=api_args.first
125
+ case spec
126
+ when Hash
127
+ actual_arg.kind_of?(Hash) && match_hash(actual_arg, spec)
128
+ when Array
129
+ actual_arg.kind_of?(Array) && match_array(actual_arg, spec)
130
+ else
131
+ raise UnknownSpec.new(spec)
132
+ end
133
+ end
134
+
135
+ private
136
+ def match_hash actual_arg, spec
137
+ (spec.keys | actual_arg.keys).all? do |key|
138
+ match_value(actual_arg, spec, key)
139
+ end
140
+ end
141
+
142
+ def match_array actual_arg, spec
143
+ spec.zip(actual_arg).all? do |(ev, av)|
144
+ match_value(av, ev)
145
+ end
146
+ end
147
+
148
+ def match_value av, ev, key=None
149
+ if key == None
150
+ a, e = av, ev
151
+ elsif av.key?(key) && ev.key?(key)
152
+ a, e = av[key], ev[key]
153
+ else
154
+ return false
155
+ end
156
+
157
+ case e
158
+ when Satisfying
159
+ e.match(a)
160
+ when Hash
161
+ a.kind_of?(Hash) && match_hash(a, e)
162
+ when Array
163
+ a.kind_of?(Array) && match_array(a, e)
164
+ else
165
+ e == a
166
+ end
167
+ end
168
+ end
169
+
170
+ class Having < Where
171
+ def initialize subset
172
+ super(subset)
173
+ end
174
+
175
+ private
176
+ def match_hash actual_arg, subset
177
+ subset.each_key.all? do |key|
178
+ match_value(actual_arg, subset, key)
179
+ end
180
+ end
181
+ end
182
+
183
+ class Allowing < Where
184
+ def initialize superset
185
+ super(superset)
186
+ end
187
+
188
+ private
189
+ def match_hash actual_arg, superset
190
+ actual_arg.each_key.all? do |key|
191
+ match_value(actual_arg, superset, key)
192
+ end
193
+ end
194
+ end
195
+ end
@@ -10,10 +10,8 @@ module Muack
10
10
 
11
11
  # used for Muack::Session#verify
12
12
  def __mock_verify
13
- @stub.__mock_disps.values.flatten.each do |defi|
14
- __mock_dispatch(defi.msg, defi.args) if __mock_defis.key?(defi.msg)
15
- end
16
- super # simulate dispatching before passing to mock to verify
13
+ __mock_dispatch_spy
14
+ super
17
15
  end
18
16
 
19
17
  # used for Muack::Session#reset, but spies never leave any track
@@ -21,5 +19,21 @@ module Muack
21
19
 
22
20
  private
23
21
  def __mock_inject_method defi; end # spies don't leave any track
22
+
23
+ # simulate dispatching before passing to mock to verify
24
+ def __mock_dispatch_spy
25
+ @stub.__mock_disps.values.flatten.each do |disp|
26
+ next unless __mock_defis.key?(disp.msg) # ignore undefined spies
27
+
28
+ defis = __mock_defis[disp.msg]
29
+ if idx = __mock_find_checked_difi(defis, disp, :index)
30
+ __mock_disps_push(defis.delete_at(idx)) # found, dispatch it
31
+ elsif defis.empty? # show called candidates
32
+ __mock_failed(disp)
33
+ else # show expected candidates
34
+ __mock_failed(disp, defis)
35
+ end
36
+ end
37
+ end
24
38
  end
25
39
  end
@@ -14,15 +14,16 @@ module Muack
14
14
  end
15
15
 
16
16
  # used for mocked object to dispatch mocked method
17
- def __mock_dispatch msg, actual_args
18
- if defi = __mock_defis[msg].find{ |d|
19
- __mock_check_args(d.args, actual_args) }
17
+ def __mock_dispatch actual_call
18
+ defis = __mock_defis[actual_call.msg]
19
+
20
+ if disp = __mock_find_checked_difi(defis, actual_call)
20
21
  # our spies are interested in this
21
- __mock_disps_push(Definition.new(msg, actual_args))
22
- defi
22
+ __mock_disps_push(actual_call)
23
+ disp
23
24
  else
24
25
  Mock.__send__(:raise, # Wrong argument
25
- Unexpected.new(object, __mock_defis[msg], msg, actual_args))
26
+ Unexpected.new(object, defis, actual_call))
26
27
  end
27
28
  end
28
29
  end
@@ -2,20 +2,51 @@
2
2
  require 'pork/auto'
3
3
  require 'muack'
4
4
 
5
- Pork::Executor.__send__(:include, Muack::API)
5
+ Pork::Suite.include(Muack::API)
6
6
 
7
- Obj = Object.new
8
- Str = 'Moo'
9
- def Obj.inspect
10
- 'obj'
11
- end
12
- def Obj.private
13
- 'pri'
7
+ Str = String.new('Moo')
8
+ class Cls
9
+ def inspect
10
+ 'obj'
11
+ end
12
+ def aloha a=0, b=1
13
+ [a, b]
14
+ end
15
+ def bonjour a: 0, b: 1
16
+ [a, b]
17
+ end
18
+ def ciao h={a: 0, b: 1}
19
+ h.values_at(:a, :b)
20
+ end
21
+ module Prepend
22
+ def prepend_aloha a=0, b=1
23
+ [a, b]
24
+ end
25
+ def prepend_bonjour a: 0, b: 1
26
+ [a, b]
27
+ end
28
+ def prepend_ciao h={a: 0, b: 1}
29
+ h.values_at(:a, :b)
30
+ end
31
+ end
32
+ prepend Prepend
14
33
  end
15
- def Obj.aloha a=0, b=1
16
- [a, b]
34
+ Obj = Cls.new
35
+ class << Obj
36
+ private def private
37
+ 'pri'
38
+ end
39
+
40
+ def single_aloha a=0, b=1
41
+ [a, b]
42
+ end
43
+ def single_bonjour a: 0, b: 1
44
+ [a, b]
45
+ end
46
+ def single_ciao h={a: 0, b: 1}
47
+ h.values_at(:a, :b)
48
+ end
17
49
  end
18
- Obj.singleton_class.__send__(:private, :private)
19
50
 
20
51
  Muack::EnsureReset = lambda{
21
52
  [Obj, Str].each do |o|
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Muack
3
- VERSION = '1.2.0'
3
+ VERSION = '1.5.0'
4
4
  end
@@ -1,62 +1,72 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: muack 1.2.0 ruby lib
2
+ # stub: muack 1.5.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
- s.name = "muack"
6
- s.version = "1.2.0"
5
+ s.name = "muack".freeze
6
+ s.version = "1.5.0"
7
7
 
8
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
- s.require_paths = ["lib"]
10
- s.authors = ["Lin Jen-Shin (godfat)"]
11
- s.date = "2015-03-10"
12
- s.description = "Muack -- A fast, small, yet powerful mocking library.\n\nInspired by [RR][], and it's 32x times faster (750s vs 23s) than RR\nfor running [Rib][] tests.\n\n[RR]: https://github.com/rr/rr\n[Rib]: https://github.com/godfat/rib"
13
- s.email = ["godfat (XD) godfat.org"]
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
+ s.require_paths = ["lib".freeze]
10
+ s.authors = ["Lin Jen-Shin (godfat)".freeze]
11
+ s.date = "2020-11-28"
12
+ s.description = "Muack -- A fast, small, yet powerful mocking library.\n\nInspired by [RR][], and it's 32x times faster (750s vs 23s) than RR\nfor running [Rib][] tests.\n\n[RR]: https://github.com/rr/rr\n[Rib]: https://github.com/godfat/rib".freeze
13
+ s.email = ["godfat (XD) godfat.org".freeze]
14
14
  s.files = [
15
- ".gitignore",
16
- ".gitmodules",
17
- ".travis.yml",
18
- "CHANGES.md",
19
- "Gemfile",
20
- "LICENSE",
21
- "README.md",
22
- "Rakefile",
23
- "lib/muack.rb",
24
- "lib/muack/any_instance_of.rb",
25
- "lib/muack/block.rb",
26
- "lib/muack/coat.rb",
27
- "lib/muack/definition.rb",
28
- "lib/muack/error.rb",
29
- "lib/muack/failure.rb",
30
- "lib/muack/mock.rb",
31
- "lib/muack/modifier.rb",
32
- "lib/muack/satisfy.rb",
33
- "lib/muack/session.rb",
34
- "lib/muack/spy.rb",
35
- "lib/muack/stub.rb",
36
- "lib/muack/test.rb",
37
- "lib/muack/version.rb",
38
- "muack.gemspec",
39
- "task/README.md",
40
- "task/gemgem.rb",
41
- "test/test_any_instance_of.rb",
42
- "test/test_coat.rb",
43
- "test/test_from_readme.rb",
44
- "test/test_mock.rb",
45
- "test/test_modifier.rb",
46
- "test/test_proxy.rb",
47
- "test/test_satisfy.rb",
48
- "test/test_stub.rb"]
49
- s.homepage = "https://github.com/godfat/muack"
50
- s.licenses = ["Apache License 2.0"]
51
- s.rubygems_version = "2.4.6"
52
- s.summary = "Muack -- A fast, small, yet powerful mocking library."
15
+ ".gitignore".freeze,
16
+ ".gitmodules".freeze,
17
+ ".travis.yml".freeze,
18
+ "CHANGES.md".freeze,
19
+ "Gemfile".freeze,
20
+ "LICENSE".freeze,
21
+ "README.md".freeze,
22
+ "Rakefile".freeze,
23
+ "lib/muack.rb".freeze,
24
+ "lib/muack/any_instance_of.rb".freeze,
25
+ "lib/muack/block.rb".freeze,
26
+ "lib/muack/block_26.rb".freeze,
27
+ "lib/muack/block_27.rb".freeze,
28
+ "lib/muack/coat.rb".freeze,
29
+ "lib/muack/definition.rb".freeze,
30
+ "lib/muack/error.rb".freeze,
31
+ "lib/muack/failure.rb".freeze,
32
+ "lib/muack/mock.rb".freeze,
33
+ "lib/muack/modifier.rb".freeze,
34
+ "lib/muack/satisfying.rb".freeze,
35
+ "lib/muack/session.rb".freeze,
36
+ "lib/muack/spy.rb".freeze,
37
+ "lib/muack/stub.rb".freeze,
38
+ "lib/muack/test.rb".freeze,
39
+ "lib/muack/version.rb".freeze,
40
+ "muack.gemspec".freeze,
41
+ "task/README.md".freeze,
42
+ "task/gemgem.rb".freeze,
43
+ "test/test_any_instance_of.rb".freeze,
44
+ "test/test_coat.rb".freeze,
45
+ "test/test_from_readme.rb".freeze,
46
+ "test/test_keyargs.rb".freeze,
47
+ "test/test_mock.rb".freeze,
48
+ "test/test_modifier.rb".freeze,
49
+ "test/test_prepend.rb".freeze,
50
+ "test/test_proxy.rb".freeze,
51
+ "test/test_satisfying.rb".freeze,
52
+ "test/test_spy.rb".freeze,
53
+ "test/test_stub.rb".freeze,
54
+ "test/test_visibility.rb".freeze]
55
+ s.homepage = "https://github.com/godfat/muack".freeze
56
+ s.licenses = ["Apache-2.0".freeze]
57
+ s.rubygems_version = "3.1.4".freeze
58
+ s.summary = "Muack -- A fast, small, yet powerful mocking library.".freeze
53
59
  s.test_files = [
54
- "test/test_any_instance_of.rb",
55
- "test/test_coat.rb",
56
- "test/test_from_readme.rb",
57
- "test/test_mock.rb",
58
- "test/test_modifier.rb",
59
- "test/test_proxy.rb",
60
- "test/test_satisfy.rb",
61
- "test/test_stub.rb"]
60
+ "test/test_any_instance_of.rb".freeze,
61
+ "test/test_coat.rb".freeze,
62
+ "test/test_from_readme.rb".freeze,
63
+ "test/test_keyargs.rb".freeze,
64
+ "test/test_mock.rb".freeze,
65
+ "test/test_modifier.rb".freeze,
66
+ "test/test_prepend.rb".freeze,
67
+ "test/test_proxy.rb".freeze,
68
+ "test/test_satisfying.rb".freeze,
69
+ "test/test_spy.rb".freeze,
70
+ "test/test_stub.rb".freeze,
71
+ "test/test_visibility.rb".freeze]
62
72
  end