kintama 0.1.11 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/lib/kintama.rb +19 -5
  3. data/lib/kintama/assertions.rb +4 -0
  4. data/lib/kintama/context.rb +58 -47
  5. data/lib/kintama/mocha.rb +13 -0
  6. data/lib/kintama/no_conflict.rb +2 -0
  7. data/lib/kintama/reporter.rb +3 -3
  8. data/test/{automatic_running_test.rb → integration/automatic_running_test.rb} +5 -5
  9. data/test/{line_based_running_test.rb → integration/line_based_running_test.rb} +13 -13
  10. data/test/reporters/base_reporter_test.rb +31 -101
  11. data/test/reporters/inline_reporter_test.rb +23 -35
  12. data/test/reporters/verbose_reporter_test.rb +77 -76
  13. data/test/test_helper.rb +92 -0
  14. data/test/{assertions_test.rb → unit/assertions_test.rb} +8 -0
  15. data/test/unit/context_test.rb +15 -0
  16. data/test/unit/runner_test.rb +87 -0
  17. data/test/{test_and_subcontext_access_test.rb → unit/test_and_subcontext_access_test.rb} +5 -32
  18. data/test/usage/01_basic_usage_test.rb +131 -0
  19. data/test/usage/02_setup_test.rb +98 -0
  20. data/test/usage/03_teardown_test.rb +120 -0
  21. data/test/usage/04_pending_tests_test.rb +16 -0
  22. data/test/usage/05_aliases_test.rb +73 -0
  23. data/test/usage/06_defining_methods_in_tests_test.rb +202 -0
  24. data/test/usage/07_exceptions_test.rb +42 -0
  25. data/test/usage/08_start_and_finish_test.rb +252 -0
  26. data/test/usage/09_expectations_and_mocking_test.rb +86 -0
  27. data/test/usage/10_let_and_subject_test.rb +125 -0
  28. data/test/usage/11_matcher_test.rb +148 -0
  29. data/test/usage/12_action_test.rb +118 -0
  30. metadata +36 -42
  31. data/test/aliases_test.rb +0 -26
  32. data/test/exceptions_test.rb +0 -40
  33. data/test/kintama_test.rb +0 -114
  34. data/test/matcher_test.rb +0 -80
  35. data/test/method_behaviour_test.rb +0 -176
  36. data/test/pending_test_and_context.rb +0 -20
  37. data/test/setup_test.rb +0 -107
  38. data/test/start_and_finish_test.rb +0 -94
  39. data/test/teardown_test.rb +0 -106
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5a59c25b77b6889c784049956601ab4ba2000c31
4
+ data.tar.gz: 8aefa27d4b0581ea3799f3c4faba2ae861a7cc5c
5
+ SHA512:
6
+ metadata.gz: b4fd4b530520996ccc0a1014cc9146d2ea0a934e6d024d009e37fc0c9efb61271033463b87b890111d1e2d5aac38641fa2e76e197d79ffad196e68a53d37f9bd
7
+ data.tar.gz: 8c5c445a35e8243820690ba4fc2ee1944144ae779562c7b8d7d4305341c61e6e679a9b1a8841c65248dba1d50bc173450837507ae13957682211e23922c7258b
@@ -11,6 +11,10 @@ module Kintama
11
11
  autoload :Assertions, 'kintama/assertions'
12
12
 
13
13
  class << self
14
+ def no_conflict?
15
+ ENV["KINTAMA_NO_CONFLICT"]
16
+ end
17
+
14
18
  def reset
15
19
  @default_context = Class.new(Runnable)
16
20
  @default_context.send(:include, Kintama::Context)
@@ -43,6 +47,16 @@ module Kintama
43
47
  default_context.teardown(&block)
44
48
  end
45
49
 
50
+ def on_start(&block)
51
+ default_context.on_start(&block)
52
+ end
53
+ alias_method :before_all, :on_start
54
+
55
+ def on_finish(&block)
56
+ default_context.on_finish(&block)
57
+ end
58
+ alias_method :after_all, :on_finish
59
+
46
60
  # Makes behaviour available within tests:
47
61
  #
48
62
  # module SomeModule
@@ -76,9 +90,7 @@ module Kintama
76
90
 
