datadog-ci 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,85 @@
1
+ module Datadog
2
+ module CI
3
+ module TestVisibility
4
+ class Recorder
5
+ @test_suite_level_visibility_enabled: bool
6
+ @enabled: bool
7
+
8
+ @environment_tags: Hash[String, String]
9
+ @local_context: Datadog::CI::TestVisibility::Context::Local
10
+ @global_context: Datadog::CI::TestVisibility::Context::Global
11
+
12
+ @null_span: Datadog::CI::NullSpan
13
+
14
+ attr_reader environment_tags: Hash[String, String]
15
+ attr_reader test_suite_level_visibility_enabled: bool
16
+ attr_reader enabled: bool
17
+
18
+ def initialize: (?enabled: bool, ?test_suite_level_visibility_enabled: bool) -> void
19
+
20
+ def trace_test: (String span_name, String test_suite_name, ?service: String?, ?tags: Hash[untyped, untyped]) ?{ (Datadog::CI::Span span) -> untyped } -> untyped
21
+
22
+ def trace: (String span_type, String span_name, ?tags: Hash[untyped, untyped]) ?{ (Datadog::CI::Span span) -> untyped } -> untyped
23
+
24
+ def start_test_session: (?service: String?, ?tags: Hash[untyped, untyped]) -> Datadog::CI::Span
25
+
26
+ def start_test_module: (String test_module_name, ?service: String?, ?tags: Hash[untyped, untyped]) -> Datadog::CI::Span
27
+
28
+ def start_test_suite: (String test_suite_name, ?service: String?, ?tags: Hash[untyped, untyped]) -> Datadog::CI::Span
29
+
30
+ def active_test_session: () -> Datadog::CI::TestSession?
31
+
32
+ def active_test_module: () -> Datadog::CI::TestModule?
33
+
34
+ def active_test_suite: (String test_suite_name) -> Datadog::CI::TestSuite?
35
+
36
+ def active_test: () -> Datadog::CI::Test?
37
+
38
+ def active_span: () -> Datadog::CI::Span?
39
+
40
+ def deactivate_test: (Datadog::CI::Test test) -> void
41
+
42
+ def deactivate_test_session: () -> void
43
+
44
+ def deactivate_test_module: () -> void
45
+
46
+ def deactivate_test_suite: (String test_suite_name) -> void
47
+
48
+ def create_datadog_span: (String span_name, ?span_options: Hash[untyped, untyped], ?tags: Hash[untyped, untyped]) ?{ (Datadog::CI::Span span) -> untyped } -> untyped
49
+
50
+ def set_trace_origin: (Datadog::Tracing::TraceOperation trace) -> untyped
51
+
52
+ private
53
+
54
+ def build_test: (Datadog::Tracing::SpanOperation tracer_span, Hash[untyped, untyped] tags) -> Datadog::CI::Test
55
+
56
+ def build_test_session: (Datadog::Tracing::SpanOperation tracer_span, Hash[untyped, untyped] tags) -> Datadog::CI::TestSession
57
+
58
+ def build_test_module: (Datadog::Tracing::SpanOperation tracer_span, Hash[untyped, untyped] tags) -> Datadog::CI::TestModule
59
+
60
+ def build_test_suite: (Datadog::Tracing::SpanOperation tracer_span, Hash[untyped, untyped] tags) -> Datadog::CI::TestSuite
61
+
62
+ def build_span: (Datadog::Tracing::SpanOperation tracer_span, Hash[untyped, untyped] tags) -> Datadog::CI::Span
63
+
64
+ def build_span_options: (String? service_name, String span_type, ?Hash[Symbol, untyped] other_options) -> Hash[Symbol, untyped]
65
+
66
+ def set_initial_tags: (Datadog::CI::Span ci_span, Hash[untyped, untyped] tags) -> void
67
+
68
+ # the type (Datadog::CI::TestSession | Datadog::Tracing::SpanOperation) screams of wrong/mising abstraction
69
+ def set_session_context: (Hash[untyped, untyped] tags, ?Datadog::CI::TestSession | Datadog::Tracing::SpanOperation? test_session) -> void
70
+
71
+ def set_suite_context: (Hash[untyped, untyped] tags, ?span: Datadog::Tracing::SpanOperation, ?name: String) -> void
72
+
73
+ def set_module_context: (Hash[untyped, untyped] tags, ?Datadog::CI::TestModule | Datadog::Tracing::SpanOperation? test_module) -> void
74
+
75
+ def null_span: () -> Datadog::CI::Span
76
+
77
+ def skip_tracing: (?untyped block) -> untyped
78
+
79
+ def start_datadog_tracer_span: (String span_name, Hash[untyped, untyped] span_options) ?{ (untyped) -> untyped } -> untyped
80
+
81
+ def set_inherited_globals: (Hash[untyped, untyped] tags) -> void
82
+ end
83
+ end
84
+ end
85
+ end
data/sig/datadog/ci.rbs CHANGED
@@ -30,6 +30,6 @@ module Datadog
30
30
 
