spectre-core 1.12.3 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  require_relative '../spectre'
2
+ require_relative '../spectre/helpers'
2
3
 
3
4
  require 'ostruct'
4
5
  require_relative 'logger'
@@ -7,38 +8,45 @@ require_relative 'logger'
7
8
  module Spectre
8
9
  module Assertion
9
10
  class ::Object
10
- def should_be(val)
11
- raise AssertionFailure.new("The value '#{self.to_s.trim}' should be '#{val.to_s.trim}'", val, self) unless self.to_s == val.to_s
11
+ def should_be(value)
12
+ evaluate(value, "#{self} should be #{value}") do |x|
13
+ self.equal? x
14
+ end
12
15
  end
13
16
 
14
17
  def should_be_empty
15
- raise AssertionFailure.new("The value '#{self.to_s.trim}' should be empty", nil, self) unless self.nil?
18
+ raise AssertionFailure.new("#{self.to_s.trim} should be empty", nil, self) unless self.nil?
16
19
  end
17
20
 
18
21
  def should_not_be(val)
19
- raise AssertionFailure.new("The value '#{self.to_s.trim}' should not be '#{val.to_s.trim}'", val, self) unless self.to_s != val.to_s
22
+ raise AssertionFailure.new("#{self.to_s.trim} should not be #{val.to_s.trim}", val, self) unless self.to_s != val.to_s
20
23
  end
21
24
 
22
25
  def should_not_exist
23
- raise AssertionFailure.new("The value '#{self.to_s.trim}' should not exist, but it does", val, self) unless self.to_s != nil
26
+ raise AssertionFailure.new("#{self.to_s.trim} should not exist, but it does", val, self) unless self.to_s != nil
24
27
  end
25
28
 
26
29
  def should_not_be_empty
27
- raise AssertionFailure.new('The value is empty', 'nothing', self) unless self != nil
30
+ raise AssertionFailure.new('empty value', 'nothing', self) unless self != nil
31
+ end
32
+
33
+ def evaluate val, message, &block
34
+ val = Evaluation.new(val) unless val.is_a? Evaluation
35
+ raise AssertionFailure.new(message, val, self) unless val.run &block
28
36
  end
29
37
 
30
38
  def or other
31
- OrEvaluation.new self, other
39
+ OrEvaluation.new(self, other)
32
40
  end
33
41
 
34
42
  def and other
35
- AndEvaluation.new self, other
43
+ AndEvaluation.new(self, other)
36
44
  end
37
45
  end
38
46
 
39
47
  class ::NilClass
40
48
  def should_be(val)
41
- raise AssertionFailure.new("There is nothing, but the value should be '#{val.to_s.trim}'", val, nil) unless val == nil
49
+ raise AssertionFailure.new("Value is empty, but it should be '#{val.to_s.trim}'", val, nil) unless val == nil
42
50
  end
43
51
 
44
52
  def should_be_empty
@@ -52,7 +60,7 @@ module Spectre
52
60
  end
53
61
 
54
62
  def should_not_be_empty
55
- raise AssertionFailure.new('The value does not exist', 'nil')
63
+ raise AssertionFailure.new('Value is empty', 'nil')
56
64
  end
57
65
  end
58
66
 
@@ -66,21 +74,21 @@ module Spectre
66
74
  end
67
75
 
68
76
  def should_be_empty
69
- raise AssertionFailure.new('The object should be empty', nil, self) unless self.empty?
77
+ raise AssertionFailure.new('Object should be empty', nil, self) unless self.empty?
70
78
  end
71
79
 
72
80
  def should_not_be_empty
73
- raise AssertionFailure.new('The object should not be empty', nil, self) if self.empty?
81
+ raise AssertionFailure.new('Object should not be empty', nil, self) if self.empty?
74
82
  end
75
83
  end
76
84
 
77
85
  class ::OpenStruct
78
86
  def should_be_empty
79
- raise AssertionFailure.new('The object should be empty', nil, self) unless self.to_h.empty?
87
+ raise AssertionFailure.new('Object should be empty', nil, self) unless self.to_h.empty?
80
88
  end
81
89
 
82
90
  def should_not_be_empty
83
- raise AssertionFailure.new('The object should not be empty', nil, self) if self.to_h.empty?
91
+ raise AssertionFailure.new('Object should not be empty', nil, self) if self.to_h.empty?
84
92
  end