77
91
  opts.on("-r", "--reporter NAME",
78
92
  "Use the given reporter (inline or verbose)") do |reporter|
79
- puts "reporter!"
80
93
  options.reporter = Kintama::Reporter.called(reporter)
81
- p options.reporter
82
94
  end
83
95
  opts.on("-l", "--line LINE",
84
96
  "Run the test or context on the given line") do |line|
@@ -127,9 +139,11 @@ module Kintama
127
139
  end
128
140
  end
129
141
 
130
- [:context, :given, :describe, :testcase].each do |method|
131
- unless self.respond_to?(method)
132
- eval %|def #{method}(*args, &block); Kintama.#{method}(*args, &block); end|
142
+ unless Kintama.no_conflict?
143
+ [:context, :given, :describe, :testcase].each do |method|
144
+ unless self.respond_to?(method)
145
+ eval %|def #{method}(*args, &block); Kintama.#{method}(*args, &block); end|
146
+ end
133
147
  end
134
148
  end
135
149
 
@@ -34,6 +34,10 @@ module Kintama
34
34
  assert thing.is_a?(klass), message
35
35
  end
36
36
 
37
+ def assert_same(expected, actual, message="Expected #{expected.inspect} (oid=#{expected.object_id}) to be the same as #{actual.inspect} (oid=#{actual.object_id})")
38
+ assert actual.equal?(expected), message
39
+ end
40
+
37
41
  def assert_same_elements(expected, object, message = "#{object.inspect} does not contain the same elements as #{expected.inspect}")
38
42
  assert Set.new(expected) == Set.new(object), message
39
43
  end
@@ -1,9 +1,46 @@
1
1
  module Kintama
2
2
  module Context
3
- def setup # noop
3
+ def setup
4
+ (setup_blocks + after_setup_blocks).each do |block|
5
+ instance_eval(&block)
6
+ end
7
+ end
8
+
9
+ def teardown
10
+ blocks = teardown_blocks
11
+ blocks.each do |block|
12
+ instance_eval(&block)
13
+ end
4
14
  end
5
15
 
6
- def teardown # noop
16
+ def setup_blocks
17
+ context = self.class
18
+ blocks = context.setup_blocks
19
+ while(context.superclass.respond_to?(:setup_blocks))
20
+ context = context.superclass
21
+ blocks.unshift(*context.setup_blocks)
22
+ end
23
+ blocks
24
+ end
25
+
26
+ def after_setup_blocks
27
+ context = self.class
28
+ blocks = context.after_setup_blocks
29
+ while(context.superclass.respond_to?(:after_setup_blocks))
30
+ context = context.superclass
31
+ blocks.unshift(*context.after_setup_blocks)
32
+ end
33
+ blocks
34
+ end
35
+
36
+ def teardown_blocks
37
+ context = self.class
38
+ blocks = context.teardown_blocks
39
+ while(context.superclass.respond_to?(:teardown_blocks))
40
+ context = context.superclass
41
+ blocks.push(*context.teardown_blocks)
42
+ end
43
+ blocks
7
44
  end
8
45
 
9
46
  def self.included(base)
@@ -21,7 +58,7 @@ module Kintama
21
58
  end
22
59
  end
23
60
 
24
- def find_definition_1_9(&block)
61
+ def find_definition_yarv(&block)
25
62
  block.source_location if block
26
63
  end
27
64
 
@@ -35,8 +72,8 @@ module Kintama
35
72
  def find_definition(&block)
36
73
  if defined? RUBY_ENGINE
37
74
  case RUBY_ENGINE
38
- when "ruby"
39
- RUBY_VERSION =~ /^1.9/ ? find_definition_1_9(&block) : find_definition_1_8
75
+ when "ruby", "jruby"
76
+ Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9') ? find_definition_yarv(&block) : find_definition_1_8
40
77
  when "rbx"
41
78
  find_definition_rbx(&block)
42
79
  end
@@ -67,6 +104,10 @@ module Kintama
67
104
  @setup_blocks ||= []
68
105
  end
69
106
 
107
+ def after_setup_blocks
108
+ @after_setup_blocks ||= []
109
+ end
110
+
70
111
  def teardown_blocks
71
112
  @teardown_blocks ||= []
72
113
  end
