spectre-core 1.12.0 → 1.12.1
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 +4 -4
- data/exe/spectre +516 -516
- data/lib/spectre/assertion.rb +10 -10
- data/lib/spectre/bag.rb +21 -21
- data/lib/spectre/curl.rb +397 -397
- data/lib/spectre/diagnostic.rb +39 -39
- data/lib/spectre/environment.rb +30 -30
- data/lib/spectre/helpers.rb +133 -133
- data/lib/spectre/http.rb +373 -364
- data/lib/spectre/logger/console.rb +143 -143
- data/lib/spectre/logger/file.rb +96 -96
- data/lib/spectre/logger.rb +146 -146
- data/lib/spectre/mixin.rb +58 -58
- data/lib/spectre/reporter/console.rb +101 -102
- data/lib/spectre/reporter/junit.rb +100 -100
- data/lib/spectre/resources.rb +49 -49
- data/lib/spectre.rb +447 -440
- metadata +12 -12
data/lib/spectre/logger.rb
CHANGED
@@ -1,146 +1,146 @@
|
|
1
|
-
require_relative '../spectre'
|
2
|
-
require 'date'
|
3
|
-
|
4
|
-
module Spectre
|
5
|
-
module Logger
|
6
|
-
module Status
|
7
|
-
OK = '[ok]'
|
8
|
-
FAILED = '[failed]'
|
9
|
-
ERROR = '[error]'
|
10
|
-
INFO = '[info]'
|
11
|
-
SKIPPED = '[skipped]'
|
12
|
-
DEBUG = '[debug]'
|
13
|
-
end
|
14
|
-
|
15
|
-
class << self
|
16
|
-
@@debug = false
|
17
|
-
@@logger = []
|
18
|
-
|
19
|
-
def debug!
|
20
|
-
@@debug = true
|
21
|
-
end
|
22
|
-
|
23
|
-
def debug?
|
24
|
-
@@debug
|
25
|
-
end
|
26
|
-
|
27
|
-
def add logger
|
28
|
-
@@logger.append logger
|
29
|
-
end
|
30
|
-
|
31
|
-
def start_subject subject
|
32
|
-
delegate(:start_subject, subject)
|
33
|
-
end
|
34
|
-
|
35
|
-
def end_subject subject
|
36
|
-
delegate(:end_subject, subject)
|
37
|
-
end
|
38
|
-
|
39
|
-
def start_context context
|
40
|
-
delegate(:start_context, context)
|
41
|
-
end
|
42
|
-
|
43
|
-
def end_context context
|
44
|
-
delegate(:end_context, context)
|
45
|
-
end
|
46
|
-
|
47
|
-
def start_spec spec, data=nil
|
48
|
-
delegate(:start_spec, spec, data)
|
49
|
-
end
|
50
|
-
|
51
|
-
def end_spec spec, data=nil
|
52
|
-
delegate(:end_spec, spec, data)
|
53
|
-
end
|
54
|
-
|
55
|
-
def log_subject subject
|
56
|
-
begin
|
57
|
-
start_subject(subject)
|
58
|
-
yield
|
59
|
-
ensure
|
60
|
-
end_subject(subject)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def log_context context
|
65
|
-
begin
|
66
|
-
start_context(context)
|
67
|
-
yield
|
68
|
-
ensure
|
69
|
-
end_context(context)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def log_spec spec, data=nil
|
74
|
-
start_spec(spec, data)
|
75
|
-
yield
|
76
|
-
end_spec(spec, data)
|
77
|
-
end
|
78
|
-
|
79
|
-
def log_separator desc
|
80
|
-
delegate(:log_separator, desc)
|
81
|
-
end
|
82
|
-
|
83
|
-
def start_group desc
|
84
|
-
delegate(:start_group, desc)
|
85
|
-
end
|
86
|
-
|
87
|
-
def end_group desc
|
88
|
-
delegate(:end_group, desc)
|
89
|
-
end
|
90
|
-
|
91
|
-
def log_process desc
|
92
|
-
delegate(:log_process, desc)
|
93
|
-
end
|
94
|
-
|
95
|
-
def log_info message
|
96
|
-
add_log(message)
|
97
|
-
delegate(:log_info, message)
|
98
|
-
end
|
99
|
-
|
100
|
-
def log_debug message
|
101
|
-
return unless @@debug
|
102
|
-
|
103
|
-
add_log(message)
|
104
|
-
delegate(:log_debug, message)
|
105
|
-
end
|
106
|
-
|
107
|
-
def log_error spec, exception
|
108
|
-
add_log(exception)
|
109
|
-
delegate(:log_error, spec, exception)
|
110
|
-
end
|
111
|
-
|
112
|
-
def log_skipped spec
|
113
|
-
delegate(:log_skipped, spec)
|
114
|
-
end
|
115
|
-
|
116
|
-
def log_status desc, status, annotation=nil
|
117
|
-
delegate(:log_status, desc, status, annotation)
|
118
|
-
end
|
119
|
-
|
120
|
-
def group desc
|
121
|
-
Logger.start_group desc
|
122
|
-
yield
|
123
|
-
Logger.end_group desc
|
124
|
-
end
|
125
|
-
|
126
|
-
alias_method :info, :log_info
|
127
|
-
alias_method :log, :log_info
|
128
|
-
alias_method :debug, :log_debug
|
129
|
-
alias_method :separate, :log_separator
|
130
|
-
|
131
|
-
private
|
132
|
-
|
133
|
-
def delegate method, *args
|
134
|
-
@@logger.each do |logger|
|
135
|
-
logger.send(method, *args) if logger.respond_to? method
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def add_log message
|
140
|
-
Spectre::Runner.current.log.append([DateTime.now, message])
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
Spectre.delegate :log, :info, :debug, :group, :separate, to: self
|
145
|
-
end
|
146
|
-
end
|
1
|
+
require_relative '../spectre'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module Spectre
|
5
|
+
module Logger
|
6
|
+
module Status
|
7
|
+
OK = '[ok]'
|
8
|
+
FAILED = '[failed]'
|
9
|
+
ERROR = '[error]'
|
10
|
+
INFO = '[info]'
|
11
|
+
SKIPPED = '[skipped]'
|
12
|
+
DEBUG = '[debug]'
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
@@debug = false
|
17
|
+
@@logger = []
|
18
|
+
|
19
|
+
def debug!
|
20
|
+
@@debug = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def debug?
|
24
|
+
@@debug
|
25
|
+
end
|
26
|
+
|
27
|
+
def add logger
|
28
|
+
@@logger.append logger
|
29
|
+
end
|
30
|
+
|
31
|
+
def start_subject subject
|
32
|
+
delegate(:start_subject, subject)
|
33
|
+
end
|
34
|
+
|
35
|
+
def end_subject subject
|
36
|
+
delegate(:end_subject, subject)
|
37
|
+
end
|
38
|
+
|
39
|
+
def start_context context
|
40
|
+
delegate(:start_context, context)
|
41
|
+
end
|
42
|
+
|
43
|
+
def end_context context
|
44
|
+
delegate(:end_context, context)
|
45
|
+
end
|
46
|
+
|
47
|
+
def start_spec spec, data=nil
|
48
|
+
delegate(:start_spec, spec, data)
|
49
|
+
end
|
50
|
+
|
51
|
+
def end_spec spec, data=nil
|
52
|
+
delegate(:end_spec, spec, data)
|
53
|
+
end
|
54
|
+
|
55
|
+
def log_subject subject
|
56
|
+
begin
|
57
|
+
start_subject(subject)
|
58
|
+
yield
|
59
|
+
ensure
|
60
|
+
end_subject(subject)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def log_context context
|
65
|
+
begin
|
66
|
+
start_context(context)
|
67
|
+
yield
|
68
|
+
ensure
|
69
|
+
end_context(context)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def log_spec spec, data=nil
|
74
|
+
start_spec(spec, data)
|
75
|
+
yield
|
76
|
+
end_spec(spec, data)
|
77
|
+
end
|
78
|
+
|
79
|
+
def log_separator desc
|
80
|
+
delegate(:log_separator, desc)
|
81
|
+
end
|
82
|
+
|
83
|
+
def start_group desc
|
84
|
+
delegate(:start_group, desc)
|
85
|
+
end
|
86
|
+
|
87
|
+
def end_group desc
|
88
|
+
delegate(:end_group, desc)
|
89
|
+
end
|
90
|
+
|
91
|
+
def log_process desc
|
92
|
+
delegate(:log_process, desc)
|
93
|
+
end
|
94
|
+
|
95
|
+
def log_info message
|
96
|
+
add_log(message)
|
97
|
+
delegate(:log_info, message)
|
98
|
+
end
|
99
|
+
|
100
|
+
def log_debug message
|
101
|
+
return unless @@debug
|
102
|
+
|
103
|
+
add_log(message)
|
104
|
+
delegate(:log_debug, message)
|
105
|
+
end
|
106
|
+
|
107
|
+
def log_error spec, exception
|
108
|
+
add_log(exception)
|
109
|
+
delegate(:log_error, spec, exception)
|
110
|
+
end
|
111
|
+
|
112
|
+
def log_skipped spec
|
113
|
+
delegate(:log_skipped, spec)
|
114
|
+
end
|
115
|
+
|
116
|
+
def log_status desc, status, annotation=nil
|
117
|
+
delegate(:log_status, desc, status, annotation)
|
118
|
+
end
|
119
|
+
|
120
|
+
def group desc
|
121
|
+
Logger.start_group desc
|
122
|
+
yield
|
123
|
+
Logger.end_group desc
|
124
|
+
end
|
125
|
+
|
126
|
+
alias_method :info, :log_info
|
127
|
+
alias_method :log, :log_info
|
128
|
+
alias_method :debug, :log_debug
|
129
|
+
alias_method :separate, :log_separator
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def delegate method, *args
|
134
|
+
@@logger.each do |logger|
|
135
|
+
logger.send(method, *args) if logger.respond_to? method
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def add_log message
|
140
|
+
Spectre::Runner.current.log.append([DateTime.now, message])
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
Spectre.delegate :log, :info, :debug, :group, :separate, to: self
|
145
|
+
end
|
146
|
+
end
|
data/lib/spectre/mixin.rb
CHANGED
@@ -1,58 +1,58 @@
|
|
1
|
-
require_relative '../spectre'
|
2
|
-
require_relative 'logger'
|
3
|
-
|
4
|
-
require 'ostruct'
|
5
|
-
|
6
|
-
module Spectre
|
7
|
-
module Mixin
|
8
|
-
class MixinContext < Spectre::DslClass
|
9
|
-
def initialize desc
|
10
|
-
@__desc = desc
|
11
|
-
end
|
12
|
-
|
13
|
-
def required params, *keys
|
14
|
-
missing_keys = keys.select { |x| !params.to_h.key? x }
|
15
|
-
Spectre::Logger.log_debug("required parameters for '#{@__desc}': #{keys.join ', '}")
|
16
|
-
raise ArgumentError, "mixin '#{@__desc}' requires #{keys.join ', '}, but only has #{missing_keys.join ', '} given" unless missing_keys.empty?
|
17
|
-
end
|
18
|
-
|
19
|
-
def optional params, *keys
|
20
|
-
Spectre::Logger.log_debug("optional parameters for '#{@__desc}': #{keys.join ', '}")
|
21
|
-
params
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class << self
|
26
|
-
@@mixins = {}
|
27
|
-
|
28
|
-
def mixin desc, &block
|
29
|
-
@@mixins[desc] = block
|
30
|
-
end
|
31
|
-
|
32
|
-
def run desc, with: []
|
33
|
-
raise "no mixin with desc '#{desc}' defined" unless @@mixins.key? desc
|
34
|
-
|
35
|
-
Spectre::Logger.log_debug "running mixin '#{desc}'"
|
36
|
-
|
37
|
-
params = with || {}
|
38
|
-
|
39
|
-
ctx = MixinContext.new(desc)
|
40
|
-
|
41
|
-
if params.is_a? Array
|
42
|
-
return_val = ctx._execute(*params, &@@mixins[desc])
|
43
|
-
elsif params.is_a? Hash
|
44
|
-
return_val = ctx._execute(OpenStruct.new(params), &@@mixins[desc])
|
45
|
-
else
|
46
|
-
return_val = ctx._execute(params, &@@mixins[desc])
|
47
|
-
end
|
48
|
-
|
49
|
-
return_val.is_a?(Hash) ? OpenStruct.new(return_val) : return_val
|
50
|
-
end
|
51
|
-
|
52
|
-
alias_method :also, :run
|
53
|
-
alias_method :step, :run
|
54
|
-
end
|
55
|
-
|
56
|
-
Spectre.delegate :mixin, :run, :also, :step, to: self
|
57
|
-
end
|
58
|
-
end
|
1
|
+
require_relative '../spectre'
|
2
|
+
require_relative 'logger'
|
3
|
+
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module Spectre
|
7
|
+
module Mixin
|
8
|
+
class MixinContext < Spectre::DslClass
|
9
|
+
def initialize desc
|
10
|
+
@__desc = desc
|
11
|
+
end
|
12
|
+
|
13
|
+
def required params, *keys
|
14
|
+
missing_keys = keys.select { |x| !params.to_h.key? x }
|
15
|
+
Spectre::Logger.log_debug("required parameters for '#{@__desc}': #{keys.join ', '}")
|
16
|
+
raise ArgumentError, "mixin '#{@__desc}' requires #{keys.join ', '}, but only has #{missing_keys.join ', '} given" unless missing_keys.empty?
|
17
|
+
end
|
18
|
+
|
19
|
+
def optional params, *keys
|
20
|
+
Spectre::Logger.log_debug("optional parameters for '#{@__desc}': #{keys.join ', '}")
|
21
|
+
params
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
@@mixins = {}
|
27
|
+
|
28
|
+
def mixin desc, &block
|
29
|
+
@@mixins[desc] = block
|
30
|
+
end
|
31
|
+
|
32
|
+
def run desc, with: []
|
33
|
+
raise "no mixin with desc '#{desc}' defined" unless @@mixins.key? desc
|
34
|
+
|
35
|
+
Spectre::Logger.log_debug "running mixin '#{desc}'"
|
36
|
+
|
37
|
+
params = with || {}
|
38
|
+
|
39
|
+
ctx = MixinContext.new(desc)
|
40
|
+
|
41
|
+
if params.is_a? Array
|
42
|
+
return_val = ctx._execute(*params, &@@mixins[desc])
|
43
|
+
elsif params.is_a? Hash
|
44
|
+
return_val = ctx._execute(OpenStruct.new(params), &@@mixins[desc])
|
45
|
+
else
|
46
|
+
return_val = ctx._execute(params, &@@mixins[desc])
|
47
|
+
end
|
48
|
+
|
49
|
+
return_val.is_a?(Hash) ? OpenStruct.new(return_val) : return_val
|
50
|
+
end
|
51
|
+
|
52
|
+
alias_method :also, :run
|
53
|
+
alias_method :step, :run
|
54
|
+
end
|
55
|
+
|
56
|
+
Spectre.delegate :mixin, :run, :also, :step, to: self
|
57
|
+
end
|
58
|
+
end
|
@@ -1,102 +1,101 @@
|
|
1
|
-
module Spectre::Reporter
|
2
|
-
class Console
|
3
|
-
def initialize config
|
4
|
-
@config = config
|
5
|
-
end
|
6
|
-
|
7
|
-
def report run_infos
|
8
|
-
report_str = ''
|
9
|
-
|
10
|
-
errors = 0
|
11
|
-
failures = 0
|
12
|
-
skipped = run_infos.select { |x| x.skipped? }.count
|
13
|
-
|
14
|
-
run_infos
|
15
|
-
.select { |x| x.error != nil or x.failure != nil }
|
16
|
-
.each_with_index do |run_info, index|
|
17
|
-
spec = run_info.spec
|
18
|
-
|
19
|
-
report_str += "\n#{index+1}) #{format_title(run_info)}\n"
|
20
|
-
|
21
|
-
if run_info.failure
|
22
|
-
report_str += " Expected #{run_info.failure.expectation}"
|
23
|
-
report_str += " with #{run_info.data}" if run_info.data
|
24
|
-
report_str += " during #{spec.context.__desc}" if spec.context.__desc
|
25
|
-
|
26
|
-
report_str += " but it failed"
|
27
|
-
|
28
|
-
if run_info.failure.cause
|
29
|
-
report_str += "\n with an unexpected error:\n"
|
30
|
-
report_str += format_exception(run_info.failure.cause)
|
31
|
-
|
32
|
-
elsif run_info.failure.message and not run_info.failure.message.empty?
|
33
|
-
report_str += " with:\n #{run_info.failure.message}"
|
34
|
-
|
35
|
-
else
|
36
|
-
report_str += '.'
|
37
|
-
end
|
38
|
-
|
39
|
-
report_str += "\n"
|
40
|
-
failures += 1
|
41
|
-
|
42
|
-
else
|
43
|
-
report_str += " but an unexpected error occured during run\n"
|
44
|
-
report_str += format_exception(run_info.error)
|
45
|
-
errors += 1
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
if failures + errors > 0
|
50
|
-
summary = ''
|
51
|
-
summary += "#{run_infos.length - failures - errors - skipped} succeeded "
|
52
|
-
summary += "#{failures} failures " if failures > 0
|
53
|
-
summary += "#{errors} errors " if errors > 0
|
54
|
-
summary += "#{skipped} skipped " if skipped > 0
|
55
|
-
summary += "#{run_infos.length} total"
|
56
|
-
print "\n#{summary}\n".red
|
57
|
-
else
|
58
|
-
summary = ''
|
59
|
-
summary = "\nRun finished successfully"
|
60
|
-
summary += " (#{skipped} skipped)" if skipped > 0
|
61
|
-
print "#{summary}\n".green
|
62
|
-
end
|
63
|
-
|
64
|
-
puts report_str.red
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def format_title run_info
|
70
|
-
title = run_info.spec.subject.desc
|
71
|
-
title += ' ' + run_info.spec.desc
|
72
|
-
title += " (#{'%.3f' % run_info.duration}s)"
|
73
|
-
title += " [#{run_info.spec.name}]"
|
74
|
-
title
|
75
|
-
end
|
76
|
-
|
77
|
-
def format_exception error
|
78
|
-
non_spectre_files = error.backtrace.select { |x| !x.include? 'lib/spectre' }
|
79
|
-
|
80
|
-
if non_spectre_files.count > 0
|
81
|
-
causing_file = non_spectre_files.first
|
82
|
-
else
|
83
|
-
causing_file = error.backtrace[0]
|
84
|
-
end
|
85
|
-
|
86
|
-
matches = causing_file.match(/(.*\.rb):(\d+)/)
|
87
|
-
|
88
|
-
return '' unless matches
|
89
|
-
|
90
|
-
file, line = matches.captures
|
91
|
-
file.slice!(Dir.pwd + '/')
|
92
|
-
|
93
|
-
str = ''
|
94
|
-
str += " file.....: #{file}\n"
|
95
|
-
str += "
|
96
|
-
str += "
|
97
|
-
str += "
|
98
|
-
str
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
1
|
+
module Spectre::Reporter
|
2
|
+
class Console
|
3
|
+
def initialize config
|
4
|
+
@config = config
|
5
|
+
end
|
6
|
+
|
7
|
+
def report run_infos
|
8
|
+
report_str = ''
|
9
|
+
|
10
|
+
errors = 0
|
11
|
+
failures = 0
|
12
|
+
skipped = run_infos.select { |x| x.skipped? }.count
|
13
|
+
|
14
|
+
run_infos
|
15
|
+
.select { |x| x.error != nil or x.failure != nil }
|
16
|
+
.each_with_index do |run_info, index|
|
17
|
+
spec = run_info.spec
|
18
|
+
|
19
|
+
report_str += "\n#{index+1}) #{format_title(run_info)}\n"
|
20
|
+
|
21
|
+
if run_info.failure
|
22
|
+
report_str += " Expected #{run_info.failure.expectation}"
|
23
|
+
report_str += " with #{run_info.data}" if run_info.data
|
24
|
+
report_str += " during #{spec.context.__desc}" if spec.context.__desc
|
25
|
+
|
26
|
+
report_str += " but it failed"
|
27
|
+
|
28
|
+
if run_info.failure.cause
|
29
|
+
report_str += "\n with an unexpected error:\n"
|
30
|
+
report_str += format_exception(run_info.failure.cause)
|
31
|
+
|
32
|
+
elsif run_info.failure.message and not run_info.failure.message.empty?
|
33
|
+
report_str += " with:\n #{run_info.failure.message}"
|
34
|
+
|
35
|
+
else
|
36
|
+
report_str += '.'
|
37
|
+
end
|
38
|
+
|
39
|
+
report_str += "\n"
|
40
|
+
failures += 1
|
41
|
+
|
42
|
+
else
|
43
|
+
report_str += " but an unexpected error occured during run\n"
|
44
|
+
report_str += format_exception(run_info.error)
|
45
|
+
errors += 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if failures + errors > 0
|
50
|
+
summary = ''
|
51
|
+
summary += "#{run_infos.length - failures - errors - skipped} succeeded "
|
52
|
+
summary += "#{failures} failures " if failures > 0
|
53
|
+
summary += "#{errors} errors " if errors > 0
|
54
|
+
summary += "#{skipped} skipped " if skipped > 0
|
55
|
+
summary += "#{run_infos.length} total"
|
56
|
+
print "\n#{summary}\n".red
|
57
|
+
else
|
58
|
+
summary = ''
|
59
|
+
summary = "\nRun finished successfully"
|
60
|
+
summary += " (#{skipped} skipped)" if skipped > 0
|
61
|
+
print "#{summary}\n".green
|
62
|
+
end
|
63
|
+
|
64
|
+
puts report_str.red
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def format_title run_info
|
70
|
+
title = run_info.spec.subject.desc
|
71
|
+
title += ' ' + run_info.spec.desc
|
72
|
+
title += " (#{'%.3f' % run_info.duration}s)"
|
73
|
+
title += " [#{run_info.spec.name}]"
|
74
|
+
title
|
75
|
+
end
|
76
|
+
|
77
|
+
def format_exception error
|
78
|
+
non_spectre_files = error.backtrace.select { |x| !x.include? 'lib/spectre' }
|
79
|
+
|
80
|
+
if non_spectre_files.count > 0
|
81
|
+
causing_file = non_spectre_files.first
|
82
|
+
else
|
83
|
+
causing_file = error.backtrace[0]
|
84
|
+
end
|
85
|
+
|
86
|
+
matches = causing_file.match(/(.*\.rb):(\d+)/)
|
87
|
+
|
88
|
+
return '' unless matches
|
89
|
+
|
90
|
+
file, line = matches.captures
|
91
|
+
file.slice!(Dir.pwd + '/')
|
92
|
+
|
93
|
+
str = ''
|
94
|
+
str += " file.....: #{file}:#{line}\n"
|
95
|
+
str += " type.....: #{error.class}\n"
|
96
|
+
str += " message..: #{error.message}\n"
|
97
|
+
str += " backtrace: \n #{error.backtrace.join("\n ")}\n" if @config['debug']
|
98
|
+
str
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|