85
93
  end
86
94
 
@@ -93,7 +101,9 @@ module Spectre
93
101
  val = OpenStruct.new(val)
94
102
  end
95
103
 
96
- raise AssertionFailure.new("The list [#{list.join(', ').trim}] should contain '#{val.to_s.trim}'", val, list) unless list.include? val
104
+ evaluate(val, "#{self} should contain #{val.to_s}") do |x|
105
+ self.include? x
106
+ end
97
107
  end
98
108
 
99
109
  def should_not_contain(val)
@@ -104,57 +114,51 @@ module Spectre
104
114
  val = OpenStruct.new(val)
105
115
  end
106
116
 
107
- raise AssertionFailure.new("The list [#{list.join(', ').trim}] should not contain '#{val.to_s.trim}'", val, list) if list.include? val
117
+ raise AssertionFailure.new("[#{list.join(', ').trim}] should not contain '#{val.to_s.trim}'", val, list) if list.include? val
108
118
  end
109
119
 
110
120
  def should_be_empty
111
- raise AssertionFailure.new('The list is not empty', self) unless self.empty?
121
+ raise AssertionFailure.new('List is not empty', self) unless self.empty?
112
122
  end
113
123
 
114
124
  def should_not_be_empty
115
- raise AssertionFailure.new('The list is empty', self) if self.empty?
125
+ raise AssertionFailure.new('List is empty', self) if self.empty?
116
126
  end
117
127
  end
118
128
 
119
129
  class ::String
120
130
  def should_be(val)
121
- raise AssertionFailure.new("The text '#{self.trim}' should be '#{val.to_s.trim}'", val, self) unless self == val
131
+ raise AssertionFailure.new("'#{self.trim}' should be '#{val.to_s.trim}'", val, self) unless self == val
122
132
  end
123
133
 
124
134
  def should_be_empty
125
- raise AssertionFailure.new("The text '#{self.trim}' should be empty", nil, self) unless self.empty?
135
+ raise AssertionFailure.new("'#{self.trim}' should be empty", nil, self) unless self.empty?
126
136
  end
127
137
 
128
138
  def should_not_be(val)
129
- raise AssertionFailure.new("The text '#{self.trim}' should not be '#{val.to_s.trim}'", val, self) unless self != val
139
+ raise AssertionFailure.new("'#{self.trim}' should not be '#{val.to_s.trim}'", val, self) unless self != val
130
140
  end
131
141
 
132
142
  def should_not_be_empty
133
- raise AssertionFailure.new('The text should not be empty', 'nothing', self) unless not self.empty?
143
+ raise AssertionFailure.new('Text should not be empty', 'nothing', self) unless not self.empty?
134
144
  end
135
145
 
136
146
  def should_contain(value)
137
- raise AssertionFailure.new("The value is nil") if value.nil?
138
-
139
- predicate = proc { |x| self.include? x.to_s }
140
- evaluation = SingleEvaluation.new(value)
141
- success = evaluation.call(predicate)
142
-
143
- return if success
144
-
145
- raise AssertionFailure.new("The text '#{self.to_s.trim}' should contain #{evaluation.to_s}", evaluation, self)
147
+ evaluate(value, "'#{self.trim}' should contain #{value.to_s}") do |x|
148
+ self.include? x.to_s
149
+ end
146
150
  end
147
151
 
148
152
  def should_not_contain(val)
149
- raise AssertionFailure.new("The text '#{self.trim}' should not contain '#{val.trim}'", val, self) if self.include? val
153
+ raise AssertionFailure.new("'#{self.trim}' should not contain '#{val.trim}'", val, self) if self.include? val
150
154
  end
151
155
 
152
156
  def should_match(regex)
153
- raise AssertionFailure.new("The text '#{self.trim}' should match '#{val}'", regex, self) unless self.match(regex)
157
+ raise AssertionFailure.new("'#{self.trim}' should match '#{val}'", regex, self) unless self.match(regex)
154
158
  end
155
159
 
156
160
  def should_not_match(regex)
157
- raise AssertionFailure.new("The text '#{self.trim}' should not match '#{val}'", regex, self) if self.match(regex)
161
+ raise AssertionFailure.new("'#{self.trim}' should not match '#{val}'", regex, self) if self.match(regex)
158
162
  end