31
31
  def self.components: () -> Datadog::CI::Configuration::Components
32
32
 
33
- def self.recorder: () -> Datadog::CI::Recorder
33
+ def self.recorder: () -> Datadog::CI::TestVisibility::Recorder
34
34
  end
35
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datadog-ci
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-06 00:00:00.000000000 Z
11
+ date: 2023-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -44,9 +44,8 @@ files:
44
44
  - lib/datadog/ci.rb
45
45
  - lib/datadog/ci/concurrent_span.rb
46
46
  - lib/datadog/ci/configuration/components.rb
47
+ - lib/datadog/ci/configuration/extensions.rb
47
48
  - lib/datadog/ci/configuration/settings.rb
48
- - lib/datadog/ci/context/global.rb
49
- - lib/datadog/ci/context/local.rb
50
49
  - lib/datadog/ci/contrib/cucumber/configuration/settings.rb
51
50
  - lib/datadog/ci/contrib/cucumber/ext.rb
52
51
  - lib/datadog/ci/contrib/cucumber/formatter.rb
@@ -90,15 +89,16 @@ files:
90
89
  - lib/datadog/ci/ext/settings.rb
91
90
  - lib/datadog/ci/ext/test.rb
92
91
  - lib/datadog/ci/ext/transport.rb
93
- - lib/datadog/ci/extensions.rb
94
92
  - lib/datadog/ci/null_span.rb
95
- - lib/datadog/ci/recorder.rb
96
93
  - lib/datadog/ci/span.rb
97
94
  - lib/datadog/ci/test.rb
98
95
  - lib/datadog/ci/test_module.rb
99
96
  - lib/datadog/ci/test_session.rb
100
97
  - lib/datadog/ci/test_suite.rb
98
+ - lib/datadog/ci/test_visibility/context/global.rb
99
+ - lib/datadog/ci/test_visibility/context/local.rb
101
100
  - lib/datadog/ci/test_visibility/flush.rb
101
+ - lib/datadog/ci/test_visibility/recorder.rb
102
102
  - lib/datadog/ci/test_visibility/serializers/base.rb
103
103
  - lib/datadog/ci/test_visibility/serializers/factories/test_level.rb
104
104
  - lib/datadog/ci/test_visibility/serializers/factories/test_suite_level.rb
@@ -122,9 +122,8 @@ files:
122
122
  - sig/datadog/ci.rbs
123
123
  - sig/datadog/ci/concurrent_span.rbs
124
124
  - sig/datadog/ci/configuration/components.rbs
125
+ - sig/datadog/ci/configuration/extensions.rbs
125
126
  - sig/datadog/ci/configuration/settings.rbs
126
- - sig/datadog/ci/context/global.rbs
127
- - sig/datadog/ci/context/local.rbs
128
127
  - sig/datadog/ci/contrib/cucumber/configuration/settings.rbs
129
128
  - sig/datadog/ci/contrib/cucumber/ext.rbs
130
129
  - sig/datadog/ci/contrib/cucumber/formatter.rbs
@@ -168,15 +167,16 @@ files:
168
167
  - sig/datadog/ci/ext/settings.rbs
169
168
  - sig/datadog/ci/ext/test.rbs
