deep-cover 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +5 -5
  2. data/.deep_cover.rb +8 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +15 -1
  5. data/.travis.yml +1 -0
  6. data/README.md +30 -1
  7. data/Rakefile +10 -1
  8. data/bin/cov +1 -1
  9. data/deep_cover.gemspec +4 -5
  10. data/exe/deep-cover +5 -3
  11. data/lib/deep_cover.rb +1 -1
  12. data/lib/deep_cover/analyser/node.rb +1 -1
  13. data/lib/deep_cover/analyser/ruby25_like_branch.rb +209 -0
  14. data/lib/deep_cover/auto_run.rb +19 -19
  15. data/lib/deep_cover/autoload_tracker.rb +181 -44
  16. data/lib/deep_cover/backports.rb +3 -1
  17. data/lib/deep_cover/base.rb +13 -8
  18. data/lib/deep_cover/basics.rb +1 -1
  19. data/lib/deep_cover/cli/debugger.rb +2 -2
  20. data/lib/deep_cover/cli/instrumented_clone_reporter.rb +21 -8
  21. data/lib/deep_cover/cli/runner.rb +126 -0
  22. data/lib/deep_cover/config_setter.rb +1 -0
  23. data/lib/deep_cover/core_ext/autoload_overrides.rb +82 -14
  24. data/lib/deep_cover/core_ext/coverage_replacement.rb +34 -5
  25. data/lib/deep_cover/core_ext/exec_callbacks.rb +27 -0
  26. data/lib/deep_cover/core_ext/load_overrides.rb +4 -6
  27. data/lib/deep_cover/core_ext/require_overrides.rb +1 -3
  28. data/lib/deep_cover/coverage.rb +105 -2
  29. data/lib/deep_cover/coverage/analysis.rb +30 -28
  30. data/lib/deep_cover/coverage/persistence.rb +60 -70
  31. data/lib/deep_cover/covered_code.rb +16 -49
  32. data/lib/deep_cover/custom_requirer.rb +112 -51
  33. data/lib/deep_cover/load.rb +10 -6
  34. data/lib/deep_cover/memoize.rb +1 -3
  35. data/lib/deep_cover/module_override.rb +7 -0
  36. data/lib/deep_cover/node/assignments.rb +2 -1
  37. data/lib/deep_cover/node/base.rb +6 -6
  38. data/lib/deep_cover/node/block.rb +10 -8
  39. data/lib/deep_cover/node/case.rb +3 -3
  40. data/lib/deep_cover/node/collections.rb +8 -0
  41. data/lib/deep_cover/node/if.rb +19 -3
  42. data/lib/deep_cover/node/literals.rb +28 -7
  43. data/lib/deep_cover/node/mixin/can_augment_children.rb +4 -4
  44. data/lib/deep_cover/node/mixin/child_can_be_empty.rb +1 -1
  45. data/lib/deep_cover/node/mixin/filters.rb +6 -2
  46. data/lib/deep_cover/node/mixin/has_child.rb +8 -8
  47. data/lib/deep_cover/node/mixin/has_child_handler.rb +3 -3
  48. data/lib/deep_cover/node/mixin/has_tracker.rb +7 -3
  49. data/lib/deep_cover/node/root.rb +1 -1
  50. data/lib/deep_cover/node/send.rb +53 -7
  51. data/lib/deep_cover/node/short_circuit.rb +11 -3
  52. data/lib/deep_cover/parser_ext/range.rb +11 -27
  53. data/lib/deep_cover/problem_with_diagnostic.rb +1 -1
  54. data/lib/deep_cover/reporter.rb +0 -1
  55. data/lib/deep_cover/reporter/base.rb +68 -0
  56. data/lib/deep_cover/reporter/html.rb +1 -1
  57. data/lib/deep_cover/reporter/html/index.rb +4 -8
  58. data/lib/deep_cover/reporter/html/site.rb +10 -18
  59. data/lib/deep_cover/reporter/html/source.rb +3 -3
  60. data/lib/deep_cover/reporter/html/template/source.html.erb +1 -1
  61. data/lib/deep_cover/reporter/istanbul.rb +86 -56
  62. data/lib/deep_cover/reporter/text.rb +5 -13
  63. data/lib/deep_cover/reporter/{util/tree.rb → tree/util.rb} +19 -21
  64. data/lib/deep_cover/tools/blank.rb +25 -0
  65. data/lib/deep_cover/tools/builtin_coverage.rb +8 -8
  66. data/lib/deep_cover/tools/dump_covered_code.rb +2 -9
  67. data/lib/deep_cover/tools/execute_sample.rb +17 -6
  68. data/lib/deep_cover/tools/format_generated_code.rb +1 -1
  69. data/lib/deep_cover/tools/indent_string.rb +26 -0
  70. data/lib/deep_cover/tools/our_coverage.rb +2 -2
  71. data/lib/deep_cover/tools/strip_heredoc.rb +18 -0
  72. data/lib/deep_cover/tracker_bucket.rb +50 -0
  73. data/lib/deep_cover/tracker_hits_per_path.rb +35 -0
  74. data/lib/deep_cover/tracker_storage.rb +76 -0
  75. data/lib/deep_cover/tracker_storage_per_path.rb +34 -0
  76. data/lib/deep_cover/version.rb +1 -1
  77. data/lib/deep_cover_entry.rb +3 -0
  78. metadata +30 -37
  79. data/bin/gemcov +0 -8
  80. data/bin/selfcov +0 -21
  81. data/lib/deep_cover/cli/deep_cover.rb +0 -126
  82. data/lib/deep_cover/coverage/base.rb +0 -81
  83. data/lib/deep_cover/coverage/istanbul.rb +0 -34
  84. data/lib/deep_cover/tools/transform_keys.rb +0 -9
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepCover
4
+ bootstrap
5
+
6
+ # Should be seen as a hash like {path => tracker_storage, ...}
7
+ # Make it easier to separate some concerns, as well as marshalling
8
+ #
9
+ class TrackerStoragePerPath
10
+ extend Forwardable
11
+ def_delegators :@index, :each, :each_key, :map, :transform_values
12
+
13
+ attr_reader :bucket
14
+
15
+ def initialize(bucket)
16
+ @bucket = bucket
17
+ @index = {}
18
+ end
19
+
20
+ def [](path)
21
+ @index[path] ||= @bucket.create_storage
22
+ end
23
+
24
+ def tracker_hits_per_path
25
+ TrackerHitsPerPath.new(@index.transform_values(&:tracker_hits))
26
+ end
27
+
28
+ def tracker_hits_per_path=(tracker_hits_per_path)
29
+ tracker_hits_per_path.each do |path, tracker_hits|
30
+ self[path].tracker_hits = tracker_hits
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeepCover
4
- VERSION = '0.5.2'
4
+ VERSION = '0.5.3'
5
5
  end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This empty file is just a hack so we can self-cover our exe/deep-cover
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deep-cover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-André Lafortune
@@ -9,52 +9,52 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2018-02-15 00:00:00.000000000 Z
12
+ date: 2018-05-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: parser
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ">="
18
+ - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: '0'
20
+ version: 2.5.0
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - ">="
25
+ - - "~>"
26
26
  - !ruby/object:Gem::Version