@@ -75,26 +116,17 @@ module Kintama
75
116
  # It will also be run for any subcontexts, before their own setup blocks
76
117
  def setup(&block)
77
118
  self.setup_blocks << block
119
+ end
78
120
 
79
- # redefine setup for the current set of blocks
80
- blocks = self.setup_blocks
81
- define_method(:setup) do
82
- super()
83
- blocks.each { |b| instance_eval(&b) }
84
- end
121
+ def after_setup(&block)
122
+ self.after_setup_blocks << block
85
123
  end
124
+ alias_method :action, :after_setup
86
125
 
87
126
  # Define the teardown for this context.
88
127
  # It will also be run for any subcontexts, after their own teardown blocks
89
128
  def teardown(&block)
90
129
  self.teardown_blocks << block
91
-
92
- # redefine teardown for the current set of blocks
93
- blocks = self.teardown_blocks
94
- define_method(:teardown) do
95
- blocks.each { |b| instance_eval(&b) }
96
- super()
97
- end
98
130
  end
99
131
 
100
132
  def on_start_blocks
@@ -115,11 +147,16 @@ module Kintama
115
147
  end
116
148
  alias_method :after_all, :on_finish
117
149
 
150
+ def let(name, &block)
151
+ define_method(name) do
152
+ memo = "@__#{name}"
153
+ instance_variable_get(memo) || instance_variable_set(memo, instance_eval(&block))
154
+ end
155
+ end
156
+
118
157
  # Defines the subject of any matcher-based tests.
119
158
  def subject(&block)
120
- define_method(:subject) do
121
- @__subject ||= yield
122
- end
159
+ let("subject", &block)
123
160
  end
124
161
 
125
162
  # Define a test to run in this context.
@@ -199,28 +236,6 @@ module Kintama
199
236
  subcontexts.find { |s| s.name == name } || tests.find { |t| t.name == name }
200
237
  end
201
238
 
202
- def method_missing(name, *args, &block)
203
- if self[de_methodize(name)]
204
- self[de_methodize(name)]
205
- else
206
- begin
207
- super
208
- rescue NameError, NoMethodError => e
209
- if parent
210
- parent.send(name, *args, &block)
211
- else
212
- raise e
213
- end
214
- end
215
- end
216
- end
217
-
218
- def respond_to?(name)
219
- self[name] ||
220
- super ||
221
- (parent ? parent.respond_to?(name) : false)
222
- end
223
-
224
239
  # Runs all tests in this context and any subcontexts.
225
240
  # Returns true if all tests passed; otherwise false
226
241
  def run(reporter=nil)
@@ -257,10 +272,6 @@ module Kintama
257
272
 
258
273
  private
259
274
 
260
- def de_methodize(name)
261
- name.to_s.gsub("_", " ")
262
- end
263
-
264
275
  def ran_tests
265
276
  @ran_tests || []
266
277
  end
@@ -1,3 +1,4 @@
1
+ require 'kintama'
1
2
  require 'mocha/api'
2
3
 
3
4
  Kintama.include Mocha::API
@@ -10,3 +11,15 @@ Kintama.teardown do
10
11
  mocha_teardown
11
12
  end
12
13
  end
14
+
15
+ module Kintama::Mocha
16
+ module Expect
17
+ def expect(name, &block)
18
+ context do
19
+ setup(&block)
20
+ test("expect " + name) {}
21
+ end
22
+ end
23
+ end
24
+ end
25
+ Kintama.extend(Kintama::Mocha::Expect)
@@ -0,0 +1,2 @@
1
+ ENV["KINTAMA_NO_CONFLICT"] = "true"
2
+ require "kintama"
@@ -17,9 +17,9 @@ module Kintama
17
17
  end
18
18
 
19
19
  class Base
20
- attr_reader :runner
20
+ attr_reader :runner, :test_count
21
21
 
22
- def initialize
22
+ def initialize(*args)
23
23
  @test_count = 0
24
24
  end
25
25
 
@@ -157,4 +157,4 @@ module Kintama
157
157
  end
158
158
 
159
159
  end
160
- end
160
+ end
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  class AutomaticRunningTest < Test::Unit::TestCase
4
4
 
