minitest_to_rspec 0.11.0 → 0.12.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 +3 -2
- data/CHANGELOG.md +19 -0
- data/README.md +3 -0
- data/lib/minitest_to_rspec/converter.rb +2 -2
- data/lib/minitest_to_rspec/input/model/base.rb +15 -0
- data/lib/minitest_to_rspec/input/model/call.rb +185 -0
- data/lib/minitest_to_rspec/input/model/defn.rb +37 -0
- data/lib/minitest_to_rspec/input/model/hash_exp.rb +24 -0
- data/lib/minitest_to_rspec/input/model/iter.rb +89 -0
- data/lib/minitest_to_rspec/input/model/klass.rb +102 -0
- data/lib/minitest_to_rspec/input/processor.rb +40 -0
- data/lib/minitest_to_rspec/input/subprocessors/base.rb +91 -0
- data/lib/minitest_to_rspec/input/subprocessors/call.rb +279 -0
- data/lib/minitest_to_rspec/input/subprocessors/defn.rb +64 -0
- data/lib/minitest_to_rspec/input/subprocessors/iter.rb +148 -0
- data/lib/minitest_to_rspec/input/subprocessors/klass.rb +101 -0
- data/lib/minitest_to_rspec/minitest/stub.rb +85 -0
- data/lib/minitest_to_rspec/{expression_builders → rspec}/stub.rb +3 -2
- data/lib/minitest_to_rspec/type.rb +1 -1
- data/lib/minitest_to_rspec/version.rb +1 -1
- metadata +17 -18
- data/lib/minitest_to_rspec/model/base.rb +0 -13
- data/lib/minitest_to_rspec/model/call.rb +0 -165
- data/lib/minitest_to_rspec/model/calls/once.rb +0 -13
- data/lib/minitest_to_rspec/model/calls/twice.rb +0 -13
- data/lib/minitest_to_rspec/model/defn.rb +0 -27
- data/lib/minitest_to_rspec/model/hash_exp.rb +0 -20
- data/lib/minitest_to_rspec/model/iter.rb +0 -79
- data/lib/minitest_to_rspec/model/klass.rb +0 -100
- data/lib/minitest_to_rspec/processor.rb +0 -38
- data/lib/minitest_to_rspec/subprocessors/base.rb +0 -89
- data/lib/minitest_to_rspec/subprocessors/call.rb +0 -391
- data/lib/minitest_to_rspec/subprocessors/defn.rb +0 -49
- data/lib/minitest_to_rspec/subprocessors/iter.rb +0 -138
- data/lib/minitest_to_rspec/subprocessors/klass.rb +0 -99
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e8f9207756131c32b3622da37695b0da546d38749df1d22ae1f7d1daa1f1f1a5
|
4
|
+
data.tar.gz: dfb8bb34cd541ed7172822c98ce84db91bb7b99af0bd3f01d907da50a0472af1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45025ed9cdf045697a49d0571811fa14919ba91868044630de17f4436a0f1ffa32c6f172a9b8518eb7139dadce949798caa6a37828101303892d1055c3522001
|
7
|
+
data.tar.gz: 3ea95cd4da9200505c5c23bf072d084aa37d4994add74f8689e9766061046aa8a68ce6f66adb5c10d325143b3e73a878a86bafc5693088e5016fc4a4d01c7385
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -17,6 +17,25 @@ of [keepachangelog.com][2].
|
|
17
17
|
|
18
18
|
- None
|
19
19
|
|
20
|
+
## 0.12.0 (2018-03-11)
|
21
|
+
|
22
|
+
### Breaking Changes
|
23
|
+
|
24
|
+
- None
|
25
|
+
|
26
|
+
### Added
|
27
|
+
|
28
|
+
- [#21](https://github.com/jaredbeck/minitest_to_rspec/pull/21) -
|
29
|
+
convert `setup`/`teardown` methods to `before`/`after` blocks
|
30
|
+
- [#20](https://github.com/jaredbeck/minitest_to_rspec/pull/20) -
|
31
|
+
Support `refute_raise[s]`
|
32
|
+
- [#18](https://github.com/jaredbeck/minitest_to_rspec/pull/18) -
|
33
|
+
Support namespaced exceptions for assert_raise[s]
|
34
|
+
|
35
|
+
### Fixed
|
36
|
+
|
37
|
+
- None
|
38
|
+
|
20
39
|
## 0.11.0 (2017-11-10)
|
21
40
|
|
22
41
|
### Breaking Changes
|
data/README.md
CHANGED
@@ -97,6 +97,8 @@ assert_not_nil | |
|
|
97
97
|
[assert_raises][13] | 0..2 | Minitest
|
98
98
|
refute | |
|
99
99
|
refute_equal | |
|
100
|
+
[refute_raise][39] | |
|
101
|
+
[refute_raises][39] | |
|
100
102
|
|
101
103
|
## Supported Mocha
|
102
104
|
|
@@ -160,3 +162,4 @@ This project would not be possible without [ruby_parser][14],
|
|
160
162
|
[36]: http://www.rubydoc.info/github/thoughtbot/shoulda-context/master/Shoulda/Context/ClassMethods#context-instance_method
|
161
163
|
[37]: http://www.rubydoc.info/github/thoughtbot/shoulda-context/master/Shoulda/Context/Context#setup-instance_method
|
162
164
|
[38]: http://www.rubydoc.info/github/thoughtbot/shoulda-context/master/Shoulda/Context/ClassMethods#should-instance_method
|
165
|
+
[39]: https://github.com/jaredbeck/minitest_to_rspec/pull/20
|
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
require 'ruby_parser'
|
4
4
|
require 'ruby2ruby'
|
5
|
-
|
5
|
+
require 'minitest_to_rspec/input/processor'
|
6
6
|
require_relative 'errors'
|
7
7
|
|
8
8
|
module MinitestToRspec
|
9
9
|
# Converts strings of minitest code. Does not read or write files.
|
10
10
|
class Converter
|
11
11
|
def initialize(rails: false, mocha: false)
|
12
|
-
@processor = Processor.new(rails, mocha)
|
12
|
+
@processor = Input::Processor.new(rails, mocha)
|
13
13
|
end
|
14
14
|
|
15
15
|
# - `input` - Contents of a ruby file.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_to_rspec/sexp_assertions'
|
4
|
+
|
5
|
+
module MinitestToRspec
|
6
|
+
module Input
|
7
|
+
module Model
|
8
|
+
# Input models are simple data objects
|
9
|
+
# representing the S-expressions they are named after.
|
10
|
+
class Base
|
11
|
+
include SexpAssertions
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_to_rspec/input/model/base'
|
4
|
+
|
5
|
+
module MinitestToRspec
|
6
|
+
module Input
|
7
|
+
module Model
|
8
|
+
# Data object. Represents a `:call` s-expression.
|
9
|
+
class Call < Base
|
10
|
+
attr_reader :original
|
11
|
+
|
12
|
+
def initialize(exp)
|
13
|
+
assert_sexp_type(:call, exp)
|
14
|
+
@exp = exp.dup
|
15
|
+
@original = exp.dup
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def assert_difference?(exp)
|
20
|
+
exp.sexp_type == :call && new(exp).assert_difference?
|
21
|
+
end
|
22
|
+
|
23
|
+
def assert_no_difference?(exp)
|
24
|
+
exp.sexp_type == :call && new(exp).assert_no_difference?
|
25
|
+
end
|
26
|
+
|
27
|
+
def assert_nothing_raised?(exp)
|
28
|
+
exp.sexp_type == :call && new(exp).assert_nothing_raised?
|
29
|
+
end
|
30
|
+
|
31
|
+
def assert_raise?(exp)
|
32
|
+
exp.sexp_type == :call && new(exp).assert_raise?
|
33
|
+
end
|
34
|
+
|
35
|
+
def assert_raises?(exp)
|
36
|
+
exp.sexp_type == :call && new(exp).assert_raises?
|
37
|
+
end
|
38
|
+
|
39
|
+
def refute_raise?(exp)
|
40
|
+
exp.sexp_type == :call && new(exp).refute_raise?
|
41
|
+
end
|
42
|
+
|
43
|
+
def refute_raises?(exp)
|
44
|
+
exp.sexp_type == :call && new(exp).refute_raises?
|
45
|
+
end
|
46
|
+
|
47
|
+
def method_name?(exp, name)
|
48
|
+
exp.sexp_type == :call && new(exp).method_name.to_s == name.to_s
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def arguments
|
53
|
+
@exp[3..-1] || []
|
54
|
+
end
|
55
|
+
|
56
|
+
def argument_types
|
57
|
+
arguments.map(&:sexp_type)
|
58
|
+
end
|
59
|
+
|
60
|
+
def assert_difference?
|
61
|
+
return false unless method_name == :assert_difference
|
62
|
+
[[:str], %i[str lit]].include?(argument_types)
|
63
|
+
end
|
64
|
+
|
65
|
+
def assert_no_difference?
|
66
|
+
method_name == :assert_no_difference &&
|
67
|
+
arguments.length == 1 &&
|
68
|
+
arguments[0].sexp_type == :str
|
69
|
+
end
|
70
|
+
|
71
|
+
def assert_nothing_raised?
|
72
|
+
method_name == :assert_nothing_raised && arguments.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
def assert_raise?
|
76
|
+
method_name == :assert_raise && raise_error_args?
|
77
|
+
end
|
78
|
+
|
79
|
+
def assert_raises?
|
80
|
+
method_name == :assert_raises && raise_error_args?
|
81
|
+
end
|
82
|
+
|
83
|
+
def refute_raise?
|
84
|
+
method_name == :refute_raise && raise_error_args?
|
85
|
+
end
|
86
|
+
|
87
|
+
def refute_raises?
|
88
|
+
method_name == :refute_raises && raise_error_args?
|
89
|
+
end
|
90
|
+
|
91
|
+
def calls_in_receiver_chain
|
92
|
+
receiver_chain.each_with_object([]) do |e, a|
|
93
|
+
next unless sexp_type?(:call, e)
|
94
|
+
a << self.class.new(e)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_call_in_receiver_chain(method_names)
|
99
|
+
name_array = [method_names].flatten
|
100
|
+
calls_in_receiver_chain.find { |i|
|
101
|
+
name_array.include?(i.method_name)
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
def method_name
|
106
|
+
@exp[2]
|
107
|
+
end
|
108
|
+
|
109
|
+
def num_arguments
|
110
|
+
arguments.length
|
111
|
+
end
|
112
|
+
|
113
|
+
def one_string_argument?
|
114
|
+
arguments.length == 1 && string?(arguments[0])
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns true if arguments can be processed into RSpec's `raise_error`
|
118
|
+
# matcher. When the last argument is a string, it represents the
|
119
|
+
# assertion failure message, which will be discarded later.
|
120
|
+
def raise_error_args?
|
121
|
+
arg_types = arguments.map(&:sexp_type)
|
122
|
+
[[], [:str], [:const], %i[const str], [:colon2]].include?(arg_types)
|
123
|
+
end
|
124
|
+
|
125
|
+
def receiver
|
126
|
+
@exp[1]
|
127
|
+
end
|
128
|
+
|
129
|
+
# Consider the following chain of method calls:
|
130
|
+
#
|
131
|
+
# @a.b.c
|
132
|
+
#
|
133
|
+
# whose S-expression is
|
134
|
+
#
|
135
|
+
# s(:call, s(:call, s(:call, nil, :a), :b), :c)
|
136
|
+
#
|
137
|
+
# the "receiver chain" is
|
138
|
+
#
|
139
|
+
# [
|
140
|
+
# s(:call, s(:call, nil, :a), :b),
|
141
|
+
# s(:call, nil, :a),
|
142
|
+
# nil
|
143
|
+
# ]
|
144
|
+
#
|
145
|
+
# The order of the returned array matches the order in which
|
146
|
+
# messages are received, i.e. the order of execution.
|
147
|
+
#
|
148
|
+
# Note that the final receiver `nil` is included. This `nil`
|
149
|
+
# represents the implicit receiver, e.g. `self` or `main`.
|
150
|
+
#
|
151
|
+
def receiver_chain
|
152
|
+
receivers = []
|
153
|
+
ptr = @exp
|
154
|
+
while sexp_type?(:call, ptr)
|
155
|
+
receivers << ptr[1]
|
156
|
+
ptr = ptr[1]
|
157
|
+
end
|
158
|
+
receivers
|
159
|
+
end
|
160
|
+
|
161
|
+
def receiver_chain_include?(method_name)
|
162
|
+
receiver_chain.compact.any? { |r|
|
163
|
+
self.class.method_name?(r, method_name)
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
def require_test_helper?
|
168
|
+
method_name == :require &&
|
169
|
+
one_string_argument? &&
|
170
|
+
arguments[0][1] == 'test_helper'
|
171
|
+
end
|
172
|
+
|
173
|
+
def question_mark_method?
|
174
|
+
method_name.to_s.end_with?('?')
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def string?(exp)
|
180
|
+
exp.sexp_type == :str
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_to_rspec/input/model/base'
|
4
|
+
|
5
|
+
module MinitestToRspec
|
6
|
+
module Input
|
7
|
+
module Model
|
8
|
+
# Data object. Represents a `:defn` s-expression.
|
9
|
+
class Defn < Base
|
10
|
+
def initialize(exp)
|
11
|
+
assert_sexp_type(:defn, exp)
|
12
|
+
@exp = exp.dup
|
13
|
+
end
|
14
|
+
|
15
|
+
def body
|
16
|
+
@exp[3..-1]
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_name
|
20
|
+
@exp[1].to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_method?
|
24
|
+
method_name.start_with?('test_')
|
25
|
+
end
|
26
|
+
|
27
|
+
def setup?
|
28
|
+
method_name == 'setup'
|
29
|
+
end
|
30
|
+
|
31
|
+
def teardown?
|
32
|
+
method_name == 'teardown'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_to_rspec/input/model/base'
|
4
|
+
|
5
|
+
module MinitestToRspec
|
6
|
+
module Input
|
7
|
+
module Model
|
8
|
+
# Data object. Represents a `:hash` S-expression.
|
9
|
+
class HashExp < Base
|
10
|
+
def initialize(sexp)
|
11
|
+
assert_sexp_type(:hash, sexp)
|
12
|
+
@exp = sexp.dup
|
13
|
+
end
|
14
|
+
|
15
|
+
# A slightly nicer implementation would be:
|
16
|
+
# `@exp[1..-1].each_slice(2).to_h`
|
17
|
+
# but that would require ruby >= 2.1
|
18
|
+
def to_h
|
19
|
+
Hash[@exp[1..-1].each_slice(2).to_a]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_to_rspec/input/model/base'
|
4
|
+
|
5
|
+
module MinitestToRspec
|
6
|
+
module Input
|
7
|
+
module Model
|
8
|
+
# Data object. Represents an `:iter` s-expression.
|
9
|
+
class Iter < Base
|
10
|
+
def initialize(exp)
|
11
|
+
assert_sexp_type(:iter, exp)
|
12
|
+
@exp = exp.dup
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](*args)
|
16
|
+
@exp[*args]
|
17
|
+
end
|
18
|
+
|
19
|
+
def assert_difference?
|
20
|
+
!empty? && Model::Call.assert_difference?(@exp[1])
|
21
|
+
end
|
22
|
+
|
23
|
+
def assert_no_difference?
|
24
|
+
!empty? && Model::Call.assert_no_difference?(@exp[1])
|
25
|
+
end
|
26
|
+
|
27
|
+
def assert_nothing_raised?
|
28
|
+
!empty? && Model::Call.assert_nothing_raised?(@exp[1])
|
29
|
+
end
|
30
|
+
|
31
|
+
def assert_raise?
|
32
|
+
!empty? && Model::Call.assert_raise?(@exp[1])
|
33
|
+
end
|
34
|
+
|
35
|
+
def assert_raises?
|
36
|
+
!empty? && Model::Call.assert_raises?(@exp[1])
|
37
|
+
end
|
38
|
+
|
39
|
+
def refute_raise?
|
40
|
+
!empty? && Model::Call.refute_raise?(@exp[1])
|
41
|
+
end
|
42
|
+
|
43
|
+
def refute_raises?
|
44
|
+
!empty? && Model::Call.refute_raises?(@exp[1])
|
45
|
+
end
|
46
|
+
|
47
|
+
def block
|
48
|
+
@exp[3]
|
49
|
+
end
|
50
|
+
|
51
|
+
def call
|
52
|
+
@exp[1]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Not to be confused with block arguments.
|
56
|
+
def call_arguments
|
57
|
+
call_obj.arguments
|
58
|
+
end
|
59
|
+
|
60
|
+
def call_obj
|
61
|
+
Model::Call.new(call)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Enumerates children, skipping the base `call` and
|
65
|
+
# starting with the block arguments, then each `:call` in
|
66
|
+
# the block.
|
67
|
+
def each
|
68
|
+
@exp[2..-1].each do |e| yield(e) end
|
69
|
+
end
|
70
|
+
|
71
|
+
def empty?
|
72
|
+
@exp.length == 1 # just the sexp_type
|
73
|
+
end
|
74
|
+
|
75
|
+
def setup?
|
76
|
+
!empty? && Model::Call.method_name?(@exp[1], :setup)
|
77
|
+
end
|
78
|
+
|
79
|
+
def teardown?
|
80
|
+
!empty? && Model::Call.method_name?(@exp[1], :teardown)
|
81
|
+
end
|
82
|
+
|
83
|
+
def sexp
|
84
|
+
@exp
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minitest_to_rspec/input/model/base'
|
4
|
+
|
5
|
+
module MinitestToRspec
|
6
|
+
module Input
|
7
|
+
module Model
|
8
|
+
# Data object. Represents a `:class` S-expression.
|
9
|
+
class Klass < Base
|
10
|
+
def initialize(exp)
|
11
|
+
assert_sexp_type(:class, exp)
|
12
|
+
@exp = exp.dup
|
13
|
+
assert_valid_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def action_controller_test_case?
|
17
|
+
lineage?(parent, %i[ActionController TestCase])
|
18
|
+
end
|
19
|
+
|
20
|
+
def action_mailer_test_case?
|
21
|
+
lineage?(parent, %i[ActionMailer TestCase])
|
22
|
+
end
|
23
|
+
|
24
|
+
def active_support_test_case?
|
25
|
+
lineage?(parent, %i[ActiveSupport TestCase])
|
26
|
+
end
|
27
|
+
|
28
|
+
# Raise an error if we don't know now to process the name
|
29
|
+
# of this class. Specifically, classes with module-shorthand.
|
30
|
+
def assert_valid_name
|
31
|
+
if name.is_a?(Symbol)
|
32
|
+
# Valid
|
33
|
+
elsif name.respond_to?(:sexp_type) && name.sexp_type == :colon2
|
34
|
+
raise ModuleShorthandError
|
35
|
+
else
|
36
|
+
raise ProcessingError, "Unexpected class expression: #{name}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def block?
|
41
|
+
!block.empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
def block
|
45
|
+
@_block ||= @exp[3..-1] || []
|
46
|
+
end
|
47
|
+
|
48
|
+
def draper_test_case?
|
49
|
+
lineage?(parent, %i[Draper TestCase])
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the name of the class. Examples:
|
53
|
+
#
|
54
|
+
# - Banana #=> :Banana
|
55
|
+
# - Fruit::Banana #=> s(:colon2, s(:const, :Fruit), :Banana)
|
56
|
+
#
|
57
|
+
# Note that the latter (module shorthand) is not supported
|
58
|
+
# by MinitestToRspec. See `#assert_valid_name`.
|
59
|
+
#
|
60
|
+
def name
|
61
|
+
@exp[1]
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the "inheritance". Examples:
|
65
|
+
#
|
66
|
+
# - Inherit nothing #=> nil
|
67
|
+
# - Inherit Foo #=> s(:const, :Foo)
|
68
|
+
# - Inherit Bar::Foo #=> s(:colon2, s(:const, :Bar), :Foo)
|
69
|
+
#
|
70
|
+
def parent
|
71
|
+
@_parent ||= @exp[2]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns true if `@exp` inherits from, e.g. ActiveSupport::TestCase.
|
75
|
+
# TODO: Other test case parent classes.
|
76
|
+
def test_case?
|
77
|
+
return false unless sexp_type?(:colon2, parent)
|
78
|
+
active_support_test_case? ||
|
79
|
+
action_controller_test_case? ||
|
80
|
+
action_mailer_test_case? ||
|
81
|
+
draper_test_case?
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def ancestor_name(exp, index)
|
87
|
+
assert_sexp_type(:colon2, exp)
|
88
|
+
ancestor = exp[index + 1]
|
89
|
+
sexp_type?(:const, ancestor) ? ancestor[1] : ancestor
|
90
|
+
end
|
91
|
+
|
92
|
+
def lineage?(exp, names)
|
93
|
+
assert_sexp_type(:colon2, exp)
|
94
|
+
exp.length == names.length + 1 &&
|
95
|
+
names.each_with_index.all? { |name, ix|
|
96
|
+
name.to_sym == ancestor_name(exp, ix).to_sym
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|