mumuki-gobstones-runner 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +5 -13
  2. data/lib/assets_server.rb +37 -0
  3. data/lib/checker.rb +101 -0
  4. data/lib/expectations_hook.rb +8 -96
  5. data/lib/extensions/string.rb +17 -0
  6. data/lib/gobstones.rb +11 -0
  7. data/lib/gobstones/batch.rb +38 -0
  8. data/lib/gobstones/batch_parser.rb +68 -0
  9. data/lib/gobstones/error_builder.rb +6 -0
  10. data/lib/gobstones/example_code_builder.rb +35 -0
  11. data/lib/gobstones_runner.rb +17 -4
  12. data/lib/locales/en.yml +13 -0
  13. data/lib/locales/es.yml +13 -0
  14. data/lib/metadata_hook.rb +29 -7
  15. data/lib/multiple_executions_runner.rb +28 -0
  16. data/lib/precompile_hook.rb +30 -0
  17. data/lib/render/boards.html.erb +58 -0
  18. data/lib/render/editor/editor.css +12 -0
  19. data/lib/render/editor/editor.js +9 -0
  20. data/lib/render/html_board.rb +12 -0
  21. data/lib/render/html_renderer.rb +95 -0
  22. data/lib/render/with_renderer.rb +22 -0
  23. data/lib/test_hook.rb +8 -21
  24. metadata +66 -55
  25. data/lib/assets/boom.png +0 -0
  26. data/lib/stones_spec.rb +0 -21
  27. data/lib/stones_spec/error_message_parser.rb +0 -17
  28. data/lib/stones_spec/example.rb +0 -47
  29. data/lib/stones_spec/gobstones.rb +0 -55
  30. data/lib/stones_spec/hash.rb +0 -5
  31. data/lib/stones_spec/html_board_renderer.rb +0 -77
  32. data/lib/stones_spec/postcondition/expected_boom.rb +0 -55
  33. data/lib/stones_spec/postcondition/expected_final_board.rb +0 -56
  34. data/lib/stones_spec/postcondition/expected_return_value.rb +0 -44
  35. data/lib/stones_spec/postcondition/postcondition.rb +0 -35
  36. data/lib/stones_spec/precondition.rb +0 -26
  37. data/lib/stones_spec/runner.rb +0 -41
  38. data/lib/stones_spec/string.rb +0 -10
  39. data/lib/stones_spec/subject.rb +0 -80
  40. data/lib/stones_spec/version.rb +0 -3
  41. data/lib/stones_spec/with_command_line.rb +0 -7
  42. data/lib/stones_spec/with_gbb_html_rendering.rb +0 -60
  43. data/lib/stones_spec/with_gobstones_css.rb +0 -112
  44. data/lib/stones_spec/with_tempfile.rb +0 -13
  45. data/lib/with_test_parser.rb +0 -8
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NWJiYTc4NTUwOTRkNjgyZGE0OTQ5NTg0MTcyYjczMTJjMDVmZGM2OQ==
5
- data.tar.gz: !binary |-
6
- NjcyYTc4MWQ5MmFjM2UwOWVkOGJkNDk2YWE2ZjcxZjhhNzVkOTJhNQ==
2
+ SHA1:
3
+ metadata.gz: 27939843f1079b96e0fe0dcd5ac9b7b8d2a8a63f
4
+ data.tar.gz: 2716a7461ea02e23df6d9186c009e1415ea85c25
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NWJmYjRkZjg3YWVlNGEzOWQ1YTNiOTNjMGZjODQ5NWI1OThiMTg4YjhlZDdm
10
- YzIyOTZhNGRjODE5NjY3YTk4MjRlYzU3Y2JhOTRiZTFhZTMzMTU3YTFmODI0
11
- OWU0ZjQwNWMwMWIyZTAzZmRkNmMyZGJlMDEzMTE3ZjdlOTg2Mjg=
12
- data.tar.gz: !binary |-
13
- MmUxZmJiY2ZlYTg2MmM3YmFhZTA3NDA5MDc1YmRhYTIxOGIyYjI2Zjg5YWMy
14
- ODlkNzk5MWQzNzYwNTY2MGI1NTkxNzE1ZDg2MTdiYzQyZjMwNzY4ZmFlZTZm
15
- NWI0ZWQ1M2JlNDJmODVmOTEyMzEyMTE0ZWJjZDI4YTczNmNkZTI=
6
+ metadata.gz: 875d7f8a03261bad7ee99e45e672644ea4498fd43467ee6f182060526cf218fb242ef830dae5d77bdb1054f2eceab801fc672fb657dbcefdd4d28dd3d954c939
7
+ data.tar.gz: 833e0840163ed6dfe96a12446833457aacac8d5d9bfc4e294da45293b15bd65b5e8506c77a4af81591c958d4ab051449dc9b96424c3c923730ff014bb2999ebf
@@ -0,0 +1,37 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/cross_origin'
3
+
4
+ class Mumukit::Server::App < Sinatra::Base
5
+ register Sinatra::CrossOrigin
6
+
7
+ configure do
8
+ enable :cross_origin
9
+ end
10
+
11
+ def self.get_asset(route, absolute_path, type)
12
+ get "/assets/#{route}" do
13
+ send_file absolute_path, { type: type }
14
+ end
15
+ end
16
+
17
+ def self.get_board_asset(route, path, type)
18
+ get_asset route, Gobstones::Board.assets_path_for(path), type
19
+ end
20
+
21
+ def self.get_editor_asset(route, path, type)
22
+ get_asset route, Gobstones::Blockly.assets_path_for(path), type
23
+ end
24
+
25
+ def self.get_local_asset(route, path, type)
26
+ get_asset route, File.expand_path(path), type
27
+ end
28
+
29
+ get_board_asset 'webcomponents.js', 'javascripts/vendor/webcomponents.min.js', 'application/javascript'
30
+ get_local_asset 'editor/editor.js', 'lib/render/editor/editor.js', 'application/javascript'
31
+ get_board_asset 'polymer.html', 'htmls/vendor/polymer.html', 'text/html'
32
+ get_board_asset 'polymer-mini.html', 'htmls/vendor/polymer-mini.html', 'text/html'
33
+ get_board_asset 'polymer-micro.html', 'htmls/vendor/polymer-micro.html', 'text/html'
34
+ get_board_asset 'gs-board.html', 'htmls/gs-board.html', 'text/html'
35
+ get_editor_asset 'editor/editor.html', 'htmls/gs-element-blockly.html', 'text/html'
36
+ get_local_asset 'editor/editor.css', 'lib/render/editor/editor.css', 'text/css'
37
+ end
data/lib/checker.rb ADDED
@@ -0,0 +1,101 @@
1
+ module Gobstones
2
+ class Checker < Mumukit::Metatest::Checker
3
+ include Gobstones::WithRenderer
4
+
5
+ def initialize(options)
6
+ @options = options
7
+ end
8
+
9
+ def check_final_board(output, expected)
10
+ status = output[:status]
11
+ result = output[:result]
12
+
13
+ is_expected_timeout = result[:finalBoardError] &&
14
+ result[:finalBoardError][:reason][:code] == 'timeout' &&
15
+ @options[:expect_endless_while]
16
+
17
+ return if is_expected_timeout
18
+ assert_not_boom status, result
19
+
20
+ expected_board = result[:extraBoard]
21
+ actual_board = result[:finalBoard]
22
+ boards_match = board_json(expected_board).eql? board_json(actual_board)
23
+ headers_match = expected_board[:head].eql?(actual_board[:head]) || !@options[:check_head_position]
24
+
25
+ fail_with status: :check_final_board_failed_different_boards,
26
+ result: {
27
+ initial: result[:initialBoard],
28
+ expected: expected_board,
29
+ actual: actual_board
30
+ } unless boards_match && headers_match
31
+ end
32
+
33
+ def check_error(output, expected)
34
+ status = output[:status]
35
+ result = output[:result]
36
+
37
+ fail_with status: :check_error_failed_expected_boom,
38
+ result: {
39
+ initial: result[:initialBoard],
40
+ expected: :boom,
41
+ final: result[:finalBoard]
42
+ } if status == :passed
43
+
44
+ reason_code = convert_known_reason_code result[:finalBoardError][:reason][:code]
45
+ fail_with status: :check_error_failed_another_reason,
46
+ result: {
47
+ reason: result[:finalBoardError],
48
+ expected_code: expected
49
+ } if reason_code != expected
50
+ end
51
+
52
+ def check_return(output, expected)
53
+ status = output[:status]
54
+ result = output[:result]
55
+
56
+ assert_not_boom status, result
57
+ return_value = result[:finalBoard][:returnValue]
58
+
59
+ fail_with status: :check_return_failed_no_return,
60
+ result: {
61
+ initial: result[:initialBoard],
62
+ expected_value: expected
63
+ } if return_value.nil?
64
+
65
+ final_value = return_value[:value]
66
+ fail_with status: :check_return_failed_different_values,
67
+ result: {
68
+ initial: result[:initialBoard],
69
+ expected_value: expected,
70
+ actual_value: final_value
71
+ } if final_value != expected
72
+ end
73
+
74
+ private
75
+
76
+ def assert_not_boom(status, result)
77
+ fail_with status: :check_failed_unexpected_boom,
78
+ result: {
79
+ initial: result[:initialBoard],
80
+ expected: result[:extraBoard],
81
+ actual: :boom,
82
+ reason: result[:finalBoardError]
83
+ } if status == :runtime_error
84
+ end
85
+
86
+ def fail_with(error)
87
+ fail error.to_json
88
+ end
89
+
90
+ def board_json(board)
91
+ board[:table][:json]
92
+ end
93
+
94
+ def convert_known_reason_code(code)
95
+ return "no_stones" if code == "cannot-remove-stone"
96
+ return "out_of_board" if code == "cannot-move-to"
97
+
98
+ code
99
+ end
100
+ end
101
+ end
@@ -1,102 +1,14 @@
1
- require 'mumukit'
2
- require 'mumukit/inspection'
3
- require 'yaml'
1
+ class GobstonesExpectationsHook < Mumukit::Templates::MulangExpectationsHook
2
+ include_smells true
4
3
 