170
169
  - sig/datadog/ci/ext/transport.rbs
171
- - sig/datadog/ci/extensions.rbs
172
170
  - sig/datadog/ci/null_span.rbs
173
- - sig/datadog/ci/recorder.rbs
174
171
  - sig/datadog/ci/span.rbs
175
172
  - sig/datadog/ci/test.rbs
176
173
  - sig/datadog/ci/test_module.rbs
177
174
  - sig/datadog/ci/test_session.rbs
178
175
  - sig/datadog/ci/test_suite.rbs
176
+ - sig/datadog/ci/test_visibility/context/global.rbs
177
+ - sig/datadog/ci/test_visibility/context/local.rbs
179
178
  - sig/datadog/ci/test_visibility/flush.rbs
179
+ - sig/datadog/ci/test_visibility/recorder.rbs
180
180
  - sig/datadog/ci/test_visibility/serializers/base.rbs
181
181
  - sig/datadog/ci/test_visibility/serializers/factories/test_level.rbs
182
182
  - sig/datadog/ci/test_visibility/serializers/factories/test_suite_level.rbs
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Datadog
4
- module CI
5
- module Context
6
- # This context is shared between threads and represents the current test session and test module.
7
- class Global
8
- def initialize
9
- # we are using Monitor instead of Mutex because it is reentrant
10
- @mutex = Monitor.new
11
-
12
- @test_session = nil
13
- @test_module = nil
14
- @test_suites = {}
15
- end
16
-
17
- def fetch_or_activate_test_suite(test_suite_name, &block)
18
- @mutex.synchronize do
19
- @test_suites[test_suite_name] ||= block.call
20
- end
21
- end
22
-
23
- def fetch_or_activate_test_module(&block)
24
- @mutex.synchronize do
25
- @test_module ||= block.call
26
- end
27
- end
28
-
29
- def fetch_or_activate_test_session(&block)
30
- @mutex.synchronize do
31
- @test_session ||= block.call
32
- end
33
- end
34
-
35
- def active_test_module
36
- @test_module
37
- end
38
-
39
- def active_test_session
40
- @test_session
41
- end
42
-
43
- def active_test_suite(test_suite_name)
44
- @mutex.synchronize { @test_suites[test_suite_name] }
45
- end
46
-
47
- def service
48
- @mutex.synchronize do
49
- # thank you RBS for this weirdness
50
- test_session = @test_session
51
- test_session.service if test_session
52
- end
53
- end
54
-
55
- def inheritable_session_tags
56
- @mutex.synchronize do
57
- test_session = @test_session
58
- if test_session
59
- test_session.inheritable_tags
60
- else
61
- {}
62
- end
63
- end
64
- end
65
-
66
- def deactivate_test_session!
67
- @mutex.synchronize { @test_session = nil }
68
- end
69
-
70
- def deactivate_test_module!
71
- @mutex.synchronize { @test_module = nil }
72
- end
73
-
74
- def deactivate_test_suite!(test_suite_name)
75
- @mutex.synchronize { @test_suites.delete(test_suite_name) }
76
- end
77
- end
78
- end
79
- end
80
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Datadog
4
- module CI
5
- module Context
6
- class Local
7
- def initialize
8
- @key = :datadog_ci_active_test
9
-
10
- self.active_test = nil
11
- end
12
-
13
- def activate_test!(test)
14
- raise "Nested tests are not supported. Currently active test: #{active_test}" unless active_test.nil?
15
-
16
- if block_given?
17
- begin
18
- self.active_test = test
19
- yield
20
- ensure
21
- self.active_test = nil
22
- end
23
- else
24
- self.active_test = test
25
- end
26
- end
27
-
28
- def deactivate_test!(test)
29
- return if active_test.nil?
30
-
31
- if active_test == test
32
- self.active_test = nil
33
- else
34
- raise "Trying to deactivate test #{test}, but currently active test is #{active_test}"
35
- end
36
- end
37
-
38
- def active_test
39
- Thread.current[@key]
40
- end
41
-
42
- private
43
-
44
- def active_test=(test)
45
- Thread.current[@key] = test
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "datadog/core/configuration/settings"
4
- require "datadog/core/configuration/components"
5
-
6
- require_relative "configuration/settings"
7
- require_relative "configuration/components"
8
-
9
- module Datadog
10
- module CI
11
- # Extends Datadog tracing with CI features
12
- module Extensions
13
- def self.activate!
14
- Core::Configuration::Settings.extend(CI::Configuration::Settings)
15
- Core::Configuration::Components.prepend(CI::Configuration::Components)
16
- end
17
- end
18
- end
19
- end
@@ -1,291 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "datadog/tracing"
4
- require "datadog/tracing/trace_digest"
5
-
6
- require "rbconfig"
7
-
8
- require_relative "ext/app_types"
9
- require_relative "ext/test"
10
- require_relative "ext/environment"
11
-
12
- require_relative "context/global"
13
- require_relative "context/local"
14
-
15
- require_relative "span"
16
- require_relative "null_span"
17
- require_relative "test"
18
- require_relative "test_session"
19
- require_relative "test_module"
20
- require_relative "test_suite"
21
-
22
- module Datadog
23
- module CI
24
- # Common behavior for CI tests
25
- # Note: this class has too many responsibilities and should be split into multiple classes
26
- class Recorder
27
- attr_reader :environment_tags, :test_suite_level_visibility_enabled, :enabled
28
-
29
- def initialize(enabled: true, test_suite_level_visibility_enabled: false)
30
- @enabled = enabled
31
- @test_suite_level_visibility_enabled = enabled && test_suite_level_visibility_enabled
32
-
33
- @environment_tags = Ext::Environment.tags(ENV).freeze
34
- @local_context = Context::Local.new
35
- @global_context = Context::Global.new
36
- end
37
-
38
- def start_test_session(service: nil, tags: {})
39
- return skip_tracing unless test_suite_level_visibility_enabled
40
-
41
- @global_context.fetch_or_activate_test_session do
42
- tracer_span = start_datadog_tracer_span(
43
- "test.session", build_span_options(service, Ext::AppTypes::TYPE_TEST_SESSION)
44
- )
45
- set_session_context(tags, tracer_span)
46
-
47
- build_test_session(tracer_span, tags)
48
- end
49
- end
50
-
51
- def start_test_module(test_module_name, service: nil, tags: {})
52
- return skip_tracing unless test_suite_level_visibility_enabled
53
-
54
- @global_context.fetch_or_activate_test_module do
55
- set_inherited_globals(tags)
56
- set_session_context(tags)
57
-
58
- tracer_span = start_datadog_tracer_span(
59
- test_module_name, build_span_options(service, Ext::AppTypes::TYPE_TEST_MODULE)
60
- )
61
- set_module_context(tags, tracer_span)
62
-
63
- build_test_module(tracer_span, tags)
64
- end
65
- end
66
-
67
- def start_test_suite(test_suite_name, service: nil, tags: {})
68
- return skip_tracing unless test_suite_level_visibility_enabled
69
-
70
- @global_context.fetch_or_activate_test_suite(test_suite_name) do
71
- set_inherited_globals(tags)
72
- set_session_context(tags)
73
- set_module_context(tags)
74
-
75
- tracer_span = start_datadog_tracer_span(
76
- test_suite_name, build_span_options(service, Ext::AppTypes::TYPE_TEST_SUITE)
77
- )
78
- set_suite_context(tags, span: tracer_span)
79
-
80
- build_test_suite(tracer_span, tags)
81
- end
82
- end
83
-
84
- def trace_test(test_name, test_suite_name, service: nil, tags: {}, &block)
85
- return skip_tracing(block) unless enabled
86
-
87
- set_inherited_globals(tags)
88
- set_session_context(tags)
89
- set_module_context(tags)
90
- set_suite_context(tags, name: test_suite_name)
91
-
92
- tags[Ext::Test::TAG_NAME] = test_name
93
-
94
- span_options = build_span_options(
95
- service,
96
- Ext::AppTypes::TYPE_TEST,
97
- # :resource is needed for the agent APM protocol to work correctly (for older agent versions)
98
- # :continue_from is required to start a new trace for each test
99
- {resource: test_name, continue_from: Datadog::Tracing::TraceDigest.new}
100
- )
101
-
102
- if block
103
- start_datadog_tracer_span(test_name, span_options) do |tracer_span|
104
- test = build_test(tracer_span, tags)
105
-
106
- @local_context.activate_test!(test) do
107
- block.call(test)
108
- end
109
- end
110
- else
111
- tracer_span = start_datadog_tracer_span(test_name, span_options)
112
-
113
- test = build_test(tracer_span, tags)
114
- @local_context.activate_test!(test)
115
- test
116
- end
117
- end
118
-
119
- def trace(span_type, span_name, tags: {}, &block)
120
- return skip_tracing(block) unless enabled
121
-
122
- span_options = build_span_options(
123
- nil, # service name is completely optional for custom spans
124
- span_type,
125
- {resource: span_name}
126
- )
127
-
128
- if block
129
- start_datadog_tracer_span(span_name, span_options) do |tracer_span|
130
- block.call(build_span(tracer_span, tags))
131
- end
132
- else
133
- tracer_span = start_datadog_tracer_span(span_name, span_options)
134
-
135
- build_span(tracer_span, tags)
136
- end
137
- end
138
-
139
- def active_span
140
- tracer_span = Datadog::Tracing.active_span
141
- Span.new(tracer_span) if tracer_span
142
- end
143
-
144
- def active_test
145
- @local_context.active_test
146
- end
147
-
148
- def active_test_session
149
- @global_context.active_test_session
150
- end
151
-
152
- def active_test_module
153
- @global_context.active_test_module
154
- end
155
-
156
- def active_test_suite(test_suite_name)
157
- @global_context.active_test_suite(test_suite_name)
158
- end
159
-
160
- def deactivate_test(test)
161
- @local_context.deactivate_test!(test)
162
- end
163
-
164
- def deactivate_test_session
165
- @global_context.deactivate_test_session!
166
- end
167
-
168
- def deactivate_test_module
169
- @global_context.deactivate_test_module!
170
- end
171
-
172
- def deactivate_test_suite(test_suite_name)
173
- @global_context.deactivate_test_suite!(test_suite_name)
174
- end
175
-
176
- private
177
-
178
- def skip_tracing(block = nil)
179
- if block
180
- block.call(null_span)
181
- else
182
- null_span
183
- end
184
- end
185
-
186
- # Sets trace's origin to ciapp-test
187
- def set_trace_origin(trace)
188
- trace.origin = Ext::Test::CONTEXT_ORIGIN if trace
189
- end
190
-
191
- def build_test_session(tracer_span, tags)
192
- test_session = TestSession.new(tracer_span)
193
- set_initial_tags(test_session, tags)
194
- test_session
195
- end
196
-
197
- def build_test_module(tracer_span, tags)
198
- test_module = TestModule.new(tracer_span)
199
- set_initial_tags(test_module, tags)
200
- test_module
201
- end
202
-
203
- def build_test_suite(tracer_span, tags)
204
- test_suite = TestSuite.new(tracer_span)
205
- set_initial_tags(test_suite, tags)
206
- test_suite
207
- end
208
-
209
- def build_test(tracer_span, tags)
210
- test = Test.new(tracer_span)
211
- set_initial_tags(test, tags)
212
- test
213
- end
214
-
215
- def build_span(tracer_span, tags)
216
- span = Span.new(tracer_span)
217
- set_initial_tags(span, tags)
218
- span
219
- end
220
-
221
- def build_span_options(service, span_type, other_options = {})
222
- other_options[:service] = service || @global_context.service
223
- other_options[:span_type] = span_type
224
-
225
- other_options
226
- end
227
-
228
- def set_inherited_globals(tags)
229
- # this code achieves the same as @global_context.inheritable_session_tags.merge(tags)
230
- # but without allocating a new hash
231
- @global_context.inheritable_session_tags.each do |key, value|
232
- tags[key] = value unless tags.key?(key)
233
- end
234
- end
235
-
236
- def set_initial_tags(ci_span, tags)
237
- ci_span.set_default_tags
238
- ci_span.set_environment_runtime_tags
239
-
240
- ci_span.set_tags(tags)
241
- ci_span.set_tags(environment_tags)
242
- end
243
-
244
- def set_session_context(tags, test_session = nil)
245
- test_session ||= active_test_session
246
- tags[Ext::Test::TAG_TEST_SESSION_ID] = test_session.id.to_s if test_session
247
- end
248
-
249
- def set_module_context(tags, test_module = nil)
250
- test_module ||= active_test_module
251
- if test_module
252
- tags[Ext::Test::TAG_TEST_MODULE_ID] = test_module.id.to_s
253
- tags[Ext::Test::TAG_MODULE] = test_module.name
254
- end
255
- end
256
-
257
- def set_suite_context(tags, span: nil, name: nil)
258
- return if span.nil? && name.nil?
259
-
260
- test_suite = span || active_test_suite(name)
261
-
262
- if test_suite
263
- tags[Ext::Test::TAG_TEST_SUITE_ID] = test_suite.id.to_s
264
- tags[Ext::Test::TAG_SUITE] = test_suite.name
265
- else
266
- tags[Ext::Test::TAG_SUITE] = name
267
- end
268
- end
269
-
270
- def start_datadog_tracer_span(span_name, span_options, &block)
271
- if block
272
- Datadog::Tracing.trace(span_name, **span_options) do |tracer_span, trace|
273
- set_trace_origin(trace)
274
-
275
- yield tracer_span
276
- end
277
- else
278
- tracer_span = Datadog::Tracing.trace(span_name, **span_options)
279
- trace = Datadog::Tracing.active_trace
280
- set_trace_origin(trace)
281
-
282
- tracer_span
283
- end
284
- end
285
-
286
- def null_span
287
- @null_span ||= NullSpan.new
288
- end
289
- end
290
- end
291
- end
@@ -1,37 +0,0 @@
1
- module Datadog
2
- module CI
3
- module Context
4
- class Global
5
- @mutex: Thread::Mutex
6
-
7
- @test_session: Datadog::CI::TestSession?
8
- @test_module: Datadog::CI::TestModule?
9
- @test_suites: Hash[String, Datadog::CI::TestSuite]
10
-
11
- def initialize: () -> void
12
-
13
- def fetch_or_activate_test_suite: (String test_suite_name) {() -> Datadog::CI::TestSuite} -> Datadog::CI::TestSuite
14
-
15
- def fetch_or_activate_test_module: () {() -> Datadog::CI::TestModule} -> Datadog::CI::TestModule
16
-
17
- def fetch_or_activate_test_session: () {() -> Datadog::CI::TestSession} -> Datadog::CI::TestSession
18
-
19
- def active_test_session: () -> Datadog::CI::TestSession?
20
-
21
- def active_test_suite: (String test_suite_name) -> Datadog::CI::TestSuite?
22
-
23
- def service: () -> String?
24
-
25
- def inheritable_session_tags: () -> Hash[untyped, untyped]
26
-
27
- def active_test_module: () -> Datadog::CI::TestModule?
28
-
29
- def deactivate_test_session!: () -> void
30
-
31
- def deactivate_test_module!: () -> void
32
-
33
- def deactivate_test_suite!: (String test_suite_name) -> void
34
- end
35
- end
36
- end
37
- end
@@ -1,21 +0,0 @@
1
- module Datadog
2
- module CI
3
- module Context
4
- class Local
5
- @key: Symbol
6
-
7
- def initialize: () -> void
8
-
9
- def activate_test!: (Datadog::CI::Test test) ?{ () -> untyped } -> void
10
-
11
- def deactivate_test!: (Datadog::CI::Test test) -> void
12
-
13
- def active_test: () -> Datadog::CI::Test?
14
-
15
- private
16
-
17
- def active_test=: (Datadog::CI::Test? test) -> untyped
18
- end
19
- end
20
- end
21
- end
@@ -1,7 +0,0 @@
1
- module Datadog
2
- module CI
3
- module Extensions
4
- def self.activate!: () -> untyped
5
- end
6
- end
7
- end