159
163
 
160
164
  alias :| :or
@@ -162,62 +166,58 @@ module Spectre
162
166
  end
163
167
 
164
168
  class Evaluation
165
- def initialize value, other
166
- @value = value
167
- @other = other
169
+ def initialize val
170
+ @val = val
171
+ end
172
+
173
+ def run &block
174
+ evaluate(@val, block)
168
175
  end
169
176
 
170
- def eval_assertion predicate, val
177
+ def evaluate(val, predicate)
171
178
  if val.is_a? Evaluation
172
- val.call(predicate)
179
+ val.run &predicate
173
180
  else
174
181
  predicate.call(val)
175
182
  end
176
183
  end
177
184
 
178
- alias :| :or
179
- alias :& :and
180
- end
181
-
182
- class SingleEvaluation < Evaluation
183
- def initialize value
184
- super(value, nil)
185
- end
186
-
187
- def call predicate
188
- eval_assertion(predicate, @value)
189
- end
190
-
191
185
  def to_s
192
- @value.to_s
186
+ @val.to_s
193
187
  end
194
188
  end
195
189
 
196
190
  class OrEvaluation < Evaluation
197
- def initialize value, other
198
- super(value, other)
191
+ def initialize val, other
192
+ @val = val
193
+ @other = other
199
194
  end
200
195
 
201
- def call predicate
202
- eval_assertion(predicate, @value) or eval_assertion(predicate, @other)
196
+ def run &block
197
+ res1 = evaluate(@val, block)
198
+ res2 = evaluate(@other, block)
199
+ res1 or res2
203
200
  end
204
201
 
205
202
  def to_s
206
- "(#{@value.to_s} or #{@other.to_s})"
203
+ "(#{@val} or #{@other})"
207
204
  end
208
205
  end
209
206
 
210
207
  class AndEvaluation < Evaluation
211
- def initialize value, other
212
- super(value, other)
208
+ def initialize val, other
209
+ @val = val
210
+ @other = other
213
211
  end
214
212
 
215
- def call predicate
216
- eval_assertion(predicate, @value) and eval_assertion(predicate, @other)
213
+ def run &block
214
+ res1 = evaluate(@val, block)
215
+ res2 = evaluate(@other, block)
216
+ res1 and res2
217
217
  end
218
218
 
219
219
  def to_s
220
- "(#{@value.to_s} and #{@other.to_s})"
220
+ "(#{@val} and #{@other})"
221
221
  end
222
222
  end
223
223
 
@@ -236,18 +236,26 @@ module Spectre
236
236
  @@success = nil
237
237
 
238
238
  def expect desc
239
+ status = 'unknown'
240
+
239
241
  begin
240
- Logger.log_process("expect #{desc}")
242
+ Logging.log_process("expect #{desc}")
241
243
  yield
242
- Logger.log_status(desc, Logger::Status::OK)
244
+ Logging.log_status(desc, Logging::Status::OK)
245
+ status = 'ok'
243
246
  rescue Interrupt => e
247
+ status = 'skipped'
244
248
  raise e
245
249
  rescue AssertionFailure => e
246
- Logger.log_status(desc, Logger::Status::FAILED)
250
+ Logging.log_status(desc, Logging::Status::FAILED)
251
+ status = 'failed'
247
252
  raise AssertionFailure.new(e.message, e.expected, e.actual, desc), cause: nil
248
253
  rescue Exception => e
249
- Logger.log_status(desc, Logger::Status::ERROR)
254
+ Logging.log_status(desc, Logging::Status::ERROR)
255
+ status = 'error'
250
256
  raise AssertionFailure.new("An unexpected error occurred during expectation: #{e.message}", nil, nil, desc), cause: e
257
+ ensure
258
+ Spectre::Runner.current.expectations.append([desc, status])
251
259
  end
252
260
  end
253
261
 
@@ -256,7 +264,7 @@ module Spectre
256
264
  prefix += " '#{desc}'" if desc
257
265
 
258
266
  begin
259
- Logger.log_info(prefix) if desc
267
+ Logging.log_info(prefix) if desc
260
268
  yield
261
269
  @@success = true
262
270
  @@logger.info("#{prefix} finished with success")