5
- require_relative 'with_test_parser'
6
-
7
- module EvalExpectationsOnAST
8
- def eval_in_gobstones(binding, ast)
9
- pattern_generator = expectations[type]
10
-
11
- if pattern_generator
12
- !!(ast =~ pattern_generator[binding])
13
- else
14
- true
15
- end
16
- end
17
-
18
- def use(regexp)
19
- lambda { |_| regexp }
20
- end
21
-
22
- def subject_for(binding)
23
- if binding == 'program'
24
- StonesSpec::Subject::Program
25
- else
26
- StonesSpec::Subject.from(binding)
27
- end
28
- end
29
-
30
- def check_repeat_of(target)
31
- use(/AST\(repeat\s*#{target}/)
32
- end
33
- end
34
-
35
- class Mumukit::Inspection::PlainInspection
36
- include EvalExpectationsOnAST
37
-
38
- def expectations
39
- {
40
- 'HasBinding' => lambda { |binding| subject_for(binding).ast_regexp },
41
- 'HasForeach' => use(/AST\(foreach/),
42
- 'HasRepeat' => check_repeat_of('.+'),
43
- 'HasVariable' => use(/AST\(assignVarName/),
44
- 'HasWhile' => use(/AST\(while/)
45
- }
46
- end
47
- end
48
-
49
- class Mumukit::Inspection::TargetedInspection
50
- include EvalExpectationsOnAST
51
-
52
- def expectations
53
- {
54
- 'HasArity' => lambda { |binding| /#{subject_for(binding).ast_regexp}\s*AST\((\s*\w+){#{target}}\)/ },
55
- 'HasRepeatOf' => check_repeat_of("AST\\(literal\\s*#{target}\\)"),
56
- 'HasUsage' => use(/AST\((proc|func)Call\s*#{target}$/)
57
- }
4
+ def language
5
+ 'Mulang'
58
6
  end
59
- end
60
-
61
- class Mumukit::Inspection::NegatedInspection
62
- def eval_in_gobstones(binding, ast)
63
- !@inspection.eval_in_gobstones(binding, ast)
64
- end
65
- end
66
-
67
- class GobstonesExpectationsHook < Mumukit::Defaults::ExpectationsHook
68
- include StonesSpec::WithTempfile
69
- include WithTestParser
70
-
71
- def run!(request)
72
- content = request[:content]
73
- expectations = request[:expectations]
74
-
75
- if content.strip.empty?
76
- return expectations.map { |exp| {'expectation' => exp, 'result' => false } }
77
- end
78
-
79
- ast = generate_ast! content
80
- all_expectations = expectations + (default_expectations_for parse_test request)
81
7
 
82
- all_expectations.map { |exp| {'expectation' => exp, 'result' => run_expectation!(exp, ast)} }
83
- end
84
-
85
- private
86
-
87
- def default_expectations_for(test)
88
- StonesSpec::Subject.from(test[:subject]).default_expectations
89
- end
90
-
91
- def run_expectation!(expectation, ast)
92
- Mumukit::Inspection.parse(expectation['inspection']).eval_in_gobstones(expectation['binding'], ast)
93
- end
94
-
95
- def generate_ast!(source_code)
96
- %x"#{gobstones_command} #{write_tempfile(source_code, 'gbs').path} --print-ast --target parse --no-print-retvals --silent"
97
- end
8
+ def mulang_code(request)
9
+ output, status = request.result
98
10
 
99
- def gobstones_command
100
- @config['gobstones_command']
11
+ ast = output.first[:result][:mulangAst]
12
+ Mulang::Code.new(mulang_language, ast)
101
13
  end
102
14
  end
@@ -0,0 +1,17 @@
1
+ class String
2
+ def parse_as_json
3
+ JSON.parse(self, symbolize_names: true)
4
+ end
5
+
6
+ def initial_is_lower?
7
+ initial.lower?
8
+ end
9
+
10
+ def initial
11
+ slice 0, 1
12
+ end
13
+
14
+ def lower?
15
+ self == downcase
16
+ end
17
+ end
data/lib/gobstones.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'gobstones/board'
2
+ require 'gobstones/blockly'
3
+
4
+ module Gobstones
5
+
6
+ end
7
+
8
+ require_relative './gobstones/batch'
9
+ require_relative './gobstones/batch_parser'
10
+ require_relative './gobstones/error_builder'
11
+ require_relative './gobstones/example_code_builder'
@@ -0,0 +1,38 @@
1
+ class Gobstones::Batch
2
+ attr_accessor :options, :examples, :content, :extra
3
+
4
+ def initialize(content, examples, extra, options)
5
+ @content = content
6
+ @examples = examples
7
+ @extra = extra
8
+ @options = options
9
+ end
10
+
11
+ def run_tests!(output)
12
+ Mumukit::Metatest::Framework.new(
13
+ checker: Gobstones::Checker.new(options),
14
+ runner: Gobstones::MultipleExecutionsRunner.new).test output, examples
15
+ end
16
+
17
+ def to_json
18
+ examples.map { |example| example_json(example) }.to_json
19
+ end
20
+
21
+ private
22
+
23
+ def example_json(example)
24
+ expected_board = example[:postconditions][:final_board]
25
+ base = example_base_json(example)
26
+ expected_board ? base.merge(extraBoard: expected_board) : base
27
+ end
28
+
29
+ def example_base_json(example)
30
+ {initialBoard: example[:preconditions][:initial_board],
31
+ originalCode: content,
32
+ code: "#{example_code(example)}\n#{extra}"}
33
+ end
34
+
35
+ def example_code(example)
36
+ Gobstones::ExampleCodeBuilder.new(content, example, options).build
37
+ end
38
+ end
@@ -0,0 +1,68 @@
1
+ module Gobstones::BatchParser
2
+ class << self
3
+ def parse(request)
4
+ test = parse_test(request)
5
+
6
+ options = parse_options test
7
+ examples = parse_examples test, options
8
+
9
+ Gobstones::Batch.new request.content, examples, request.extra, options
10
+ end
11
+
12
+ private
13
+
14
+ def parse_test(request)
15
+ YAML.load(request.test).deep_symbolize_keys
16
+ end
17
+
18
+ def parse_examples(test, options)
19
+ examples = test[:examples]
20
+ examples.each_with_index.map do |example, index|
21
+ parse_example options, {
22
+ id: index,
23
+ name: example[:title],
24
+ preconditions: example.slice(*preconditions),
25
+ postconditions: example.slice(*postconditions)
26
+ }
27
+ end
28
+ end
29
+
30
+ def parse_example(options, example)
31
+ return example unless options[:subject]
32
+
33
+ return_value = example[:postconditions][:return]
34
+ if return_value
35
+ example[:name] = "#{options[:subject]}() -> #{return_value}" unless example[:name]
36
+ options[:show_final_board] = false
37
+ end
38
+
39
+ example
40
+ end
41
+
42
+ def parse_options(test)
43
+ [
44
+ struct(key: :show_initial_board, default: true),
45
+ struct(key: :show_final_board, default: true),
46
+ struct(key: :check_head_position, default: false),
47
+ struct(key: :expect_endless_while, default: false),
48
+ struct(key: :subject, default: nil)
49
+ ].map { |it| [
50
+ it.key,
51
+ if test[it.key].nil?
52
+ it.default
53
+ else
54
+ test[it.key]
55
+ end
56
+ ]
57
+ }.to_h
58
+ end
59
+
60
+ def preconditions
61
+ [:initial_board, :arguments]
62
+ end
63
+
64
+ def postconditions
65
+ [:final_board, :error, :return]
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,6 @@
1
+ module Gobstones
2
+ def self.build_error(error)
3
+ position = error[:on][:range][:start]
4
+ "[#{position[:row]}:#{position[:column]}]: #{error[:message]}"
5
+ end
6
+ end
@@ -0,0 +1,35 @@
1
+ class Gobstones::ExampleCodeBuilder
2
+ def initialize(code, example, options)
3
+ @code = code
4
+ @example = example
5
+ @options = options
6
+ end
7
+
8
+ def build
9
+ return @code unless subject
10
+ <<GBS
11
+ #{@code}
12
+ program {
13
+ #{code_call}
14
+ }
15
+ GBS
16
+ end
17
+
18
+ private
19
+
20
+ def code_call
21
+ subject.initial_is_lower? ? "return (#{invocation})" : invocation
22
+ end
23
+
24
+ def subject
25
+ @options[:subject]
26
+ end
27
+
28
+ def invocation
29
+ "#{subject}(#{arguments})"
30
+ end
31
+
32
+ def arguments
33
+ (@example[:preconditions][:arguments] || []).join ','
34
+ end
35
+ end