flexmock 0.6.4 → 0.7.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.
- data/CHANGES +8 -0
- data/README +116 -3
- data/Rakefile +5 -5
- data/TAGS +396 -282
- data/doc/releases/flexmock-0.7.0.rdoc +138 -0
- data/lib/flexmock/base.rb +2 -1
- data/lib/flexmock/core.rb +30 -29
- data/lib/flexmock/core_class_methods.rb +21 -30
- data/lib/flexmock/deprecated_methods.rb +62 -0
- data/lib/flexmock/expectation.rb +101 -21
- data/lib/flexmock/expectation_director.rb +10 -5
- data/lib/flexmock/mock_container.rb +144 -29
- data/lib/flexmock/ordering.rb +51 -0
- data/lib/flexmock/partial_mock.rb +23 -22
- data/test/asserts.rb +34 -0
- data/test/redirect_error.rb +16 -0
- data/test/rspec_integration/integration_spec.rb +6 -0
- data/test/test_demeter_mocking.rb +129 -0
- data/test/{test_mock.rb → test_deprecated_methods.rb} +64 -17
- data/test/test_example.rb +4 -3
- data/test/test_extended_should_receive.rb +1 -1
- data/test/test_flexmodel.rb +17 -1
- data/test/test_naming.rb +43 -8
- data/test/test_new_instances.rb +2 -22
- data/test/test_partial_mock.rb +19 -19
- data/test/test_record_mode.rb +27 -38
- data/test/test_should_receive.rb +199 -27
- metadata +11 -4
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
class FlexMock
|
4
|
+
module RedirectError
|
5
|
+
def redirect_error
|
6
|
+
require 'stringio'
|
7
|
+
old_err = $stderr
|
8
|
+
$stderr = StringIO.new
|
9
|
+
yield
|
10
|
+
$stderr.string
|
11
|
+
ensure
|
12
|
+
$stderr = old_err
|
13
|
+
end
|
14
|
+
private :redirect_error
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'flexmock'
|
5
|
+
require 'test/asserts'
|
6
|
+
|
7
|
+
class TestDemeterMocking < Test::Unit::TestCase
|
8
|
+
include FlexMock::TestCase
|
9
|
+
include FlexMock::FailureAssertion
|
10
|
+
|
11
|
+
def test_demeter_mocking
|
12
|
+
m = flexmock("A")
|
13
|
+
m.should_receive("children.first").and_return(:first)
|
14
|
+
assert_kind_of FlexMock, m
|
15
|
+
assert_kind_of FlexMock, m.children
|
16
|
+
assert_equal :first, m.children.first
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_multiple_demeter_mocks_on_same_branch_is_ok
|
20
|
+
m = flexmock("A")
|
21
|
+
m.should_receive("child.x.y.z.first").and_return(:first)
|
22
|
+
m.should_receive("child.x.y.z.last").and_return(:last)
|
23
|
+
assert_equal :first, m.child.x.y.z.first
|
24
|
+
assert_equal :last, m.child.x.y.z.last
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_multi_level_deep_demeter_violation
|
28
|
+
a = flexmock("a")
|
29
|
+
a.should_receive("b.c.d.e.f.g.h.i.j.k").and_return(:xyzzy)
|
30
|
+
assert_equal :xyzzy, a.b.c.d.e.f.g.h.i.j.k
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_final_method_can_have_multiple_expecations
|
34
|
+
a = flexmock("a")
|
35
|
+
a.should_receive("b.c.d.last").with(1).and_return(:one).once
|
36
|
+
a.should_receive("b.c.d.last").with(2).and_return(:two).once
|
37
|
+
assert_equal :one, a.b.c.d.last(1)
|
38
|
+
assert_equal :two, a.b.c.d.last(2)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_conflicting_mock_declarations_raises_an_error
|
42
|
+
m = flexmock("A")
|
43
|
+
ex = assert_raise(FlexMock::UsageError) do
|
44
|
+
m.should_receive("child").and_return(:xyzzy)
|
45
|
+
m.should_receive("child.other").and_return(:other)
|
46
|
+
m.child.other
|
47
|
+
end
|
48
|
+
assert_match(/conflicting/i, ex.message)
|
49
|
+
assert_match(/mock\s+declaration/i, ex.message)
|
50
|
+
assert_match(/child/i, ex.message)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_conflicting_mock_declarations_in_reverse_order_does_not_raise_error
|
54
|
+
# Not all conflicting definitions can be detected.
|
55
|
+
m = flexmock("A")
|
56
|
+
assert_failure() do
|
57
|
+
m.should_receive("child.other").and_return(:other)
|
58
|
+
m.should_receive("child").and_return(:xyzzy)
|
59
|
+
assert_equal :xyzzy, m.child.other
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_preestablishing_existing_mock_is_ok
|
64
|
+
engine = flexmock("engine")
|
65
|
+
car = flexmock("A")
|
66
|
+
car.should_receive(:engine).and_return(engine)
|
67
|
+
car.should_receive("engine.cylinder").and_return(:cyl)
|
68
|
+
assert_equal :cyl, car.engine.cylinder
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_quick_defs_can_use_demeter_mocking
|
72
|
+
a = flexmock("a")
|
73
|
+
a.should_receive("b.c.d.x").and_return(:x)
|
74
|
+
a.should_receive("b.c.d.y").and_return(:y)
|
75
|
+
a.should_receive("b.c.d.z").and_return(:z)
|
76
|
+
assert_equal :x, a.b.c.d.x
|
77
|
+
assert_equal :y, a.b.c.d.y
|
78
|
+
assert_equal :z, a.b.c.d.z
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_quick_defs_can_use_demeter_mocking_two
|
82
|
+
a = flexmock("a", "b.c.d.xx" => :x, "b.c.d.yy" => :y, "b.c.d.zz" => :z)
|
83
|
+
assert_equal :x, a.b.c.d.xx
|
84
|
+
assert_equal :y, a.b.c.d.yy
|
85
|
+
assert_equal :z, a.b.c.d.zz
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_errors_on_ill_formed_method_names
|
89
|
+
m = flexmock("a")
|
90
|
+
[
|
91
|
+
'a(2)', '0a', 'a-b', 'a b', ' ', 'a ', ' b', 'a!b', "a?b", 'a=b'
|
92
|
+
].each do |method|
|
93
|
+
assert_raise FlexMock::UsageError do m.should_receive(method) end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_no_errors_on_well_formed_method_names
|
98
|
+
m = flexmock("a")
|
99
|
+
[
|
100
|
+
'a', 'a?', 'a!', 'a=', 'z0', 'save!'
|
101
|
+
].each do |method|
|
102
|
+
assert_nothing_raised do m.should_receive(method) end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_readme_example_1
|
107
|
+
cog = flexmock("cog")
|
108
|
+
cog.should_receive(:turn).once.and_return(:ok).mock
|
109
|
+
joint = flexmock("gear", :cog => cog)
|
110
|
+
axle = flexmock("axle", :universal_joint => joint)
|
111
|
+
chassis = flexmock("chassis", :axle => axle)
|
112
|
+
car = flexmock("car", :chassis => chassis)
|
113
|
+
assert_equal :ok, car.chassis.axle.universal_joint.cog.turn
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_readme_example_2
|
117
|
+
car = flexmock("car")
|
118
|
+
car.should_receive("chassis.axle.universal_joint.cog.turn" => :ok).once
|
119
|
+
assert_equal :ok, car.chassis.axle.universal_joint.cog.turn
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_readme_example_3
|
123
|
+
car = flexmock("car")
|
124
|
+
car.should_receive("chassis.axle.universal_joint.cog.turn").once.
|
125
|
+
and_return(:ok)
|
126
|
+
assert_equal :ok, car.chassis.axle.universal_joint.cog.turn
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
@@ -11,34 +11,43 @@
|
|
11
11
|
|
12
12
|
require 'test/unit'
|
13
13
|
require 'flexmock'
|
14
|
+
require 'flexmock/deprecated_methods'
|
15
|
+
require 'test/redirect_error'
|
14
16
|
|
15
17
|
class TestFlexMock < Test::Unit::TestCase
|
18
|
+
include FlexMock::TestCase
|
19
|
+
include FlexMock::RedirectError
|
20
|
+
|
21
|
+
def s(&block)
|
22
|
+
redirect_error(&block)
|
23
|
+
end
|
24
|
+
|
16
25
|
def setup
|
17
|
-
@mock =
|
26
|
+
@mock = flexmock('mock')
|
18
27
|
end
|
19
28
|
|
20
29
|
def test_handle
|
21
30
|
args = nil
|
22
|
-
@mock.mock_handle(:hi) { |a, b| args = [a,b] }
|
31
|
+
s { @mock.mock_handle(:hi) { |a, b| args = [a,b] } }
|
23
32
|
@mock.hi(1,2)
|
24
33
|
assert_equal [1,2], args
|
25
34
|
end
|
26
35
|
|
27
36
|
def test_handle_no_block
|
28
|
-
@mock.mock_handle(:blip)
|
37
|
+
s { @mock.mock_handle(:blip) }
|
29
38
|
@mock.blip
|
30
39
|
assert true, "just checking for failures"
|
31
40
|
end
|
32
41
|
|
33
42
|
def test_called_with_block
|
34
43
|
called = false
|
35
|
-
@mock.mock_handle(:blip) { |block| block.call }
|
44
|
+
s { @mock.mock_handle(:blip) { |block| block.call } }
|
36
45
|
@mock.blip { called = true }
|
37
46
|
assert called, "Block to blip should be called"
|
38
47
|
end
|
39
48
|
|
40
49
|
def test_return_value
|
41
|
-
@mock.mock_handle(:blip) { 10 }
|
50
|
+
s { @mock.mock_handle(:blip) { 10 } }
|
42
51
|
assert_equal 10, @mock.blip
|
43
52
|
end
|
44
53
|
|
@@ -57,19 +66,19 @@ class TestFlexMock < Test::Unit::TestCase
|
|
57
66
|
end
|
58
67
|
|
59
68
|
def test_good_counts
|
60
|
-
@mock.mock_handle(:blip, 3)
|
69
|
+
s { @mock.mock_handle(:blip, 3) }
|
61
70
|
@mock.blip
|
62
71
|
@mock.blip
|
63
72
|
@mock.blip
|
64
|
-
@mock.
|
73
|
+
@mock.flexmock_verify
|
65
74
|
end
|
66
75
|
|
67
76
|
def test_bad_counts
|
68
|
-
@mock.mock_handle(:blip, 3)
|
77
|
+
s { @mock.mock_handle(:blip, 3) }
|
69
78
|
@mock.blip
|
70
79
|
@mock.blip
|
71
80
|
begin
|
72
|
-
@mock.
|
81
|
+
@mock.flexmock_verify
|
73
82
|
rescue Test::Unit::AssertionFailedError => err
|
74
83
|
end
|
75
84
|
assert_not_nil err
|
@@ -77,7 +86,7 @@ class TestFlexMock < Test::Unit::TestCase
|
|
77
86
|
|
78
87
|
def test_undetermined_counts
|
79
88
|
FlexMock.use('fs') { |m|
|
80
|
-
m.mock_handle(:blip)
|
89
|
+
s { m.mock_handle(:blip) }
|
81
90
|
m.blip
|
82
91
|
m.blip
|
83
92
|
m.blip
|
@@ -87,7 +96,7 @@ class TestFlexMock < Test::Unit::TestCase
|
|
87
96
|
def test_zero_counts
|
88
97
|
assert_raises(Test::Unit::AssertionFailedError) do
|
89
98
|
FlexMock.use { |m|
|
90
|
-
m.mock_handle(:blip, 0)
|
99
|
+
s { m.mock_handle(:blip, 0) }
|
91
100
|
m.blip
|
92
101
|
}
|
93
102
|
end
|
@@ -96,7 +105,7 @@ class TestFlexMock < Test::Unit::TestCase
|
|
96
105
|
def test_file_io_with_use
|
97
106
|
file = FlexMock.use do |m|
|
98
107
|
filedata = ["line 1", "line 2"]
|
99
|
-
m.mock_handle(:gets, 3) { filedata.shift }
|
108
|
+
s { m.mock_handle(:gets, 3) { filedata.shift } }
|
100
109
|
assert_equal 2, count_lines(m)
|
101
110
|
end
|
102
111
|
end
|
@@ -112,7 +121,7 @@ class TestFlexMock < Test::Unit::TestCase
|
|
112
121
|
def test_use
|
113
122
|
assert_raises(Test::Unit::AssertionFailedError) {
|
114
123
|
FlexMock.use do |m|
|
115
|
-
m.mock_handle(:blip, 2)
|
124
|
+
s { m.mock_handle(:blip, 2) }
|
116
125
|
m.blip
|
117
126
|
end
|
118
127
|
}
|
@@ -121,7 +130,7 @@ class TestFlexMock < Test::Unit::TestCase
|
|
121
130
|
def test_failures_during_use
|
122
131
|
ex = assert_raises(NameError) {
|
123
132
|
FlexMock.use do |m|
|
124
|
-
m.mock_handle(:blip, 2)
|
133
|
+
s { m.mock_handle(:blip, 2) }
|
125
134
|
xyz
|
126
135
|
end
|
127
136
|
}
|
@@ -130,7 +139,7 @@ class TestFlexMock < Test::Unit::TestCase
|
|
130
139
|
|
131
140
|
def test_sequential_values
|
132
141
|
values = [1,4,9,16]
|
133
|
-
@mock.mock_handle(:get) { values.shift }
|
142
|
+
s { @mock.mock_handle(:get) { values.shift } }
|
134
143
|
assert_equal 1, @mock.get
|
135
144
|
assert_equal 4, @mock.get
|
136
145
|
assert_equal 9, @mock.get
|
@@ -142,7 +151,7 @@ class TestFlexMock < Test::Unit::TestCase
|
|
142
151
|
end
|
143
152
|
|
144
153
|
def test_respond_to_returns_true_for_explicit_methods
|
145
|
-
@mock.mock_handle(:xyz)
|
154
|
+
s { @mock.mock_handle(:xyz) }
|
146
155
|
assert(@mock.respond_to?(:xyz), "should respond to test")
|
147
156
|
end
|
148
157
|
|
@@ -164,7 +173,7 @@ class TestFlexMock < Test::Unit::TestCase
|
|
164
173
|
|
165
174
|
def test_method_returns_callable_proc
|
166
175
|
got_it = false
|
167
|
-
@mock.mock_handle(:xyzzy) { got_it = true }
|
176
|
+
s { @mock.mock_handle(:xyzzy) { got_it = true } }
|
168
177
|
method_proc = @mock.method(:xyzzy)
|
169
178
|
assert_not_nil method_proc
|
170
179
|
method_proc.call
|
@@ -178,3 +187,41 @@ class TestFlexMock < Test::Unit::TestCase
|
|
178
187
|
method_proc.call
|
179
188
|
end
|
180
189
|
end
|
190
|
+
|
191
|
+
class TestDeprecatedOrderingMethods < Test::Unit::TestCase
|
192
|
+
include FlexMock::TestCase
|
193
|
+
include FlexMock::RedirectError
|
194
|
+
|
195
|
+
def test_deprecated_ordering_methods
|
196
|
+
flexmock(:x).should_receive(:msg).globally.ordered(:testgroup)
|
197
|
+
assert_equal({ :testgroup => 1 }, flexmock_groups)
|
198
|
+
message = redirect_error do
|
199
|
+
assert_equal({ :testgroup => 1 }, mock_groups)
|
200
|
+
end
|
201
|
+
assert_match(/deprecated/i, message)
|
202
|
+
assert_match(/\bmock_groups/, message)
|
203
|
+
assert_match(/\bflexmock_groups/, message)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
class TestAnyInstance < Test::Unit::TestCase
|
208
|
+
include FlexMock::TestCase
|
209
|
+
include FlexMock::RedirectError
|
210
|
+
|
211
|
+
class Dog
|
212
|
+
def bark
|
213
|
+
:woof
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_any_instance_still_works_for_backwards_compatibility
|
218
|
+
message = redirect_error do
|
219
|
+
flexstub(Dog).any_instance do |obj|
|
220
|
+
obj.should_receive(:bark).and_return(:whimper)
|
221
|
+
assert_match(/deprecated/, message)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
m = Dog.new
|
225
|
+
assert_equal :whimper, m.bark
|
226
|
+
end
|
227
|
+
end
|
data/test/test_example.rb
CHANGED
@@ -21,14 +21,15 @@ class TemperatureSampler
|
|
21
21
|
total = (0...3).collect { @sensor.read_temperature }.inject { |i, s| i + s }
|
22
22
|
total / 3.0
|
23
23
|
end
|
24
|
-
|
25
24
|
end
|
26
25
|
|
27
26
|
class TestTemperatureSampler < Test::Unit::TestCase
|
27
|
+
include FlexMock::TestCase
|
28
|
+
|
28
29
|
def test_tempurature_sampler
|
29
30
|
readings = [10, 12, 14]
|
30
|
-
mock_sensor =
|
31
|
-
mock_sensor.
|
31
|
+
mock_sensor = flexmock("sensor")
|
32
|
+
mock_sensor.should_receive(:read_temperature).and_return { readings.shift }
|
32
33
|
sampler = TemperatureSampler.new(mock_sensor)
|
33
34
|
assert_equal 12, sampler.average_temp
|
34
35
|
end
|
@@ -36,7 +36,7 @@ module ExtendedShouldReceiveTests
|
|
36
36
|
def test_count_contraints_apply_to_all_expectations
|
37
37
|
@mock.should_receive(:foo, :bar => :baz).once
|
38
38
|
@obj.foo
|
39
|
-
assert_raise(Test::Unit::AssertionFailedError) { @mock.
|
39
|
+
assert_raise(Test::Unit::AssertionFailedError) { @mock.flexmock_verify }
|
40
40
|
end
|
41
41
|
|
42
42
|
def test_multiple_should_receives_are_allowed
|
data/test/test_flexmodel.rb
CHANGED
@@ -5,17 +5,33 @@ require 'test/unit'
|
|
5
5
|
class DummyModel
|
6
6
|
end
|
7
7
|
|
8
|
+
class ChildModel < DummyModel
|
9
|
+
end
|
10
|
+
|
8
11
|
######################################################################
|
9
12
|
class TestFlexModel < Test::Unit::TestCase
|
10
13
|
include FlexMock::TestCase
|
11
14
|
|
12
15
|
def test_initial_conditions
|
13
16
|
model = flexmock(:model, DummyModel)
|
14
|
-
assert_match(/^DummyModel_\d+/, model.
|
17
|
+
assert_match(/^DummyModel_\d+/, model.flexmock_name)
|
15
18
|
assert_equal model.id.to_s, model.to_params
|
16
19
|
assert ! model.new_record?
|
17
20
|
assert model.is_a?(DummyModel)
|
21
|
+
# TODO: Make these work!!!
|
18
22
|
assert_equal DummyModel, model.class
|
23
|
+
assert model.instance_of?(DummyModel)
|
24
|
+
assert model.kind_of?(DummyModel)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_classifying_mock_models
|
28
|
+
model = flexmock(:model, ChildModel)
|
29
|
+
|
30
|
+
assert model.kind_of?(ChildModel)
|
31
|
+
assert model.instance_of?(ChildModel)
|
32
|
+
|
33
|
+
assert model.kind_of?(DummyModel)
|
34
|
+
assert ! model.instance_of?(DummyModel)
|
19
35
|
end
|
20
36
|
|
21
37
|
def test_mock_models_have_different_ids
|
data/test/test_naming.rb
CHANGED
@@ -13,13 +13,15 @@ require 'test/unit'
|
|
13
13
|
require 'flexmock'
|
14
14
|
|
15
15
|
class TestNaming < Test::Unit::TestCase
|
16
|
+
include FlexMock::TestCase
|
17
|
+
|
16
18
|
def test_name
|
17
|
-
m =
|
18
|
-
assert_equal "m", m.
|
19
|
+
m = flexmock("m")
|
20
|
+
assert_equal "m", m.flexmock_name
|
19
21
|
end
|
20
22
|
|
21
23
|
def test_name_in_no_handler_found_error
|
22
|
-
m =
|
24
|
+
m = flexmock("mmm")
|
23
25
|
ex = assert_raises(Test::Unit::AssertionFailedError) {
|
24
26
|
m.should_receive(:xx).with(1)
|
25
27
|
m.xx(2)
|
@@ -28,24 +30,57 @@ class TestNaming < Test::Unit::TestCase
|
|
28
30
|
end
|
29
31
|
|
30
32
|
def test_name_in_received_count_error
|
31
|
-
m =
|
33
|
+
m = flexmock("mmm")
|
32
34
|
ex = assert_raises(Test::Unit::AssertionFailedError) {
|
33
35
|
m.should_receive(:xx).once
|
34
|
-
m.
|
36
|
+
m.flexmock_verify
|
35
37
|
}
|
36
38
|
assert_match(/'mmm'/, ex.message)
|
37
39
|
end
|
38
40
|
|
39
41
|
def test_naming_with_use
|
40
42
|
FlexMock.use("blah") do |m|
|
41
|
-
assert_equal "blah", m.
|
43
|
+
assert_equal "blah", m.flexmock_name
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
45
47
|
def test_naming_with_multiple_mocks_in_use
|
46
48
|
FlexMock.use("blah", "yuk") do |a, b|
|
47
|
-
assert_equal "blah", a.
|
48
|
-
assert_equal "yuk", b.
|
49
|
+
assert_equal "blah", a.flexmock_name
|
50
|
+
assert_equal "yuk", b.flexmock_name
|
49
51
|
end
|
50
52
|
end
|
53
|
+
|
54
|
+
def test_inspect_returns_reasonable_name
|
55
|
+
FlexMock.use("XYZZY") do |m|
|
56
|
+
assert_equal "XYZZY", m.flexmock_name
|
57
|
+
assert_equal "<FlexMock:XYZZY>", m.inspect
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_mock_can_override_inspect
|
62
|
+
FlexMock.use("XYZZY") do |m|
|
63
|
+
m.should_receive(:inspect).with_no_args.and_return("MOCK-INSPECT")
|
64
|
+
assert_equal "MOCK-INSPECT", m.inspect
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Dummy
|
69
|
+
def inspect
|
70
|
+
"DUMMY-INSPECT"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_partial_mocks_use_original_inspect
|
75
|
+
dummy = Dummy.new
|
76
|
+
flexmock(dummy).should_receive(:msg)
|
77
|
+
assert_equal "DUMMY-INSPECT", dummy.inspect
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_partial_mocks_can_override_inspect
|
81
|
+
dummy = Dummy.new
|
82
|
+
flexmock(dummy).should_receive(:inspect).and_return("MOCK-INSPECT")
|
83
|
+
assert_equal "MOCK-INSPECT", dummy.inspect
|
84
|
+
end
|
51
85
|
end
|
86
|
+
|