27
- version: '0'
27
+ version: 2.5.0
28
28
  - !ruby/object:Gem::Dependency
29
- name: parser_tree_rewriter
29
+ name: backports
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - ">="
33
33
  - !ruby/object:Gem::Version
34
- version: '0'
34
+ version: 3.11.0
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
- version: '0'
41
+ version: 3.11.0
42
42
  - !ruby/object:Gem::Dependency
43
- name: backports
43
+ name: binding_of_caller
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: 3.11.0
48
+ version: '0'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - ">="
54
54
  - !ruby/object:Gem::Version
55
- version: 3.11.0
55
+ version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
- name: binding_of_caller
57
+ name: bundler
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - ">="
@@ -151,20 +151,6 @@ dependencies:
151
151
  - - ">="
152
152
  - !ruby/object:Gem::Version
153
153
  version: '0'
154
- - !ruby/object:Gem::Dependency
155
- name: activesupport
156
- requirement: !ruby/object:Gem::Requirement
157
- requirements:
158
- - - "~>"
159
- - !ruby/object:Gem::Version
160
- version: '4.0'
161
- type: :development
162
- prerelease: false
163
- version_requirements: !ruby/object:Gem::Requirement
164
- requirements:
165
- - - "~>"
166
- - !ruby/object:Gem::Version
167
- version: '4.0'
168
154
  - !ruby/object:Gem::Dependency
169
155
  name: bundler
170
156
  requirement: !ruby/object:Gem::Requirement
@@ -227,14 +213,14 @@ dependencies:
227
213
  requirements:
228
214
  - - '='
229
215
  - !ruby/object:Gem::Version
230
- version: 0.52.1
216
+ version: 0.53.0
231
217
  type: :development
232
218
  prerelease: false
233
219
  version_requirements: !ruby/object:Gem::Requirement
234
220
  requirements:
235
221
  - - '='