@@ -281,7 +289,7 @@ module Spectre
281
289
  end
282
290
 
283
291
  Spectre.register do |config|
284
- @@logger = ::Logger.new(config['log_file'], progname: 'spectre/assertion')
292
+ @@logger = Spectre::Logging::ModuleLogger.new(config, 'spectre/assertion')
285
293
  @@debug = config['debug']
286
294
  end
287
295
 
@@ -0,0 +1,31 @@
1
+ require_relative '../spectre'
2
+
3
+ Thread.abort_on_exception = true
4
+
5
+ module Spectre
6
+ module Async
7
+ class << self
8
+ @@threads = {}
9
+
10
+ def async name='default', &block
11
+ unless @@threads.key? name
12
+ @@threads[name] = []
13
+ end
14
+
15
+ @@threads[name] << Thread.new(&block)
16
+ end
17
+
18
+ def await name='default'
19
+ return unless @@threads.key? name
20
+
21
+ threads = @@threads[name].map { |x| x.join() }
22
+
23
+ @@threads.delete(name)
24
+
25
+ threads.map { |x| x.value }
26
+ end
27
+ end
28
+
29
+ Spectre.delegate :async, :await, to: self
30
+ end
31
+ end
data/lib/spectre/bag.rb CHANGED
@@ -12,7 +12,7 @@ module Spectre
12
12
  end
13
13
  end
14
14
 
15
- Spectre.register do |config|
15
+ Spectre.register do |_config|
16
16
  @@bag = OpenStruct.new
17
17
  end
18
18
 
data/lib/spectre/curl.rb CHANGED
@@ -310,7 +310,7 @@ module Spectre::Curl
310
310
 
311
311
  start_time = Time.now
312
312
 
313
- stdin, stdout, stderr, wait_thr = Open3.popen3(sys_cmd)
313
+ _, stdout, stderr, wait_thr = Open3.popen3(sys_cmd)
314
314
 
315
315
  end_time = Time.now
316
316
 
@@ -359,8 +359,8 @@ module Spectre::Curl
359
359
  end
360
360
 
361
361
  res_log = "[<] #{req_id} #{res[:code]} #{res[:message]} (#{end_time - start_time}s)\n"
362
- res_headers.each do |header|
363
- res_log += "#{header[0].to_s.ljust(30, '.')}: #{header[1].to_s}\n"
362
+ res_headers.each do |http_header|
363
+ res_log += "#{http_header[0].to_s.ljust(30, '.')}: #{http_header[1].to_s}\n"
364
364
  end
365
365
 
366
366
  if res[:body] != nil and not res[:body].empty?
@@ -380,11 +380,9 @@ module Spectre::Curl
380
380
  Spectre.register do |config|
381
381
  @@debug = config['debug']
382
382
 
383
- @@logger = ::Logger.new(config['log_file'], progname: 'spectre/curl')
384
- @@logger.level = @@debug ? Logger::DEBUG : Logger::INFO
383
+ @@logger = Spectre::Logging::ModuleLogger.new(config, 'spectre/curl')
385
384
 
386
385
  @@secure_keys = config['secure_keys'] || []
387
-
388
386
  @@curl_path = config['curl_path'] || 'curl'
389
387
 
390
388
  if config.key? 'http'
@@ -119,6 +119,10 @@ class ::Hash
119
119
  end
120
120
 
121
121
  class ::Array
122
+ def first_element
123
+ self[0]
124
+ end
125
+
122
126
  def last_element
123
127
  self[-1]
124
128
  end
@@ -42,8 +42,6 @@ module Spectre::Http
42
42
  net_req['X-Auth-Token'] = token
43
43
  end
44
44
 
45
- private
46
-
47
45
  def self.authenticate keystone_url, username, password, project, domain, cert