5
- def test_should_be_able_to_run_tests_automatically_when_file_is_loaded
5
+ def test_should_be_able_to_run_kintama_tests_automatically_when_file_is_loaded
6
6
  assert_passes write_test %{
7
7
  context "given a thing" do
8
8
  should "work" do
@@ -27,7 +27,7 @@ class AutomaticRunningTest < Test::Unit::TestCase
27
27
  "/tmp/kintama_tmp_test.rb"
28
28
  end
29
29
 
30
- def run_test(path)
30
+ def run_kintama_test(path)
31
31
  prev = ENV["KINTAMA_EXPLICITLY_DONT_RUN"]
32
32
  ENV["KINTAMA_EXPLICITLY_DONT_RUN"] = nil
33
33
  output = `ruby #{path}`
@@ -36,10 +36,10 @@ class AutomaticRunningTest < Test::Unit::TestCase
36
36
  end
37
37
 
38
38
  def assert_passes(path)
39
- assert_equal 0, run_test(path).exitstatus
39
+ assert_equal 0, run_kintama_test(path).exitstatus
40
40
  end
41
41
 
42
42
  def assert_fails(path)
43
- assert_equal 1, run_test(path).exitstatus
43
+ assert_equal 1, run_kintama_test(path).exitstatus
44
44
  end
45
- end
45
+ end
@@ -11,11 +11,11 @@ class LineBasedRunningTest < Test::Unit::TestCase
11
11
  flunk
12
12
  end
13
13
  end}
14
- assert_match /^#{passing("should run this test")}\n\n1 tests/, run_test(test_file, "--line 3")
15
- assert_match /^1 tests, 0 failures/, run_test(test_file, "--line 3")
14
+ assert_match /^#{passing("should run this test")}\n\n1 tests/, run_kintama_test(test_file, "--line 3")
15
+ assert_match /^1 tests, 0 failures/, run_kintama_test(test_file, "--line 3")
16
16
 
17
- # assert_match /^#{failing("should not run this test")}\n\n1 tests/, run_test(test_file, "--line 6")
18
- # assert_match /^1 tests, 1 failures/, run_test(test_file, "--line 6")
17
+ assert_match /^#{failing("should not run this test")}\n\n1 tests/, run_kintama_test(test_file, "--line 6")
18
+ assert_match /^1 tests, 1 failures/, run_kintama_test(test_file, "--line 6")
19
19
  end
20
20
 
21
21
  def test_should_be_able_to_run_the_test_by_giving_the_line_number_within_the_test_definition
@@ -28,8 +28,8 @@ class LineBasedRunningTest < Test::Unit::TestCase
28
28
  flunk
29
29
  end
30
30
  end}
31
- assert_match /^#{passing("should run this test")}\n\n1 tests/, run_test(test_file, "--line 4")
32
- assert_match /^#{failing("should not run this test")}\n\n1 tests/, run_test(test_file, "--line 7")
31
+ assert_match /^#{passing("should run this test")}\n\n1 tests/, run_kintama_test(test_file, "--line 4")
32
+ assert_match /^#{failing("should not run this test")}\n\n1 tests/, run_kintama_test(test_file, "--line 7")
33
33
  end
34
34
 
35
35
  def test_should_be_able_to_run_all_tests_within_a_context_when_line_falls_on_a_context
@@ -45,7 +45,7 @@ class LineBasedRunningTest < Test::Unit::TestCase
45
45
  end
46
46
  end
47
47
  end}
48
- assert_match /#{passing("should run this test")}\n#{passing("should run this test too")}\n\n2 tests/, run_test(test_file, "--line 6")
48
+ assert_match /#{passing("should run this test")}\n#{passing("should run this test too")}\n\n2 tests/, run_kintama_test(test_file, "--line 6")
49
49
  end
50
50
 
51
51
  def test_should_be_able_to_run_a_test_defined_in_a_second_top_level_context
@@ -59,7 +59,7 @@ class LineBasedRunningTest < Test::Unit::TestCase
59
59
  should "run this test" do
60
60
  end
61
61
  end}
62
- assert_match /#{passing("should run this test")}\n\n1 tests/, run_test(test_file, "--line 8")
62
+ assert_match /#{passing("should run this test")}\n\n1 tests/, run_kintama_test(test_file, "--line 8")
63
63
  end