236
222
  - !ruby/object:Gem::Version
237
- version: 0.52.1
223
+ version: 0.53.0
238
224
  description: expression and branch coverage for Ruby.
239
225
  email:
240
226
  - github@marc-andre.ca
@@ -244,6 +230,7 @@ executables:
244
230
  extensions: []
245
231
  extra_rdoc_files: []
246
232
  files:
233
+ - ".deep_cover.rb"
247
234
  - ".gitignore"
248
235
  - ".rspec"
249
236
  - ".rspec_all"
@@ -258,8 +245,6 @@ files:
258
245
  - Rakefile
259
246
  - bin/console
260
247
  - bin/cov
261
- - bin/gemcov
262
- - bin/selfcov
263
248
  - bin/setup
264
249
  - bin/test_gems
265
250
  - bin/testall
@@ -276,6 +261,7 @@ files:
276
261
  - lib/deep_cover/analyser/node.rb
277
262
  - lib/deep_cover/analyser/per_char.rb
278
263
  - lib/deep_cover/analyser/per_line.rb
264
+ - lib/deep_cover/analyser/ruby25_like_branch.rb
279
265
  - lib/deep_cover/analyser/statement.rb
280
266
  - lib/deep_cover/analyser/stats.rb
281
267
  - lib/deep_cover/analyser/subset.rb
@@ -286,19 +272,18 @@ files:
286
272
  - lib/deep_cover/basics.rb
287
273
  - lib/deep_cover/builtin_takeover.rb
288
274
  - lib/deep_cover/cli/debugger.rb
289
- - lib/deep_cover/cli/deep_cover.rb
290
275
  - lib/deep_cover/cli/exec.rb
291
276
  - lib/deep_cover/cli/instrumented_clone_reporter.rb
277
+ - lib/deep_cover/cli/runner.rb
292
278
  - lib/deep_cover/config.rb
293
279
  - lib/deep_cover/config_setter.rb
294
280
  - lib/deep_cover/core_ext/autoload_overrides.rb
295
281
  - lib/deep_cover/core_ext/coverage_replacement.rb
282
+ - lib/deep_cover/core_ext/exec_callbacks.rb
296
283
  - lib/deep_cover/core_ext/load_overrides.rb
297
284
  - lib/deep_cover/core_ext/require_overrides.rb
298
285
  - lib/deep_cover/coverage.rb
299
286
  - lib/deep_cover/coverage/analysis.rb
300
- - lib/deep_cover/coverage/base.rb
301
- - lib/deep_cover/coverage/istanbul.rb
302
287
  - lib/deep_cover/coverage/persistence.rb
303
288
  - lib/deep_cover/covered_code.rb
304
289
  - lib/deep_cover/custom_requirer.rb
@@ -345,6 +330,7 @@ files:
345
330
  - lib/deep_cover/parser_ext/range.rb
346
331
  - lib/deep_cover/problem_with_diagnostic.rb
347
332
  - lib/deep_cover/reporter.rb
333
+ - lib/deep_cover/reporter/base.rb
348
334
  - lib/deep_cover/reporter/html.rb
349
335
  - lib/deep_cover/reporter/html/base.rb
350
336
  - lib/deep_cover/reporter/html/index.rb
@@ -363,8 +349,9 @@ files:
363
349
  - lib/deep_cover/reporter/html/template/source.html.erb
364
350
  - lib/deep_cover/reporter/istanbul.rb
365
351
  - lib/deep_cover/reporter/text.rb
366
- - lib/deep_cover/reporter/util/tree.rb
352
+ - lib/deep_cover/reporter/tree/util.rb
367
353
  - lib/deep_cover/tools.rb
354
+ - lib/deep_cover/tools/blank.rb
368
355
  - lib/deep_cover/tools/builtin_coverage.rb
369
356
  - lib/deep_cover/tools/camelize.rb
370
357
  - lib/deep_cover/tools/content_tag.rb
@@ -374,6 +361,7 @@ files:
374
361
  - lib/deep_cover/tools/format.rb
375
362
  - lib/deep_cover/tools/format_char_cover.rb
376
363
  - lib/deep_cover/tools/format_generated_code.rb
364
+ - lib/deep_cover/tools/indent_string.rb
377
365
  - lib/deep_cover/tools/merge.rb
378
366
  - lib/deep_cover/tools/number_lines.rb
379
367
  - lib/deep_cover/tools/our_coverage.rb
@@ -382,9 +370,14 @@ files:
382
370
  - lib/deep_cover/tools/require_relative_dir.rb
383
371
  - lib/deep_cover/tools/silence_warnings.rb