48
46
  auth_data = {
49
47
  auth: {
data/lib/spectre/http.rb CHANGED
@@ -19,7 +19,7 @@ module Spectre
19
19
  'cert' => nil,
20
20
  'headers' => nil,
21
21
  'query' => nil,
22
- 'content_type' => '',
22
+ 'content_type' => nil,
23
23
  'timeout' => 180,
24
24
  'retries' => 0,
25
25
  }
@@ -27,9 +27,6 @@ module Spectre
27
27
  @@modules = []
28
28
 
29
29
  class HttpError < Exception
30
- def initialize message
31
- super message
32
- end
33
30
  end
34
31
 
35
32
  class SpectreHttpRequest < Spectre::DslClass
@@ -80,8 +77,7 @@ module Spectre
80
77
  data = data.to_h if data.is_a? OpenStruct
81
78
  body JSON.pretty_generate(data)
82
79
 
83
- # TODO: Only set content type, if not explicitly set
84
- content_type('application/json')
80
+ content_type('application/json') unless @__req['content_type']
85
81
  end
86
82
 
87
83
  def body body_content
@@ -112,6 +108,10 @@ module Spectre
112
108
  @__req['use_ssl'] = true
113
109
  end
114
110
 
111
+ def no_log!
112
+ @__req['no_log'] = true
113
+ end
114
+
115
115
  def to_s
116
116
  @__req.to_s
117
117
  end
@@ -314,7 +314,14 @@ module Spectre
314
314
 
315
315
  req_log = "[>] #{req_id} #{req['method']} #{uri}\n"
316
316
  req_log += header_to_s(net_req)
317
- req_log += try_format_json(req['body'], pretty: true) if req['body'] != nil and not req['body'].empty?
317
+
318
+ unless req['body'].nil? or req['body'].empty?
319
+ unless req['no_log']
320
+ req_log += try_format_json(req['body'], pretty: true)
321
+ else
322
+ req_log += '[...]'
323
+ end
324
+ end
318
325
 
319
326
  @@logger.info(req_log)
320
327
 
@@ -345,7 +352,14 @@ module Spectre
345
352
 
346
353
  res_log = "[<] #{req_id} #{net_res.code} #{net_res.message} (#{end_time - start_time}s)\n"
347
354
  res_log += header_to_s(net_res)
348
- res_log += try_format_json(net_res.body, pretty: true) unless net_res.body.nil? or net_res.body.empty?
355
+
356
+ unless net_res.body.nil? or net_res.body.empty?
357
+ unless req['no_log']
358
+ res_log += try_format_json(net_res.body, pretty: true)
359
+ else
360
+ res_log += '[...]'
361
+ end
362
+ end
349
363
 
350
364
  @@logger.info(res_log)
351
365
 
@@ -361,7 +375,7 @@ module Spectre
361
375
  end
362
376
 
363
377
  Spectre.register do |config|
364
- @@logger = ::Logger.new(config['log_file'], progname: 'spectre/http')
378
+ @@logger = Spectre::Logging::ModuleLogger.new(config, 'spectre/http')
365
379
  @@secure_keys = config['secure_keys'] || []
366
380
  @@debug = config['debug']
367
381
 
@@ -1,7 +1,7 @@
1
1
  require 'ectoplasm'
2
2
 
3
3
  module Spectre
4
- module Logger
4
+ module Logging
5
5
  class Console
6
6
  def initialize config
7
7
  raise 'No log format section in config for console logger' unless config.key? 'log_format' and config['log_format'].key? 'console'
@@ -113,8 +113,14 @@ module Spectre
113
113
  print_line('', txt)
114
114
  end
115
115
 
116
- def log_skipped _spec
117
- print_line('', Status::SKIPPED.grey)
116
+ def log_skipped _spec, message=nil
117
+ txt = Status::SKIPPED
118
+
119
+ unless message.nil?
120
+ txt += ' - ' + message
121
+ end
122
+
123
+ print_line('', txt.grey)
118
124
  end
119
125
 
120
126
  private
@@ -1,5 +1,5 @@
1
1
  module Spectre
2
- module Logger
2
+ module Logging
3
3
  class File
4
4
  def initialize config
5
5
  raise 'No log format section in config for console logger' unless config.key? 'log_format' and config['log_format'].key? 'file'
@@ -9,87 +9,93 @@ module Spectre
9
9
  @fmt_end_group = @config['end_group']
10
10
  @fmt_sep = @config['separator']
11
11
 
12
- @file_log = ::Logger.new config['log_file'], progname: 'spectre'
12
+ @file_log = ::Logger.new(config['log_file'], progname: 'spectre')
13
13
  @file_log.level = config['debug'] ? 'DEBUG' : 'INFO'
14
14
  end
15
15
 
16
16
  def start_subject subject
17
- @file_log.debug "start running subject '#{subject.desc}'"
17
+ @file_log.debug("start running subject '#{subject.desc}'")
18
18
  end
19
19
 
20
20
  def end_subject subject
21
- @file_log.debug "subject '#{subject.desc}' finished"
21
+ @file_log.debug("subject '#{subject.desc}' finished")
22
22
  end
23
23
 
24
24
  def start_context context
25
25
  if context and context.__desc
26
- @file_log.debug "start running context '#{context.__desc}'"
26
+ @file_log.debug("start running context '#{context.__desc}'")
27
27
  else
28
- @file_log.debug "start running main context of #{context.__subject.desc}"
28
+ @file_log.debug("start running main context of #{context.__subject.desc}")
29
29
  end
30
30
  end
31
31
 
32
32
  def end_context context
33
33
  if context and context.__desc
34
- @file_log.debug "context '#{context.__desc}' finished"
34
+ @file_log.debug("context '#{context.__desc}' finished")
35
35
  else
36
- @file_log.debug "main context finished of #{context.__subject.desc}"
36
+ @file_log.debug("main context finished of #{context.__subject.desc}")
37
37
  end
38
38
  end
39
39
 
40
40
  def start_spec spec, data=nil
41
41
  log_msg = "start running spec [#{spec.name}] '#{spec.desc}'"
42
42
  log_msg += " with data #{data}" if data
43
- @file_log.debug log_msg
43
+ @file_log.debug(log_msg)
44
44
  end
45
45
 
46
46
  def end_spec spec, data=nil
47
47
  log_msg = "running spec [#{spec.name}] '#{spec.desc}'"
48
48
  log_msg += " with data #{data}" if data
49
49
  log_msg += " finished"
50
- @file_log.debug log_msg
50
+ @file_log.debug(log_msg)
51
51
  end
52
52
 
53
53
  def log_separator desc
54
54
  desc = @fmt_sep.gsub('<desc>', desc) if @fmt_sep
55
- @file_log.info desc
55
+ @file_log.info(desc)
56
56
  end
57
57
 
58
58
  def start_group desc
59
59
  desc = @fmt_start_group.gsub('<desc>', desc) if @fmt_start_group
60
- @file_log.info desc
60
+ @file_log.info(desc)
61
61
  end
62
62
 
63
63
  def end_group desc
64
64
  desc = @fmt_end_group.gsub('<desc>', desc) if @fmt_end_group
65
- @file_log.info desc
65
+ @file_log.info(desc)
66
66
  end
67
67
 
68
68
  def log_process desc
69
- @file_log.debug desc
69
+ @file_log.debug(desc)
70
70
  end
71
71
 
72
72
  def log_info message
73
- @file_log.info "#{Status::INFO} #{message}"
73
+ @file_log.info("#{Status::INFO} #{message}")
74
74
  end
75
75
 
76
76
  def log_debug message
77
- @file_log.debug "#{Status::DEBUG} #{message}"
77
+ @file_log.debug("#{Status::DEBUG} #{message}")
78
78
  end
79
79
 
80
80
  def log_error spec, exception
81
81
  file, line = exception.backtrace[0].match(/(.*\.rb):(\d+)/).captures
82
- @file_log.error "An unexpected error occurred at '#{file}:#{line}' while running spec '#{spec.name}': [#{exception.class}] #{exception.message}\n#{exception.backtrace.join "\n"}"
82
+ @file_log.error("An unexpected error occurred at '#{file}:#{line}' while running spec '#{spec.name}': [#{exception.class}] #{exception.message}\n#{exception.backtrace.join "\n"}")
83
83
  end
84
84
 
85
- def log_skipped spec
86
- @file_log.warn "spec '#{spec.desc}' canceled by user"
85
+ def log_skipped spec, message=nil
86
+ txt = "spec '#{spec.desc}' skipped"
87
+
88
+ unless message.nil?
89
+ txt += ': ' + message
90
+ end
91
+
92
+ @file_log.warn(txt)
87
93
  end
88
94
 
89
95
  def log_status desc, status, annotation=nil
90
96
  msg = "expected #{desc}...#{status.upcase}"
91
97
  msg += " - #{annotation}" if annotation
92
- @file_log.debug msg
98
+ @file_log.debug(msg)
93
99
  end
94
100
  end
95
101
  end