64
64
 
65
65
  def test_should_print_out_the_full_nested_test_name
@@ -70,7 +70,7 @@ class LineBasedRunningTest < Test::Unit::TestCase
70
70
  end
71
71
  end
72
72
  end}
73
- assert_match /given a test\n that is nested deeply\n/, run_test(test_file, "--line 5")
73
+ assert_match /given a test\n that is nested deeply\n/, run_kintama_test(test_file, "--line 5")
74
74
  end
75
75
 
76
76
  def test_should_not_show_pending_tests_in_the_same_context_as_pending_when_not_targeted
@@ -80,7 +80,7 @@ class LineBasedRunningTest < Test::Unit::TestCase
80
80
  end
81
81
  should "ignore the pending test"
82
82
  end}
83
- assert_no_match /1 pending/, run_test(test_file, "--line 3")
83
+ assert_no_match /1 pending/, run_kintama_test(test_file, "--line 3")
84
84
  end
85
85
 
86
86
  def test_should_be_able_to_target_a_top_level_context
@@ -94,7 +94,7 @@ class LineBasedRunningTest < Test::Unit::TestCase
94
94
  should "run this too" do
95
95
  end
96
96
  end}
97
- assert_match /2 tests/, run_test(test_file, "--line 2")
97
+ assert_match /2 tests/, run_kintama_test(test_file, "--line 2")
98
98
  end
99
99
 
100
100
  def test_should_report_if_nothing_runnable_can_be_found_for_that_line
@@ -103,7 +103,7 @@ class LineBasedRunningTest < Test::Unit::TestCase
103
103
  should "not run this" do
104
104
  end
105
105
  end}
106
- assert_match /Nothing runnable found on line 1/, run_test(test_file, "--line 1")
106
+ assert_match /Nothing runnable found on line 1/, run_kintama_test(test_file, "--line 1")
107
107
  end
108
108
 
109
109
  private
@@ -115,7 +115,7 @@ class LineBasedRunningTest < Test::Unit::TestCase
115
115
  end
116
116
  end
117
117
 
118
- def run_test(test_content, options)
118
+ def run_kintama_test(test_content, options)
119
119
  path = "/tmp/kintama_tmp_test.rb"
120
120
  write_test(test_content.strip, path)
121
121
  prev = ENV["KINTAMA_EXPLICITLY_DONT_RUN"]
@@ -1,10 +1,7 @@
1
1
  require 'test_helper'
2
2
 
3
- class BaseReporterTest < Test::Unit::TestCase
4
-
5
- def setup
6
- @reporter = Kintama::Reporter::Base.new
7
- end
3
+ class BaseReporterTest < KintamaIntegrationTest
4
+ report_with Kintama::Reporter::Base
8
5
 
9
6
  def test_assert_output_works
10
7
  assert_output("yes\n") do
@@ -13,145 +10,78 @@ class BaseReporterTest < Test::Unit::TestCase
13
10
  end
14
11
 
15
12
  def test_should_print_summary_when_a_test_passes
16
- c = context "given something" do
13
+ context "given something" do
17
14
  should "pass" do
18
15
  assert true
19
16
  end
20
- end
21
- r = runner(c)
22
- capture_stdout { r.run(@reporter) }
23
- assert_match /^1 tests, 0 failures/, @reporter.test_summary
17
+ end.
18
+ should_output("1 tests, 0 failures")
24
19
  end
25
20
 
26
21
  def test_should_print_out_summary_when_multiple_tests_pass
27
- c = context "given something" do
22
+ context "given something" do
28
23
  should "pass" do
29
24
  assert true
30
25
  end
31
26
  should "also pass" do
32
27
  assert true
33
28
  end
34
- end
35
- r = runner(c)
36
- capture_stdout { r.run(@reporter) }
37
- assert_match /^2 tests, 0 failures/, @reporter.test_summary
29
+ end.
30
+ should_output("2 tests, 0 failures")
38
31
  end
39
32
 
40
33
  def test_should_print_out_summary_when_a_pending_test_exists
41
- c = context "given something" do
34
+ context "given something" do
42
35
  should "pass" do
43
36
  assert true
44
37
  end
45
38
  should "not be implemented yet"