384
372
  - lib/deep_cover/tools/slice.rb
385
- - lib/deep_cover/tools/transform_keys.rb
373
+ - lib/deep_cover/tools/strip_heredoc.rb
386
374
  - lib/deep_cover/tools/truncate_backtrace.rb
375
+ - lib/deep_cover/tracker_bucket.rb
376
+ - lib/deep_cover/tracker_hits_per_path.rb
377
+ - lib/deep_cover/tracker_storage.rb
378
+ - lib/deep_cover/tracker_storage_per_path.rb
387
379
  - lib/deep_cover/version.rb
380
+ - lib/deep_cover_entry.rb
388
381
  homepage: https://github.com/deep-cover/deep-cover
389
382
  licenses:
390
383
  - MIT
@@ -405,7 +398,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
405
398
  version: '0'
406
399
  requirements: []
407
400
  rubyforge_project:
408
- rubygems_version: 2.6.14
401
+ rubygems_version: 2.7.7
409
402
  signing_key:
410
403
  specification_version: 4
411
404
  summary: In depth coverage of your Ruby code.
data/bin/gemcov DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env ruby
2
- module DeepCover
3
- require 'bundler/setup'
4
- require 'deep_cover'
5
- require 'deep_cover/cli/instrumented_clone_reporter'
6
-
7
- CLI::InstrumentedCloneReporter.new(ARGV[0] || '~/inky-rb').run
8
- end
data/bin/selfcov DELETED
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'bundler/setup'
3
- require 'pry'
4
-
5
- covered_path = ARGV[0]
6
- if covered_path
7
- $LOAD_PATH.unshift(covered_path)
8
- end
9
- require 'deep_cover'
10
-
11
- if covered_path.nil?
12
- covered_path = DeepCover::Tools.dump_covered_code_and_save('./lib', dest_path: '../covered_deep_cover')
13
- puts 'Covered code generation done. Output in', covered_path
14
- exec 'bin/selfcov', covered_path
15
- else
16
- coverage = DeepCover::Coverage.load(covered_path)
17
- require 'rspec'
18
- error = RSpec::Core::Runner::run(Dir.glob('./spec/*_spec.rb'))
19
- puts 'Lines not covered:', coverage.report
20
- binding.pry
21
- end
@@ -1,126 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- require 'slop'
5
- require_relative '../basics'
6
-
7
- module CLI
8
- end
9
-
10
- module CLI::SlopExtension
11
- attr_accessor :stopped
12
- attr_reader :ignored
13
-
14
- def try_process(*)
15
- @ignored ||= 0
16
- return if stopped
17
- o = super
18
- @ignored += 1 unless o
19
- o
20
- end
21
- end
22
- Slop::Parser.prepend CLI::SlopExtension
23
-
24
- module CLI::DeepCover
25
- extend self
26
-
27
- def show_version
28
- require_relative '../version'
29
- require 'parser'
30
- puts "deep-cover v#{DeepCover::VERSION}; parser v#{Parser::VERSION}"
31
- end
32
-
33
- def show_help
34
- puts menu
35
- end
36
-
37
- class OptionParser < Struct.new(:delegate)
38
- def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
39
- options = args.last
40
- if options.is_a?(Hash) && options.has_key?(:default)
41
- args[-2] += " [#{options[:default]}]"
42
- end
43
- delegate.public_send(method, *args, &block)
44
- end
45
- end
46
-
47
- def parse
48
- Slop.parse do |o|
49
- yield OptionParser.new(o)
50
- end
51
- end
52
-
53
- def menu
54
- @menu ||= parse do |o|
55
- o.banner = ['usage: deep-cover [options] exec <command ...>',
56
- ' or deep-cover [options] [path/to/app/or/gem]',
57
- ].join("\n")
58
- o.separator ''
59
- o.string '-o', '--output', 'output folder', default: DEFAULTS[:output]
60
- o.string '--reporter', 'reporter', default: DEFAULTS[:reporter]
61
- o.bool '--open', 'open the output coverage', default: CLI_DEFAULTS[:open]
62
-
63
- o.separator 'Coverage options'
64
- @ignore_uncovered_map = OPTIONALLY_COVERED.map do |option|
65
- default = DEFAULTS[:ignore_uncovered].include?(option)
66
- o.bool "--ignore-#{dasherize(option)}", '', default: default
67
- [:"ignore_#{option}", option]
68
- end.to_h
69
-
70
- o.separator "\nWhen not using ’exec’:"
71
- o.string '-c', '--command', 'command to run tests', default: CLI_DEFAULTS[:command]
72
- o.bool '--bundle', 'run bundle before the tests', default: CLI_DEFAULTS[:bundle]
73
- o.bool '--process', 'turn off to only redo the reporting', default: CLI_DEFAULTS[:process]
74
-
75
- o.separator "\nFor testing purposes:"
76
- o.bool '--profile', 'use profiler' unless RUBY_PLATFORM == 'java'
77
- o.string '-e', '--expression', 'test ruby expression instead of a covering a path'
78
- o.bool '-d', '--debug', 'enter debugging after cover'
79
-
80
- o.separator "\nOther available commands:"
81
- o.on('--version', 'print the version') do
82
- show_version
83
- exit
84
- end
85
- o.boolean('-h', '--help')
86
-
87
- o.boolean('exec', '', help: false) do
88
- o.parser.stopped = true if o.parser.ignored == 0
89
- end
90
- end
91
- end
92
-
93
- def convert_options(options)
94
- iu = options[:ignore_uncovered] = []
95
- @ignore_uncovered_map.each do |cli_option, option|
96
- iu << option if options.delete(cli_option)
97
- end
98
- options[:output] = false if ['false', 'f', ''].include?(options[:output])
99
- options
100
- end
101
-
102
- def go
103
- options = convert_options(menu.to_h)
104
- if options[:help]
105
- show_help
106
- elsif options[:expression]
107
- require_relative 'debugger'
108
- CLI::Debugger.new(options[:expression], **options).show
109
- elsif menu.parser.stopped
110
- require_relative 'exec'
111
- CLI::Exec.new(menu.arguments, **options).run
112
- else
113
- require_relative 'instrumented_clone_reporter'
114
- path = menu.arguments.first || '.'
115
- CLI::InstrumentedCloneReporter.new(path, **options).run
116
- end
117
- end
118
-
119
- private
120
-
121
- # Poor man's dasherize. 'an_example' => 'an-example'
122
- def dasherize(string)
123
- string.to_s.tr('_', '-')
124
- end
125
- end
126
- end
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- # A collection of CoveredCode
5
- class Coverage
6
- include Enumerable
7
-
8
- def initialize(**options)
9
- @covered_code_index = {}
10
- @options = options
11
- end
12
-
13
- def covered_codes
14
- @covered_code_index.values
15
- end
16
-
17
- def reset
18
- @covered_code_index = {}
19
- end
20
-
21
- def line_coverage(filename, **options)
22
- covered_code(filename).line_coverage(**options)
23
- end
24
-
25
- def covered_code(path, **options)
26
- raise 'path must be an absolute path' unless Pathname.new(path).absolute?
27
- @covered_code_index[path] ||= CoveredCode.new(path: path, **options, **@options)
28
- end
29
-
30
- def each
31
- return to_enum unless block_given?
32
- @covered_code_index.each_value { |covered_code| yield covered_code }
33
- self
34
- end
35
-
36
- def report(**options)
37
- case (reporter = options.fetch(:reporter, DEFAULTS[:reporter]).to_sym)
38
- when :html
39
- msg = if (output = options.fetch(:output, DEFAULTS[:output]))
40
- Reporter::HTML.report(self, **options)
41
- "HTML generated: open #{output}/index.html"
42
- else
43
- 'No HTML generated'
44
- end
45
- Reporter::Text.report(self, **options) + "\n\n" + msg
46
- when :istanbul
47
- if Reporter::Istanbul.available?
48
- report_istanbul(**options)
49
- else
50
- warn 'nyc not available. Please install `nyc` using `yarn global add nyc` or `npm i nyc -g`'
51
- end
52
- when :text
53
- Reporter::Text.report(self, **options)
54
- else
55
- raise ArgumentError, "Unknown reporter: #{reporter}"
56
- end
57
- end
58
-
59
- def self.load(dest_path, dirname = 'deep_cover', with_trackers: true)
60
- Persistence.new(dest_path, dirname).load(with_trackers: with_trackers)
61
- end
62
-
63
- def self.saved?(dest_path, dirname = 'deep_cover')
64
- Persistence.new(dest_path, dirname).saved?
65
- end
66
-
67
- def save(dest_path, dirname = 'deep_cover')
68
- Persistence.new(dest_path, dirname).save(self)
69
- self
70
- end
71
-
72
- def save_trackers(dest_path, dirname = 'deep_cover')
73
- Persistence.new(dest_path, dirname).save_trackers(tracker_global)
74
- self
75
- end
76
-
77
- def tracker_global
78
- @options.fetch(:tracker_global, DEFAULTS[:tracker_global])
79
- end
80
- end
81
- end