ciat 0.4.8 → 0.4.9
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.
- data/README.rdoc +4 -4
- data/lib/ciat.rb +3 -5
- data/lib/ciat/differs/html_differ.rb +3 -4
- data/lib/ciat/erb_helpers.rb +6 -4
- data/lib/ciat/feedback/composite.rb +2 -2
- data/lib/ciat/feedback/feedback_counter.rb +2 -2
- data/lib/ciat/feedback/html_feedback.rb +25 -8
- data/lib/ciat/feedback/html_feedback_builder.rb +28 -0
- data/lib/ciat/feedback/return_status.rb +1 -1
- data/lib/ciat/feedback/standard_output.rb +1 -1
- data/lib/ciat/io.rb +17 -0
- data/lib/ciat/processors/basic_processing.rb +21 -30
- data/lib/ciat/{executors → processors}/java.rb +7 -8
- data/lib/ciat/{executors → processors}/parrot.rb +9 -10
- data/lib/ciat/rake_task.rb +1 -1
- data/lib/ciat/subresult.rb +20 -0
- data/lib/ciat/suite.rb +27 -38
- data/lib/ciat/suite_builder.rb +54 -0
- data/lib/ciat/test.rb +32 -18
- data/lib/ciat/test_element.rb +7 -2
- data/lib/ciat/{crate.rb → test_file.rb} +24 -16
- data/lib/ciat/test_result.rb +24 -0
- data/lib/ciat/traffic_light.rb +6 -0
- data/lib/templates/detail_row.html.erb +5 -4
- data/lib/templates/group_header.html.erb +2 -2
- data/lib/templates/report.html.erb +1 -1
- data/lib/templates/summary_row.html.erb +4 -4
- data/spec/ciat/erb_helpers_spec.rb +71 -0
- data/spec/ciat/feedback/composite_spec.rb +28 -0
- data/spec/ciat/feedback/feedback_counter_spec.rb +91 -0
- data/spec/ciat/feedback/html_feedback_spec.rb +48 -0
- data/spec/ciat/feedback/return_status_spec.rb +90 -0
- data/spec/ciat/feedback/standard_output_spec.rb +109 -0
- data/spec/ciat/processors/basic_processing_spec.rb +146 -0
- data/spec/ciat/processors/java_spec.rb +31 -0
- data/spec/ciat/processors/parrot_spec.rb +40 -0
- data/spec/ciat/subresult_spec.rb +40 -0
- data/spec/ciat/suite_builder_spec.rb +70 -0
- data/spec/ciat/suite_spec.rb +64 -0
- data/spec/ciat/test_element_spec.rb +78 -0
- data/spec/ciat/test_file_spec.rb +96 -0
- data/spec/ciat/test_spec.rb +127 -0
- data/spec/ciat/traffic_light_spec.rb +41 -0
- data/spec/ciat_spec.rb +2 -0
- data/spec/spec_helper.rb +134 -0
- data/spec/templates/detail_row/elements_html_erb_spec.rb +60 -0
- data/spec/templates/detail_row_html_erb_spec.rb +73 -0
- data/spec/templates/elements/diff_html_erb_spec.rb +28 -0
- data/spec/templates/elements/plain_element_html_erb_spec.rb +36 -0
- data/spec/templates/report_html_erb_spec.rb +85 -0
- data/spec/templates/summary_row_html_erb_spec.rb +93 -0
- data/spec/templates/test_numbers_html_erb_spec.rb +82 -0
- metadata +54 -32
- data/History.txt +0 -96
- data/Rakefile +0 -40
- data/ciat.gemspec +0 -53
- data/lib/ciat/cargo.rb +0 -55
- data/lib/ciat/compilers/java.rb +0 -54
- data/lib/ciat/processors/copy.rb +0 -37
- data/lib/ciat/version.rb +0 -7
data/README.rdoc
CHANGED
@@ -69,8 +69,8 @@ When the "Parrot executor" is run on the compilation, it'll also pass in <code>8
|
|
69
69
|
This sample +Rakefile+ will pull everything together:
|
70
70
|
|
71
71
|
require 'ciat'
|
72
|
-
require 'ciat/
|
73
|
-
require 'ciat/
|
72
|
+
require 'ciat/processors/java'
|
73
|
+
require 'ciat/processors/parrot'
|
74
74
|
|
75
75
|
def compiler
|
76
76
|
classpath = Dir.glob('../lib/*.jar').join(':') + ":../bin"
|
@@ -78,7 +78,7 @@ This sample +Rakefile+ will pull everything together:
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def executor
|
81
|
-
CIAT::
|
81
|
+
CIAT::Processors::Parrot.new
|
82
82
|
end
|
83
83
|
|
84
84
|
CIAT::RakeTask.new do |t|
|
@@ -97,7 +97,7 @@ documentation for CIAT::RakeTask for more information.
|
|
97
97
|
|
98
98
|
* Pronounce "CIAT" as "dog". (See this funny video[http://www.youtube.com/watch?v=tyQvjKqXA0Y].)
|
99
99
|
* Must have +diff+ executable.
|
100
|
-
* You have to provide your own target-code
|
100
|
+
* You have to provide your own target-code processors (e.g., +parrot+ for the
|
101
101
|
Parrot Virtual Machine, +spim+ for MIPS emulation, etc.)
|
102
102
|
|
103
103
|
|
data/lib/ciat.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
$:.unshift(File.dirname(__FILE__)) unless
|
2
2
|
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
3
|
|
4
|
-
# Loads in the basic files needed to use CIAT.
|
5
|
-
#
|
6
|
-
# at.
|
4
|
+
# Loads in the basic files needed to use CIAT. Processors have to be required
|
5
|
+
# separately. CIAT::Suite is the class you really want to look at.
|
7
6
|
module CIAT; end
|
8
7
|
|
9
8
|
require 'ciat/rake_task'
|
@@ -11,8 +10,7 @@ require 'ciat/traffic_light'
|
|
11
10
|
require 'ciat/suite'
|
12
11
|
require 'ciat/test'
|
13
12
|
require 'ciat/test_element'
|
14
|
-
require 'ciat/
|
15
|
-
require 'ciat/crate'
|
13
|
+
require 'ciat/test_file'
|
16
14
|
require 'ciat/feedback/standard_output'
|
17
15
|
require 'ciat/feedback/html_feedback'
|
18
16
|
require 'ciat/differs/html_differ'
|
@@ -1,14 +1,13 @@
|
|
1
1
|
module CIAT
|
2
2
|
module Differs
|
3
|
-
# Module for implementing the diff between two files.
|
4
|
-
#
|
5
|
-
#
|
3
|
+
# Module for implementing the diff between two files. The output is in
|
4
|
+
# HTML table rows with the expected output on the left and the generated
|
5
|
+
# on the right.
|
6
6
|
module HtmlDiffer
|
7
7
|
def html_diff(expected_file, generated_file, diff_file)
|
8
8
|
system("diff #{diff_options} '#{expected_file}' '#{generated_file}' > '#{diff_file}'")
|
9
9
|
end
|
10
10
|
|
11
|
-
# TODO: it would be nice to have line numbers in the output
|
12
11
|
def diff_options
|
13
12
|
"--old-group-format='<tr><td>%df</td><td class=\"red\"><pre>%<</pre></td><td></td><td></td></tr>' " +
|
14
13
|
"--new-group-format='<tr><td></td><td></td><td>%dF</td><td class=\"red\"><pre>%></pre><td></tr>' " +
|
data/lib/ciat/erb_helpers.rb
CHANGED
@@ -41,11 +41,11 @@ module CIAT::ERBHelpers
|
|
41
41
|
string.gsub("\t", " ")
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
if @
|
44
|
+
def new_grouping?(result)
|
45
|
+
if @old_grouping == result.grouping
|
46
46
|
false
|
47
47
|
else
|
48
|
-
@
|
48
|
+
@old_grouping = result.grouping
|
49
49
|
true
|
50
50
|
end
|
51
51
|
end
|
@@ -53,7 +53,9 @@ module CIAT::ERBHelpers
|
|
53
53
|
# Recursively renders another template. If it's possible to write your own
|
54
54
|
# templates for reports, this will probably play a key role.
|
55
55
|
def render(file, locals)
|
56
|
-
ERB.new(read(file))
|
56
|
+
erb = ERB.new(read(file))
|
57
|
+
erb.filename = file
|
58
|
+
erb.result(CIAT::TemplateBinder.new(locals).get_binding)
|
57
59
|
end
|
58
60
|
|
59
61
|
private
|
@@ -13,8 +13,8 @@ module CIAT::Feedback
|
|
13
13
|
@feedbacks.each { |feedback| feedback.post_tests(suite) }
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
@feedbacks.each { |feedback| feedback.
|
16
|
+
def report_subresult(subresult)
|
17
|
+
@feedbacks.each { |feedback| feedback.report_subresult(subresult) }
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -1,34 +1,51 @@
|
|
1
1
|
require 'ciat/erb_helpers'
|
2
|
+
require 'ciat/feedback/html_feedback_builder'
|
3
|
+
require 'ciat/io'
|
2
4
|
|
3
5
|
module CIAT::Feedback
|
4
6
|
class HtmlFeedback
|
5
7
|
include CIAT::ERBHelpers
|
8
|
+
include CIAT::IO
|
6
9
|
|
7
|
-
|
10
|
+
attr_reader :report_title
|
11
|
+
attr_reader :report_filename
|
12
|
+
|
13
|
+
def initialize(counter, options)
|
8
14
|
@counter = counter
|
15
|
+
builder = CIAT::Feedback::HtmlFeedbackBuilder.new(options)
|
16
|
+
@report_title = builder.build_report_title
|
17
|
+
@report_filename = builder.build_report_filename
|
9
18
|
end
|
10
19
|
|
11
20
|
def pre_tests(suite)
|
12
|
-
suite.
|
21
|
+
FileUtils.mkdir_p(suite.output_folder)
|
22
|
+
FileUtils.cp(File.join(File.dirname(__FILE__), "..", "..", "data", "ciat.css"), suite.output_folder)
|
23
|
+
FileUtils.cp(File.join(File.dirname(__FILE__), "..", "..", "data", "prototype.js"), suite.output_folder)
|
13
24
|
end
|
14
25
|
|
15
|
-
def
|
26
|
+
def report_subresult(processor)
|
16
27
|
nil
|
17
28
|
end
|
18
29
|
|
19
30
|
def post_tests(suite)
|
20
|
-
|
21
|
-
|
22
|
-
generate_html(suite)
|
31
|
+
write_file(
|
32
|
+
report_filename,
|
33
|
+
generate_html(suite)
|
34
|
+
)
|
23
35
|
end
|
24
36
|
|
25
37
|
def generate_html(suite) #:nodoc:
|
26
38
|
processors = suite.processors
|
27
39
|
results = suite.results
|
28
|
-
report_title = suite.report_title
|
29
40
|
size = suite.size
|
30
41
|
counter = @counter
|
31
|
-
|
42
|
+
build_erb.result(binding)
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_erb
|
46
|
+
e = ERB.new(template)
|
47
|
+
e.filename = "report.html.erb"
|
48
|
+
e
|
32
49
|
end
|
33
50
|
|
34
51
|
def template #:nodoc:
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'ciat/io'
|
2
|
+
|
3
|
+
class CIAT::Feedback::HtmlFeedbackBuilder
|
4
|
+
include CIAT::IO
|
5
|
+
|
6
|
+
attr_reader :options
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def build_output_folder
|
13
|
+
options[:output_folder] || OUTPUT_FOLDER
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_report_filename
|
17
|
+
File.join(build_output_folder, options[:report_filename] || REPORT_FILENAME)
|
18
|
+
end
|
19
|
+
|
20
|
+
def build_report_title
|
21
|
+
if options[:report_title]
|
22
|
+
"CIAT Report: " + options[:report_title]
|
23
|
+
else
|
24
|
+
"CIAT Report"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/lib/ciat/io.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module CIAT::IO
|
2
|
+
|
3
|
+
OUTPUT_FOLDER = "temp"
|
4
|
+
REPORT_FILENAME = "report.html"
|
5
|
+
|
6
|
+
def write_file(filename, content)
|
7
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
8
|
+
File.open(filename, "w") do |file|
|
9
|
+
file.write content
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_file(filename)
|
14
|
+
File.read(filename)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -1,30 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# Produces a clone for an individual test.
|
4
|
-
def for_test
|
5
|
-
copy = clone
|
6
|
-
copy.light = light.clone
|
7
|
-
copy
|
8
|
-
end
|
1
|
+
require 'rake'
|
2
|
+
require 'ciat/differs/html_differ'
|
9
3
|
|
4
|
+
module CIAT::Processors
|
5
|
+
module BasicProcessing
|
6
|
+
include CIAT::Differs::HtmlDiffer
|
7
|
+
|
10
8
|
# Executes the program, and diffs the output.
|
11
9
|
def process(test)
|
12
10
|
# TODO: verify required elements
|
13
11
|
# TODO: handle optional element
|
14
12
|
if execute(test)
|
15
13
|
if diff(test)
|
16
|
-
|
14
|
+
CIAT::TrafficLight::GREEN
|
17
15
|
else
|
18
|
-
|
16
|
+
CIAT::TrafficLight::RED
|
19
17
|
end
|
20
18
|
else
|
21
|
-
|
19
|
+
CIAT::TrafficLight::YELLOW
|
22
20
|
end
|
23
|
-
test
|
24
21
|
end
|
25
|
-
|
22
|
+
|
26
23
|
def execute(test)
|
27
|
-
|
24
|
+
RakeFileUtils.verbose(false) do
|
25
|
+
sh "#{executable} '#{input_file(test)}' #{command_line_args(test)} > '#{output_file(test)}' 2> '#{error_file(test)}'" do |ok, result|
|
26
|
+
return ok
|
27
|
+
end
|
28
|
+
end
|
28
29
|
end
|
29
30
|
|
30
31
|
def command_line_args(test) #:nodoc:
|
@@ -34,31 +35,21 @@ module CIAT::Processors
|
|
34
35
|
# Compares the expected and generated executions.
|
35
36
|
def diff(test)
|
36
37
|
html_diff(
|
37
|
-
test.element(
|
38
|
-
test.element(
|
39
|
-
test.element(
|
40
|
-
end
|
41
|
-
|
42
|
-
def relevant_elements(test)
|
43
|
-
relevant_element_names.
|
44
|
-
select { |name| test.element?(name) }.
|
45
|
-
map { |name| test.element(name) }
|
46
|
-
end
|
47
|
-
|
48
|
-
def relevant_element_names
|
49
|
-
processor_kind.element_name_hash[light.setting]
|
38
|
+
test.element(kind.output_name).as_file,
|
39
|
+
test.element(kind.output_name, :generated).as_file,
|
40
|
+
test.element(kind.output_name, :diff).as_file)
|
50
41
|
end
|
51
42
|
|
52
43
|
def input_file(test)
|
53
|
-
test.element(
|
44
|
+
test.element(kind.input_name).as_file
|
54
45
|
end
|
55
46
|
|
56
47
|
def output_file(test)
|
57
|
-
test.element(
|
48
|
+
test.element(kind.output_name, :generated).as_file
|
58
49
|
end
|
59
50
|
|
60
51
|
def error_file(test)
|
61
|
-
test.element(
|
52
|
+
test.element(kind.output_name, :error).as_file
|
62
53
|
end
|
63
54
|
end
|
64
55
|
end
|
@@ -1,26 +1,25 @@
|
|
1
1
|
module CIAT
|
2
|
-
module
|
2
|
+
module Processors
|
3
3
|
# Executor class for Java interpreters.
|
4
4
|
#
|
5
5
|
class Java
|
6
6
|
include CIAT::Processors::BasicProcessing
|
7
|
-
include CIAT::Differs::HtmlDiffer
|
8
7
|
|
9
8
|
# Traffic light
|
10
|
-
|
11
|
-
|
9
|
+
attr_accessor :kind
|
10
|
+
attr_accessor :description
|
12
11
|
|
13
12
|
# Creates a Java executor.
|
14
13
|
#
|
15
14
|
# Possible options:
|
16
15
|
# * <code>:description</code> is the description used in the HTML report
|
17
16
|
# for this processor (default: <code>"Parrot virtual machine"</code>).
|
18
|
-
def initialize(classpath, interpreter_class
|
19
|
-
@processor_kind = options[:processor_kind] || CIAT::Processors::Interpreter.new
|
17
|
+
def initialize(classpath, interpreter_class)
|
20
18
|
@classpath = classpath
|
21
19
|
@interpreter_class = interpreter_class
|
22
|
-
|
23
|
-
|
20
|
+
self.kind = CIAT::Processors::Interpreter.new
|
21
|
+
self.description = "in-Java interpreter"
|
22
|
+
yield self if block_given?
|
24
23
|
end
|
25
24
|
|
26
25
|
# Provides a description of the processor.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module CIAT
|
2
|
-
module
|
2
|
+
module Processors
|
3
3
|
# Executor class for Parrot programs. This will execute PIR or PASM code
|
4
4
|
# using the +parrot+ executable.
|
5
5
|
#
|
@@ -17,9 +17,9 @@ module CIAT
|
|
17
17
|
include CIAT::Processors::BasicProcessing
|
18
18
|
include CIAT::Differs::HtmlDiffer
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
attr_accessor :kind
|
21
|
+
attr_accessor :description
|
22
|
+
attr_accessor :libraries
|
23
23
|
|
24
24
|
# Creates a Parrot executor.
|
25
25
|
#
|
@@ -30,12 +30,11 @@ module CIAT
|
|
30
30
|
# report
|
31
31
|
# for the command-line arguments (if any) (default: "Command-line
|
32
32
|
# arguments").
|
33
|
-
def initialize(
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
@light = CIAT::TrafficLight.new
|
33
|
+
def initialize()
|
34
|
+
self.kind = CIAT::Processors::Interpreter.new
|
35
|
+
self.description = "Parrot virtual machine"
|
36
|
+
self.libraries = []
|
37
|
+
yield self if block_given?
|
39
38
|
end
|
40
39
|
|
41
40
|
# Provides a description of the processor.
|
data/lib/ciat/rake_task.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
class CIAT::Subresult
|
2
|
+
attr_reader :light
|
3
|
+
attr_reader :processor
|
4
|
+
|
5
|
+
def initialize(elements, light, processor)
|
6
|
+
@elements = elements
|
7
|
+
@light = light
|
8
|
+
@processor = processor
|
9
|
+
end
|
10
|
+
|
11
|
+
def relevant_elements
|
12
|
+
relevant_element_names.
|
13
|
+
select { |name| @elements.element?(name) }.
|
14
|
+
map { |name| @elements.element(name) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def relevant_element_names
|
18
|
+
@processor.kind.element_name_hash[@light.setting]
|
19
|
+
end
|
20
|
+
end
|
data/lib/ciat/suite.rb
CHANGED
@@ -2,6 +2,7 @@ require 'erb'
|
|
2
2
|
require 'ciat/feedback/composite'
|
3
3
|
require 'ciat/feedback/feedback_counter'
|
4
4
|
require 'ciat/feedback/return_status'
|
5
|
+
require 'ciat/suite_builder'
|
5
6
|
|
6
7
|
# = A Suite of Tests
|
7
8
|
#
|
@@ -46,67 +47,55 @@ require 'ciat/feedback/return_status'
|
|
46
47
|
#
|
47
48
|
# You can create your own processors. Each processor needs to specify which
|
48
49
|
# test elements it wants or will accept, which files it wants checked, and how
|
49
|
-
# it should be executed. See CIAT::
|
50
|
-
# CIAT::
|
50
|
+
# it should be executed. See CIAT::Processors::Java and
|
51
|
+
# CIAT::Processors::Parrot (to learn how to use them and how to write others).
|
51
52
|
#
|
52
53
|
# == Test File
|
53
54
|
#
|
54
55
|
# See the README for details on the format of a test file.
|
55
56
|
#
|
56
57
|
class CIAT::Suite
|
57
|
-
|
58
|
-
attr_reader :results
|
58
|
+
|
59
59
|
attr_reader :processors
|
60
|
-
attr_reader :
|
60
|
+
attr_reader :output_folder
|
61
|
+
attr_reader :test_files
|
62
|
+
attr_reader :results
|
61
63
|
|
62
|
-
#
|
64
|
+
# Builds a suite of CIAT tests. See the instructions above for possible
|
63
65
|
# values for the +options+.
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
@feedback = options[:feedback] || default_feedback
|
72
|
-
@feedback = CIAT::Feedback::Composite.new(
|
73
|
-
@feedback, CIAT::Feedback::ReturnStatus.new
|
74
|
-
)
|
66
|
+
def self.build(options = {})
|
67
|
+
builder = CIAT::SuiteBuilder.new(options)
|
68
|
+
CIAT::Suite.new(
|
69
|
+
builder.build_processors,
|
70
|
+
builder.build_output_folder,
|
71
|
+
builder.build_test_files,
|
72
|
+
builder.build_feedback)
|
75
73
|
end
|
76
74
|
|
75
|
+
def initialize(processors, output_folder, test_files, feedback)
|
76
|
+
@processors = processors
|
77
|
+
@output_folder = output_folder
|
78
|
+
@test_files = test_files
|
79
|
+
@feedback = feedback
|
80
|
+
end
|
81
|
+
|
77
82
|
# Returns the number of tests in the suite.
|
78
83
|
def size
|
79
|
-
|
84
|
+
test_files.size
|
80
85
|
end
|
81
86
|
|
82
87
|
# Runs all of the tests in the suite, and returns the results. The results
|
83
88
|
# are also available through #results.
|
84
89
|
def run
|
85
90
|
@feedback.pre_tests(self)
|
86
|
-
@results =
|
87
|
-
map { |
|
91
|
+
@results = test_files.
|
92
|
+
map { |test_file| create_test(test_file) }.
|
88
93
|
map { |test| test.run }
|
89
94
|
@feedback.post_tests(self)
|
90
95
|
@results
|
91
96
|
end
|
92
97
|
|
93
|
-
def create_test(
|
94
|
-
CIAT::Test.new(
|
95
|
-
crate.process_test_file,
|
96
|
-
:processors => test_processors,
|
97
|
-
:feedback => @feedback)
|
98
|
-
end
|
99
|
-
|
100
|
-
def test_processors #:nodoc:
|
101
|
-
@processors.map { |processor| processor.for_test }
|
102
|
-
end
|
103
|
-
|
104
|
-
private
|
105
|
-
def default_feedback
|
106
|
-
counter = CIAT::Feedback::FeedbackCounter.new
|
107
|
-
CIAT::Feedback::Composite.new(counter,
|
108
|
-
CIAT::Feedback::StandardOutput.new(counter),
|
109
|
-
CIAT::Feedback::HtmlFeedback.new(counter)
|
110
|
-
)
|
98
|
+
def create_test(test_file)
|
99
|
+
CIAT::Test.new(test_file, @processors, @feedback)
|
111
100
|
end
|
112
101
|
end
|