46
- end
47
- r = runner(c)
48
- capture_stdout { r.run(@reporter) }
49
- assert_match /^2 tests, 0 failures, 1 pending/, @reporter.test_summary
39
+ end.
40
+ should_output("2 tests, 0 failures, 1 pending")
50
41
  end
51
42
 
52
43
  def test_should_print_out_failure_details_if_tests_fail
53
- c = context "given something" do
44
+ context "given something" do
54
45
  should "fail" do
55
46
  flunk
56
47
  end
57
48
  should "pass" do
58
49
  assert true
59
50
  end
60
- end
61
- r = runner(c)
62
- capture_stdout { r.run(@reporter) }
63
- assert_match /^1\) given something should fail:\n flunked\./, @reporter.failure_messages[0]
51
+ end.
52
+ should_output(%{
53
+ 1) given something should fail:
54
+ flunked
55
+ })
64
56
  end
65
57
 
66
58
  def test_should_print_out_the_test_duration
67
- c = context "given something" do
68
- should "pass" do
69
- assert true
70
- end
71
- end
72
- r = runner(c)
73
- capture_stdout { r.run(@reporter) }
74
- assert_match /^1 tests, 0 failures \(0\.\d+ seconds\)/, @reporter.test_summary
75
- end
76
-
77
- def test_should_be_able_to_run_tests_from_several_contexts
78
- c1 = context "given something" do
59
+ context "given something" do
79
60
  should "pass" do
80
61
  assert true
81
62
  end
82
- end
83
- c2 = context "given another thing" do
84
- should "also pass" do
85
- assert true
86
- end
87
- end
88
- r = runner(c1, c2)
89
- capture_stdout { r.run(@reporter) }
90
- assert_match /^2 tests, 0 failures/, @reporter.test_summary
91
- end
92
-
93
- def test_should_return_true_if_all_tests_pass
94
- c = context "given something" do
95
- should("pass") { assert true }
96
- should("also pass") { assert true }
97
- end
98
- capture_stdout do
99
- assert_equal true, runner(c).run(@reporter)
100
- end
101
- end
102
-
103
- def test_should_return_false_if_any_tests_fails
104
- c = context "given something" do
105
- should("pass") { assert true }
106
- should("fail") { flunk }
107
- end
108
- capture_stdout do
109
- assert_equal false, runner(c).run(@reporter)
110
- end
111
- end
112
-
113
- def test_should_only_run_each_context_once
114
- Kintama.reset
115
- $already_run = false
116
- c = context "Given something" do
117
- context "and a thing" do
118
- should "only run this once" do
119
- flunk if $already_run
120
- $already_run = true
121
- end
122
- end
123
- end
124
- capture_stdout do
125
- assert runner(c).run(@reporter), "should not have run the context twice"
126
- end
63
+ end.
64
+ should_output(/^1 tests, 0 failures \(0\.\d+ seconds\)/)
127
65
  end
128
66
 
129
67
  def test_should_print_out_the_names_of_tests_that_fail
130
- c = context "given something" do
68
+ context "given something" do
131
69
  should "fail" do
132
70
  flunk
133
71
  end
134
- end
135
- r = runner(c)
136
- capture_stdout { r.run(@reporter) }
137
- assert_match /^1\) given something should fail:\n flunked\./, @reporter.failure_messages[0]
72
+ end.
73
+ should_output(%{
74
+ 1) given something should fail:
75
+ flunked
76
+ })
138
77
  end
139
78
 
140
79
  def test_should_include_line_in_test_of_error_in_failure_message
141
- c = context "given jazz" do
142
- should "tapdance" do
80
+ context "given a test that fails" do
81
+ should "report line of failing test" do
143
82
  $line = __LINE__; flunk
144
83
  end
145
- end
146
- r = runner(c)
147
- capture_stdout { r.run(@reporter) }
148
- assert_match /#{Regexp.escape(File.expand_path(__FILE__))}:#{$line}/, @reporter.failure_messages.first
84
+ end.
85
+ should_output(/#{Regexp.escape(File.expand_path(__FILE__))}:#{$line}/)
149
86
  end
150
-
151
- private
152
-
153
- def runner(*args)
154
- Kintama::Runner::Default.new.with(*args)
155
- end
156
-
157
- end
87
+ end