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.
- checksums.yaml +5 -13
- data/lib/assets_server.rb +37 -0
- data/lib/checker.rb +101 -0
- data/lib/expectations_hook.rb +8 -96
- data/lib/extensions/string.rb +17 -0
- data/lib/gobstones.rb +11 -0
- data/lib/gobstones/batch.rb +38 -0
- data/lib/gobstones/batch_parser.rb +68 -0
- data/lib/gobstones/error_builder.rb +6 -0
- data/lib/gobstones/example_code_builder.rb +35 -0
- data/lib/gobstones_runner.rb +17 -4
- data/lib/locales/en.yml +13 -0
- data/lib/locales/es.yml +13 -0
- data/lib/metadata_hook.rb +29 -7
- data/lib/multiple_executions_runner.rb +28 -0
- data/lib/precompile_hook.rb +30 -0
- data/lib/render/boards.html.erb +58 -0
- data/lib/render/editor/editor.css +12 -0
- data/lib/render/editor/editor.js +9 -0
- data/lib/render/html_board.rb +12 -0
- data/lib/render/html_renderer.rb +95 -0
- data/lib/render/with_renderer.rb +22 -0
- data/lib/test_hook.rb +8 -21
- metadata +66 -55
- data/lib/assets/boom.png +0 -0
- data/lib/stones_spec.rb +0 -21
- data/lib/stones_spec/error_message_parser.rb +0 -17
- data/lib/stones_spec/example.rb +0 -47
- data/lib/stones_spec/gobstones.rb +0 -55
- data/lib/stones_spec/hash.rb +0 -5
- data/lib/stones_spec/html_board_renderer.rb +0 -77
- data/lib/stones_spec/postcondition/expected_boom.rb +0 -55
- data/lib/stones_spec/postcondition/expected_final_board.rb +0 -56
- data/lib/stones_spec/postcondition/expected_return_value.rb +0 -44
- data/lib/stones_spec/postcondition/postcondition.rb +0 -35
- data/lib/stones_spec/precondition.rb +0 -26
- data/lib/stones_spec/runner.rb +0 -41
- data/lib/stones_spec/string.rb +0 -10
- data/lib/stones_spec/subject.rb +0 -80
- data/lib/stones_spec/version.rb +0 -3
- data/lib/stones_spec/with_command_line.rb +0 -7
- data/lib/stones_spec/with_gbb_html_rendering.rb +0 -60
- data/lib/stones_spec/with_gobstones_css.rb +0 -112
- data/lib/stones_spec/with_tempfile.rb +0 -13
- data/lib/with_test_parser.rb +0 -8
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
NjcyYTc4MWQ5MmFjM2UwOWVkOGJkNDk2YWE2ZjcxZjhhNzVkOTJhNQ==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 27939843f1079b96e0fe0dcd5ac9b7b8d2a8a63f
|
4
|
+
data.tar.gz: 2716a7461ea02e23df6d9186c009e1415ea85c25
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
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
|
data/lib/expectations_hook.rb
CHANGED
@@ -1,102 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'yaml'
|
1
|
+
class GobstonesExpectationsHook < Mumukit::Templates::MulangExpectationsHook
|
2
|
+
include_smells true
|
4
3
|
|
5
|
-
|
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
|
-
|
83
|
-
|
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
|
-
|
100
|
-
|
11
|
+
ast = output.first[:result][:mulangAst]
|
12
|
+
Mulang::Code.new(mulang_language, ast)
|
101
13
|
end
|
102
14
|
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,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
|