ciat 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/History.txt +96 -0
  2. data/README.rdoc +135 -0
  3. data/Rakefile +40 -0
  4. data/ciat.gemspec +53 -0
  5. data/lib/ciat.rb +22 -0
  6. data/lib/ciat/cargo.rb +55 -0
  7. data/lib/ciat/compilers/java.rb +54 -0
  8. data/lib/ciat/crate.rb +58 -0
  9. data/lib/ciat/differs/html_differ.rb +20 -0
  10. data/lib/ciat/erb_helpers.rb +83 -0
  11. data/lib/ciat/executors/java.rb +36 -0
  12. data/lib/ciat/executors/parrot.rb +51 -0
  13. data/lib/ciat/feedback/composite.rb +20 -0
  14. data/lib/ciat/feedback/feedback_counter.rb +45 -0
  15. data/lib/ciat/feedback/html_feedback.rb +40 -0
  16. data/lib/ciat/feedback/return_status.rb +23 -0
  17. data/lib/ciat/feedback/standard_output.rb +29 -0
  18. data/lib/ciat/processors/basic_processing.rb +64 -0
  19. data/lib/ciat/processors/compilation_interpreter.rb +18 -0
  20. data/lib/ciat/processors/compiler.rb +18 -0
  21. data/lib/ciat/processors/copy.rb +37 -0
  22. data/lib/ciat/processors/interpreter.rb +18 -0
  23. data/lib/ciat/rake_task.rb +56 -0
  24. data/lib/ciat/suite.rb +112 -0
  25. data/lib/ciat/test.rb +41 -0
  26. data/lib/ciat/test_element.rb +44 -0
  27. data/lib/ciat/traffic_light.rb +39 -0
  28. data/lib/ciat/version.rb +7 -0
  29. data/lib/data/ciat.css +40 -0
  30. data/lib/data/elements.yml +30 -0
  31. data/lib/data/prototype.js +4228 -0
  32. data/lib/templates/detail_row.html.erb +9 -0
  33. data/lib/templates/detail_row/elements.html.erb +3 -0
  34. data/lib/templates/elements/diff.html.erb +7 -0
  35. data/lib/templates/elements/plain.html.erb +8 -0
  36. data/lib/templates/group_header.html.erb +7 -0
  37. data/lib/templates/report.html.erb +38 -0
  38. data/lib/templates/summary_row.html.erb +12 -0
  39. data/lib/templates/test_numbers.html.erb +12 -0
  40. metadata +99 -0
data/lib/ciat/crate.rb ADDED
@@ -0,0 +1,58 @@
1
+ class CIAT::Crate #:nodoc:all
2
+ attr_reader :test_file
3
+ attr_reader :stub
4
+ attr_reader :output_folder
5
+
6
+ def initialize(test_file, output_folder)
7
+ @test_file = test_file
8
+ @output_folder = output_folder
9
+ end
10
+
11
+ def stub
12
+ test_file.gsub(File.extname(test_file), "")
13
+ end
14
+
15
+ def filename(*modifiers)
16
+ File.join(output_folder, [stub, *modifiers].compact.join("_"))
17
+ end
18
+
19
+ def read_test_file
20
+ File.readlines(test_file)
21
+ end
22
+
23
+ def process_test_file #:nodoc:
24
+ elements = empty_elements_hash
25
+ split_test_file.each do |name, contents|
26
+ elements[name] = CIAT::TestElement.new(name, filename(name), contents)
27
+ end
28
+ elements
29
+ end
30
+
31
+ def split_test_file #:nodoc:
32
+ tag = :description
33
+ content = ""
34
+ raw_elements = {}
35
+ read_test_file.each do |line|
36
+ if line =~ /^==== ((\w|\s)+?)\W*$/
37
+ raw_elements[tag] = content
38
+ tag = $1.gsub(" ", "_").to_sym
39
+ content = ""
40
+ else
41
+ content += line
42
+ end
43
+ end
44
+ raw_elements[tag] = content
45
+ raw_elements
46
+ end
47
+
48
+ #
49
+ # Helpers
50
+ #
51
+ private
52
+
53
+ def empty_elements_hash
54
+ Hash.new do |hash, name|
55
+ hash[name] = CIAT::TestElement.new(name, filename(name), nil)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,20 @@
1
+ module CIAT
2
+ module Differs
3
+ # Module for implementing the diff between two files.
4
+ # The output is in HTML table rows with the expected output on the left and
5
+ # the generated on the right.
6
+ module HtmlDiffer
7
+ def html_diff(expected_file, generated_file, diff_file)
8
+ system("diff #{diff_options} '#{expected_file}' '#{generated_file}' > '#{diff_file}'")
9
+ end
10
+
11
+ # TODO: it would be nice to have line numbers in the output
12
+ def diff_options
13
+ "--old-group-format='<tr><td>%df</td><td class=\"red\"><pre>%<</pre></td><td></td><td></td></tr>' " +
14
+ "--new-group-format='<tr><td></td><td></td><td>%dF</td><td class=\"red\"><pre>%></pre><td></tr>' " +
15
+ "--changed-group-format='<tr class=\"yellow\"><td>%df</td><td><pre>%<</pre></td><td>%dF</td><td><pre>%></pre></td></tr>' " +
16
+ "--unchanged-group-format='<tr class=\"green\"><td>%df</td><td><pre>%=</pre></td><td>%dF</td><td><pre>%=</pre></td></tr>'"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,83 @@
1
+ # If it were easy for you to write your own HTML templates, then the methods
2
+ # in this module would be useful.
3
+ #
4
+ # <em>I don't know if it's easy to write your own HTML template!</em>
5
+ module CIAT::ERBHelpers
6
+ # Turns a traffic light into a more "testing" word: "FAILURE",
7
+ # "ERROR", "passed", "n/a".
8
+ def light_to_word(light)
9
+ case
10
+ when light.red? then "FAILURE"
11
+ when light.yellow? then "ERROR"
12
+ when light.green? then "passed"
13
+ when light.unset? then "n/a"
14
+ else
15
+ raise "cannot turn #{light} into word"
16
+ end
17
+ end
18
+
19
+ # Turns a traffic light in a sentence wrapped in a classed +span+.
20
+ def light_to_sentence(prefix, light)
21
+ "<span class=\"#{light.setting}\">#{prefix} " +
22
+ case
23
+ when light.red? then "failed"
24
+ when light.yellow? then "errored"
25
+ when light.green? then "passed"
26
+ when light.unset? then "not run"
27
+ else
28
+ raise "cannot turn #{light} into a sentence"
29
+ end +
30
+ ".</span>"
31
+ end
32
+
33
+ # Capitalizes string as a title.
34
+ def title(text)
35
+ text.gsub(/\b\w/){$&.upcase}
36
+ end
37
+
38
+ # Replaces tabs with spaces because Firefox does _really_ wacky things with
39
+ # tabs in a +pre+ in a +table+.
40
+ def replace_tabs(string)
41
+ string.gsub("\t", " ")
42
+ end
43
+
44
+ def new_path?(result)
45
+ if @old_path == File.dirname(result.filename)
46
+ false
47
+ else
48
+ @old_path = File.dirname(result.filename)
49
+ true
50
+ end
51
+ end
52
+
53
+ # Recursively renders another template. If it's possible to write your own
54
+ # templates for reports, this will probably play a key role.
55
+ def render(file, locals)
56
+ ERB.new(read(file)).result(CIAT::TemplateBinder.new(locals).get_binding)
57
+ end
58
+
59
+ private
60
+
61
+ def read(file)
62
+ File.read(template_path(file))
63
+ end
64
+
65
+ def template_path(file)
66
+ File.join(File.dirname(__FILE__), '..', 'templates', file + ".html.erb")
67
+ end
68
+ end
69
+
70
+ # Assists in binding local variables for a recursive render.
71
+ class CIAT::TemplateBinder
72
+ include CIAT::ERBHelpers
73
+
74
+ def initialize(locals)
75
+ locals.each do |variable, value|
76
+ self.class.send(:define_method, variable, Proc.new {value})
77
+ end
78
+ end
79
+
80
+ def get_binding
81
+ binding
82
+ end
83
+ end
@@ -0,0 +1,36 @@
1
+ module CIAT
2
+ module Executors
3
+ # Executor class for Java interpreters.
4
+ #
5
+ class Java
6
+ include CIAT::Processors::BasicProcessing
7
+ include CIAT::Differs::HtmlDiffer
8
+
9
+ # Traffic light
10
+ attr :light, true
11
+ attr_reader :processor_kind
12
+
13
+ # Creates a Java executor.
14
+ #
15
+ # Possible options:
16
+ # * <code>:description</code> is the description used in the HTML report
17
+ # for this processor (default: <code>"Parrot virtual machine"</code>).
18
+ def initialize(classpath, interpreter_class, options={})
19
+ @processor_kind = options[:processor_kind] || CIAT::Processors::Interpreter.new
20
+ @classpath = classpath
21
+ @interpreter_class = interpreter_class
22
+ @description = options[:description] || "in-Java interpreter"
23
+ @light = CIAT::TrafficLight.new
24
+ end
25
+
26
+ # Provides a description of the processor.
27
+ def describe
28
+ @description
29
+ end
30
+
31
+ def executable
32
+ "java -cp '#{@classpath}' #{@interpreter_class}"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,51 @@
1
+ module CIAT
2
+ module Executors
3
+ # Executor class for Parrot programs. This will execute PIR or PASM code
4
+ # using the +parrot+ executable.
5
+ #
6
+ # The Parrot executor expects <code>compilation</code> and
7
+ # <code>execution</code> elements. The <code>compilation</code> element
8
+ # is used as input (and it actually pulls in the generated version, but it
9
+ # should be the same as the expected). <code>execution</code> is
10
+ # compared.
11
+ #
12
+ # The Parrot executor allows for a <code>command line</code> element. This
13
+ # specifies the command-line arguments when the <code>compilation</code>
14
+ # is executed.
15
+ # If none is provided, no command-line arguments are used.
16
+ class Parrot
17
+ include CIAT::Processors::BasicProcessing
18
+ include CIAT::Differs::HtmlDiffer
19
+
20
+ # Traffic light
21
+ attr :light, true
22
+ attr_reader :processor_kind
23
+
24
+ # Creates a Parrot executor.
25
+ #
26
+ # Possible options:
27
+ # * <code>:description</code> is the description used in the HTML report
28
+ # for this processor (default: <code>"Parrot virtual machine"</code>).
29
+ # * <code>:command_line</code> is the description used in the HTML
30
+ # report
31
+ # for the command-line arguments (if any) (default: "Command-line
32
+ # arguments").
33
+ def initialize(options={})
34
+ @processor_kind = options[:processor_kind] ||
35
+ CIAT::Processors::Interpreter.new
36
+ @description = options[:description] || "Parrot virtual machine"
37
+ @libraries = options[:libraries] || []
38
+ @light = CIAT::TrafficLight.new
39
+ end
40
+
41
+ # Provides a description of the processor.
42
+ def describe
43
+ @description
44
+ end
45
+
46
+ def executable
47
+ (["parrot"] + @libraries.map { |l| "-L" + l }).join(" ")
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,20 @@
1
+ # A feedback mechanism for compositing other feedbacks.
2
+ module CIAT::Feedback
3
+ class Composite
4
+ # Pass in instances of the feedbacks as arguments to the constructor.
5
+ def initialize(*feedbacks)
6
+ @feedbacks = feedbacks
7
+ end
8
+
9
+ def pre_tests(suite)
10
+ @feedbacks.each { |feedback| feedback.pre_tests(suite) }
11
+ end
12
+ def post_tests(suite)
13
+ @feedbacks.each { |feedback| feedback.post_tests(suite) }
14
+ end
15
+
16
+ def processor_result(processor)
17
+ @feedbacks.each { |feedback| feedback.processor_result(processor) }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,45 @@
1
+ module CIAT
2
+ module Feedback
3
+ # This feedback class sends some simple messages to the screen about the
4
+ # progress of a CIAT::Suite run.
5
+ class FeedbackCounter
6
+ def initialize
7
+ @error_count = 0
8
+ @failure_count = 0
9
+ end
10
+
11
+ def error_count
12
+ @error_count
13
+ end
14
+
15
+ def failure_count
16
+ @failure_count
17
+ end
18
+
19
+ def increment_error_count
20
+ @error_count += 1
21
+ end
22
+
23
+ def increment_failure_count
24
+ @failure_count += 1
25
+ end
26
+
27
+ def pre_tests(suite)
28
+ nil
29
+ end
30
+
31
+ def post_tests(suite)
32
+ nil
33
+ end
34
+
35
+ def processor_result(processor)
36
+ case processor.light.setting
37
+ when :red
38
+ increment_failure_count
39
+ when :yellow
40
+ increment_error_count
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,40 @@
1
+ require 'ciat/erb_helpers'
2
+
3
+ module CIAT::Feedback
4
+ class HtmlFeedback
5
+ include CIAT::ERBHelpers
6
+
7
+ def initialize(counter)
8
+ @counter = counter
9
+ end
10
+
11
+ def pre_tests(suite)
12
+ suite.cargo.copy_suite_data
13
+ end
14
+
15
+ def processor_result(processor)
16
+ nil
17
+ end
18
+
19
+ def post_tests(suite)
20
+ suite.cargo.write_file(
21
+ suite.cargo.report_filename,
22
+ generate_html(suite))
23
+ end
24
+
25
+ def generate_html(suite) #:nodoc:
26
+ processors = suite.processors
27
+ results = suite.results
28
+ report_title = suite.report_title
29
+ size = suite.size
30
+ counter = @counter
31
+ ERB.new(template).result(binding)
32
+ end
33
+
34
+ def template #:nodoc:
35
+ File.read(File.join(
36
+ File.dirname(__FILE__), "..", "..", "templates", "report.html.erb"
37
+ ))
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ # A feedback mechanism for failing a task
2
+ module CIAT::Feedback
3
+ class ReturnStatus
4
+ def pre_tests(suite)
5
+ @failure = false
6
+ end
7
+
8
+ def post_tests(suite)
9
+ if failure?
10
+ fail "CIAT tests unsuccessful"
11
+ end
12
+ end
13
+
14
+ def processor_result(processor)
15
+ @failure ||= processor.light.yellow? || processor.light.red?
16
+ end
17
+
18
+ private
19
+ def failure?
20
+ @failure
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ module CIAT
2
+ module Feedback
3
+ # This feedback class sends some simple messages to the screen about the
4
+ # progress of a CIAT::Suite run.
5
+ class StandardOutput
6
+ LIGHT_OUTPUTS = { :green => ".", :red => "F", :yellow => "E", :unset => "-" }
7
+
8
+ def initialize(counter)
9
+ @counter = counter
10
+ end
11
+
12
+ def pre_tests(suite)
13
+ nil
14
+ end
15
+
16
+ def post_tests(suite)
17
+ print "\n"
18
+ print "#{suite.size} tests executed"
19
+ print ", #{@counter.failure_count} failures" if @counter.failure_count > 0
20
+ print ", #{@counter.error_count} errors" if @counter.error_count > 0
21
+ print ".\n"
22
+ end
23
+
24
+ def processor_result(processor)
25
+ putc LIGHT_OUTPUTS[processor.light.setting]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,64 @@
1
+ module CIAT::Processors
2
+ module BasicProcessing
3
+ # Produces a clone for an individual test.
4
+ def for_test
5
+ copy = clone
6
+ copy.light = light.clone
7
+ copy
8
+ end
9
+
10
+ # Executes the program, and diffs the output.
11
+ def process(test)
12
+ # TODO: verify required elements
13
+ # TODO: handle optional element
14
+ if execute(test)
15
+ if diff(test)
16
+ light.green!
17
+ else
18
+ light.red!
19
+ end
20
+ else
21
+ light.yellow!
22
+ end
23
+ test
24
+ end
25
+
26
+ def execute(test)
27
+ system "#{executable} '#{input_file(test)}' #{command_line_args(test)} > '#{output_file(test)}' 2> '#{error_file(test)}'"
28
+ end
29
+
30
+ def command_line_args(test) #:nodoc:
31
+ test.element?(:command_line) ? test.element(:command_line).content.strip : ''
32
+ end
33
+
34
+ # Compares the expected and generated executions.
35
+ def diff(test)
36
+ html_diff(
37
+ test.element(processor_kind.output_name).as_file,
38
+ test.element(processor_kind.output_name, :generated).as_file,
39
+ test.element(processor_kind.output_name, :diff).as_file)
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]
50
+ end
51
+
52
+ def input_file(test)
53
+ test.element(processor_kind.input_name).as_file
54
+ end
55
+
56
+ def output_file(test)
57
+ test.element(processor_kind.output_name, :generated).as_file
58
+ end
59
+
60
+ def error_file(test)
61
+ test.element(processor_kind.output_name, :error).as_file
62
+ end
63
+ end
64
+ end