stones-spec 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 61404af586ca76a2bbe93f918c43e9e5f96d6e27
4
+ data.tar.gz: 6a5c5604f68519886817fd96e6c8d1ebe811d2ec
5
+ SHA512:
6
+ metadata.gz: 30bfcb15abdf1f02ae85ba42def8c1d74bf91d0c5924fbc3be60a383a458e1cae152743a7c7ed0d560115746eb52f5f3160bf58a441371169f06aefd1c064ee2
7
+ data.tar.gz: c2c67ed0a00ff8c5ebe54f227448efc184f3e2fb40d55c19d0eed7f3bb2ec7f449519a1d9e1a8a8bfe975631abb7dbc526beec8b228fe3827670bc79dfa83435
data/lib/example.rb ADDED
@@ -0,0 +1,43 @@
1
+ require 'ostruct'
2
+
3
+ module StonesSpec
4
+ class Example < OpenStruct
5
+ def initialize(subject, attributes)
6
+ super attributes
7
+ @title = attributes[:title]
8
+ @subject = subject
9
+ @precondition = Precondition.from_example(self)
10
+ end
11
+
12
+ def execution_data(source)
13
+ { source: @subject.test_program(source, @precondition.arguments),
14
+ initial_board: @precondition.initial_board_gbb }
15
+ end
16
+
17
+ def execute!(files, parser)
18
+ parser.run(files[:source], files[:initial_board], files[:final_board])
19
+ end
20
+
21
+ def result(files, execution, postcondition)
22
+ if execution[:status] == :syntax_error
23
+ raise Gobstones::SyntaxError, execution[:result]
24
+ end
25
+
26
+ if execution[:status] == :runtime_error
27
+ raise Gobstones::AbortedError, execution[:result]
28
+ end
29
+
30
+ postcondition.validate(files[:initial_board].open.read, files[:final_board].open.read, execution[:result], execution[:status])
31
+ end
32
+
33
+ def title
34
+ @title || default_title
35
+ end
36
+
37
+ private
38
+
39
+ def default_title
40
+ @subject.default_title @precondition.arguments
41
+ end
42
+ end
43
+ end
data/lib/gobstones.rb ADDED
@@ -0,0 +1,26 @@
1
+ module StonesSpec
2
+ module Gobstones
3
+ def self.source_code_extension
4
+ 'gbs'
5
+ end
6
+
7
+ def self.board_extension
8
+ 'gbb'
9
+ end
10
+
11
+ class Error < Exception
12
+ end
13
+
14
+ class SyntaxError < Error
15
+ def status
16
+ :errored
17
+ end
18
+ end
19
+
20
+ class AbortedError < Error
21
+ def status
22
+ :aborted
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ class Hash
2
+ def map_values(&block)
3
+ Hash[self.map {|k, v| [k, block.call(k,v)] }]
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ class String
2
+ def start_with_lowercase?
3
+ first_letter = self[0]
4
+ first_letter.downcase == first_letter
5
+ end
6
+
7
+ def include_any?(other_strs)
8
+ other_strs.any? { |other| include? other }
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ require 'tempfile'
2
+
3
+ module StonesSpec
4
+ module WithTempfile
5
+
6
+ def write_tempfile(content, extension)
7
+ file = Tempfile.new %W(gobstones. .#{extension})
8
+ file.write content
9
+ file.close
10
+ file
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,55 @@
1
+ module StonesSpec
2
+ module Postcondition
3
+ class ExpectedBoom
4
+ include StonesSpec::WithGbbHtmlRendering
5
+
6
+ attr_reader :example, :error_type
7
+
8
+ def initialize(example)
9
+ @example = example
10
+ @error_type = known_error_types[example.error.to_sym]
11
+ end
12
+
13
+ def validate(initial_board_gbb, actual_final_board_gbb, result, status)
14
+ if status == :failed
15
+ check_right_error_type initial_board_gbb, result
16
+ else
17
+ boards = [['Tablero inicial', initial_board_gbb], ['Tablero final', actual_final_board_gbb]]
18
+ make_boards_output example.title, boards, :failed, failure_message
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def check_right_error_type(initial_board_gbb, result)
25
+ if error_type_matches? result
26
+ [example.title, :passed, make_error_output(result, initial_board_gbb)]
27
+ else
28
+ [example.title, :failed, "#{invalid_boom_type_message}\n#{make_error_output(result, initial_board_gbb)}"]
29
+ end
30
+ end
31
+
32
+ def error_type_matches?(result)
33
+ error_type[:matcher] =~ result
34
+ end
35
+
36
+ def known_error_types
37
+ {
38
+ out_of_board: { matcher: /La posición cae afuera del tablero/, friendly_message: 'caer fuera del tablero' },
39
+ no_stones: { matcher: /No hay bolitas de ese color/, friendly_message: 'no haber bolitas' },
40
+ unassigned_variable: { matcher: /podría no tener asignado ningún valor/, friendly_message: 'tener una variable sin asignar' },
41
+ wrong_argument_type: { matcher: /El argumento de .+ debería ser/, friendly_message: 'tipo erróneo de un argumento' },
42
+ wrong_arguments_quantity: { matcher: /Esperaba \d+ argumentos (.|\n)* Recibió \d+/, friendly_message: 'cantidad inválida de argumentos' }
43
+ }
44
+ end
45
+
46
+ def failure_message
47
+ 'Se esperaba que el programa hiciera BOOM pero se obtuvo un tablero final.'
48
+ end
49
+
50
+ def invalid_boom_type_message
51
+ "<p>Se esperaba que el programa hiciera BOOM por #{error_type[:friendly_message]}.</p>"
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,56 @@
1
+ module StonesSpec
2
+ module Postcondition
3
+ class ExpectedFinalBoard < ExpectedResult
4
+ attr_reader :check_head_position, :show_initial_board
5
+
6
+ def initialize(example, check_head_position, show_initial_board)
7
+ super example
8
+ @check_head_position = check_head_position
9
+ @show_initial_board = show_initial_board
10
+ end
11
+
12
+ def validate_expected_result(initial_board_gbb, actual_final_board_gbb, _result)
13
+ if matches_with_expected_board? Stones::Gbb.read actual_final_board_gbb
14
+ passed_result initial_board_gbb, actual_final_board_gbb
15
+ else
16
+ failed_result initial_board_gbb, example.final_board, actual_final_board_gbb
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def failed_result(initial_board_gbb, expected_board_gbb, actual_board_gbb)
23
+ boards = [
24
+ ['Tablero final esperado', expected_board_gbb],
25
+ ['Tablero final obtenido', actual_board_gbb]
26
+ ]
27
+
28
+ boards.unshift ['Tablero inicial', initial_board_gbb] if show_initial_board
29
+
30
+ make_boards_output example.title, boards, :failed
31
+ end
32
+
33
+ def passed_result(initial_board_gbb, actual_board_gbb)
34
+ boards = [
35
+ ['Tablero final', actual_board_gbb]
36
+ ]
37
+
38
+ boards.unshift ['Tablero inicial', initial_board_gbb] if show_initial_board
39
+
40
+ make_boards_output example.title, boards, :passed
41
+ end
42
+
43
+ def matches_with_expected_board?(actual_board)
44
+ if check_head_position
45
+ actual_board == final_board
46
+ else
47
+ actual_board.cells_equal? final_board
48
+ end
49
+ end
50
+
51
+ def final_board
52
+ Stones::Gbb.read example.final_board
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,44 @@
1
+ module StonesSpec
2
+ module Postcondition
3
+ class ExpectedReturnValue < ExpectedResult
4
+ def initialize(example, show_initial_board)
5
+ super example
6
+ @show_initial_board = show_initial_board
7
+ end
8
+
9
+ def validate_expected_result(initial_board_gbb, _actual_final_board_gbb, result)
10
+ normalized_actual_return = parse_success_output(result).strip
11
+
12
+ if normalized_actual_return == return_value
13
+ make_result(:passed, initial_board_gbb)
14
+ else
15
+ make_result(:failed, initial_board_gbb, "Se esperaba <b>#{return_value}</b> pero se obtuvo <b>#{normalized_actual_return}</b>")
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def parse_success_output(result)
22
+ first_return_value result || ''
23
+ end
24
+
25
+ def first_return_value(result)
26
+ result[/#1 -> (.+)/, 1]
27
+ end
28
+
29
+ def make_result(status, initial_board_gbb, output='')
30
+ title = "#{example.title} -> #{return_value}"
31
+
32
+ if @show_initial_board
33
+ make_boards_output title, [['Tablero inicial', initial_board_gbb]], status, output
34
+ else
35
+ [title, status, output]
36
+ end
37
+ end
38
+
39
+ def return_value
40
+ example.return.to_s
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,35 @@
1
+ module StonesSpec
2
+ module Postcondition
3
+ def self.from(example, check_head_position, show_initial_board)
4
+ if example.final_board
5
+ ExpectedFinalBoard.new(example, check_head_position, show_initial_board)
6
+ elsif example.return
7
+ ExpectedReturnValue.new(example, show_initial_board)
8
+ else
9
+ ExpectedBoom.new(example)
10
+ end
11
+ end
12
+
13
+ class ExpectedResult
14
+ include StonesSpec::WithGbbHtmlRendering
15
+
16
+ attr_reader :example
17
+
18
+ def initialize(example)
19
+ @example = example
20
+ end
21
+
22
+ def validate(initial_board_gbb, actual_final_board_gbb, result, status)
23
+ if status == :failed
24
+ [example.title, :failed, make_error_output(result, initial_board_gbb)]
25
+ else
26
+ validate_expected_result(initial_board_gbb, actual_final_board_gbb, result)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ require_relative './expected_boom'
34
+ require_relative './expected_final_board'
35
+ require_relative './expected_return_value'
@@ -0,0 +1,26 @@
1
+ module StonesSpec
2
+ class Precondition
3
+ attr_reader :initial_board_gbb
4
+
5
+ def self.from_example(example)
6
+ self.new example.initial_board, example.arguments
7
+ end
8
+
9
+ def initialize(initial_board, arguments)
10
+ @initial_board_gbb = initial_board || default_initial_board
11
+ @arguments = arguments
12
+ end
13
+
14
+ def arguments
15
+ @arguments || []
16
+ end
17
+
18
+ private
19
+
20
+ def default_initial_board
21
+ 'GBB/1.0
22
+ size 4 4
23
+ head 0 0'
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,77 @@
1
+ require_relative './with_gobstones_css'
2
+
3
+ module StonesSpec
4
+ class HtmlBoardRenderer
5
+ include StonesSpec::WithGobstonesCSS
6
+
7
+ def initialize(options = {})
8
+ @options = options
9
+ end
10
+
11
+ def render(board)
12
+ "<style type=\"text/css\">
13
+ #{render_css}</style>
14
+
15
+ #{render_html board}"
16
+ end
17
+
18
+ def method_missing(name)
19
+ @options[name]
20
+ end
21
+
22
+ def render_html(board)
23
+ width, height = board.size
24
+
25
+ "#{table_title}
26
+ #{html_row_titles width, 'top'}
27
+ #{(0...height).to_a.reverse.map {|y| html_row(board, y)}.join}#{html_row_titles width, 'bottom'}
28
+ </table>
29
+ "
30
+ end
31
+
32
+ def render_css
33
+ gobstones_css '9pt', 30
34
+ end
35
+
36
+ private
37
+
38
+ def table_title
39
+ base = "<table class=\"gbs_board\">"
40
+ caption ? base + "\n<caption>#{caption}</caption>" : base
41
+ end
42
+
43
+ def html_row(board, y)
44
+ " <tr>
45
+ <td class=\"lv\">#{y}</td>
46
+ #{(0...board.size[0]).map {|x| html_cell board, [x, y] }.join "\n"}
47
+ <td class=\"lv\">#{y}</td>
48
+ </tr>
49
+ "
50
+ end
51
+
52
+ def html_cell(board, position)
53
+ cell = board.cell_at position
54
+
55
+ " <td class=\"gc#{board.head_position == position ? ' gh' : ''}\">
56
+ <table>
57
+ <tr>#{html_stone cell, :black}#{html_stone cell, :blue}</tr>
58
+ <tr>#{html_stone cell, :red}#{html_stone cell, :green}</tr>
59
+ </table>
60
+ </td>"
61
+ end
62
+
63
+ def html_stone(cell, color)
64
+ quantity = cell[color]
65
+
66
+ if cell[color] == 0
67
+ '<td><div class="O"></div></td>'
68
+ else
69
+ "<td><div class=\"gbs_stone #{Stones::Color.all_with_names.invert[color][0]}\"><span>#{quantity}</span></div></td>"
70
+ end
71
+ end
72
+
73
+ def html_row_titles(width, caption)
74
+ "<tr><td class=\"lx #{caption}_left\"></td>#{(0...width).map {|x| "<td class=\"lh\">#{x}</td>"}.join}<td class=\"lx #{caption}_right\"></td></tr>"
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,55 @@
1
+ module StonesSpec
2
+ module WithGbbHtmlRendering
3
+ def get_html_board(caption, gbb_representation)
4
+ HtmlBoardRenderer.new(caption: caption).render(Stones::GbbReader.new.from_string gbb_representation)
5
+ end
6
+
7
+ def make_error_output(result, initial_board_gbb)
8
+ "#{get_html_board 'Tablero inicial', initial_board_gbb}\n#{get_boom_board initial_board_gbb}\n#{result}"
9
+ end
10
+
11
+ def make_boards_output(title, gbb_boards, status, extra = nil)
12
+ boards = gbb_boards.map { |gbb_with_caption| get_html_board *gbb_with_caption }.join("\n")
13
+ output = "<div>#{boards}</div>"
14
+
15
+ output = "<p>#{extra}</p>\n#{output}" if extra
16
+
17
+ [title, status, output]
18
+ end
19
+
20
+ private
21
+
22
+ def get_boom_board(initial_board_gbb)
23
+ gbb = empty_board_gbb_like initial_board_gbb
24
+
25
+ boom_css =
26
+ "<style type=\"text/css\">
27
+ table.boom {
28
+ background-image: url('#{boom_image_url}');
29
+ background-size: contain;
30
+ background-repeat: no-repeat;
31
+ background-position: center;
32
+ }
33
+ </style>"
34
+
35
+ without_header with_boom_css_class "#{boom_css}\n#{get_html_board '¡Se produjo BOOM!', gbb}"
36
+ end
37
+
38
+ def boom_image_url
39
+ 'https://raw.githubusercontent.com/mumuki/mumuki-gobstones-server/master/lib/assets/boom.png'
40
+ end
41
+
42
+ def empty_board_gbb_like(initial_board_gbb)
43
+ x, y = Stones::Gbb.read(initial_board_gbb).size
44
+ Stones::Gbb.write Stones::Board.empty(x, y)
45
+ end
46
+
47
+ def with_boom_css_class(html)
48
+ html.sub('class="gbs_board"', 'class="gbs_board boom"')
49
+ end
50
+
51
+ def without_header(html)
52
+ html.sub('class="gc gh"', 'class="gc"')
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,112 @@
1
+ module StonesSpec
2
+ module WithGobstonesCSS
3
+ def gobstones_css(font_size, size)
4
+ unit = 'px'
5
+
6
+ full_size = "#{size}#{unit}"
7
+ half_size = "#{size / 2}#{unit}"
8
+
9
+ "table.gbs_board {
10
+ border-style: none;
11
+ border: solid black 0px;
12
+ border-spacing: 0;
13
+ border-collapse: collapse;
14
+ font-family: Arial, Helvetica, sans-serif;
15
+ font-size: #{font_size};
16
+ display: inline-block;
17
+ vertical-align: top;
18
+ }
19
+ .gbs_board td {
20
+ margin: 0;
21
+ padding: 2px;
22
+ border: solid #888 1px;
23
+ width: #{full_size};
24
+ height: #{full_size};
25
+ }
26
+ .gbs_board td.gh { /* position of the header in the board */
27
+ margin: 0;
28
+ padding: 2px;
29
+ border: dotted #440 3px;
30
+ background: #dd8;
31
+ width: #{full_size};
32
+ height: #{full_size};
33
+ }
34
+ .gbs_board td.lv { /* labels at the side */
35
+ text-align: center;
36
+ vertical-align: middle;
37
+ border-style: none;
38
+ border: solid black 0px;
39
+ background: #ddd;
40
+ width: #{half_size};
41
+ }
42
+ .gbs_board td.lh { /* labels at the top / bottom */
43
+ text-align: center;
44
+ vertical-align: middle;
45
+ border-style: none;
46
+ border: solid black 0px;
47
+ background: #ddd;
48
+ height: #{half_size};
49
+ }
50
+ .gbs_board td.lx { /* corner */
51
+ border-style: none;
52
+ border: solid black 0px;
53
+ background: #ddd;
54
+ width: #{half_size};
55
+ height: #{half_size};
56
+ }
57
+ .gbs_board td.top_left {
58
+ -webkit-border-top-left-radius: 10px;
59
+ -moz-border-top-left-radius: 10px;
60
+ border-top-left-radius: 10px;
61
+ }
62
+ .gbs_board td.top_right {
63
+ -webkit-border-top-right-radius: 10px;
64
+ -moz-border-top-right-radius: 10px;
65
+ border-top-right-radius: 10px;
66
+ }
67
+ .gbs_board td.bottom_left {
68
+ -webkit-border-bottom-left-radius: 10px;
69
+ -moz-border-bottom-left-radius: 10px;
70
+ border-bottom-left-radius: 10px;
71
+ }
72
+ .gbs_board td.bottom_right {
73
+ -webkit-border-bottom-right-radius: 10px;
74
+ -moz-border-bottom-right-radius: 10px;
75
+ border-bottom-right-radius: 10px;
76
+ }
77
+ .gbs_board table.gc { /* cell table */
78
+ border-style: none;
79
+ border: solid black 0px;
80
+ }
81
+ .gbs_board .gc tr {
82
+ border-style: none;
83
+ border: 0px;
84
+ }
85
+ .gbs_board .gc td {
86
+ border-style: none;
87
+ border: solid black 0px;
88
+ width: #{half_size};
89
+ height: #{half_size};
90
+ text-align: center;
91
+ color: black;
92
+ }
93
+ .gbs_board .gc td div {
94
+ line-height: 2;
95
+ }
96
+ .gbs_board div.A { background: #88f; border: solid 1px #008; }
97
+ .gbs_board div.N { background: #aaa; border: solid 1px #222; }
98
+ .gbs_board div.R { background: #f88; border: solid 1px #800; }
99
+ .gbs_board div.V { background: #8f8; border: solid 1px #080; }
100
+ .gbs_board div.O { width: 20px; height: 20px; background: none; } /* empty */
101
+ .gbs_stone {
102
+ font-weight: bold;
103
+ font-size: 8pt;
104
+ width: 20px;
105
+ height: 20px;
106
+ -webkit-border-radius: 10px;
107
+ -moz-border-radius: 10px;
108
+ border-radius: 10px;
109
+ }"
110
+ end
111
+ end
112
+ end
data/lib/runner.rb ADDED
@@ -0,0 +1,45 @@
1
+ module StonesSpec
2
+ class Runner
3
+ include StonesSpec::WithTempfile
4
+
5
+ def initialize(parser)
6
+ @parser = parser
7
+ end
8
+
9
+ def run!(test_definition)
10
+ subject = Subject.from(test_definition[:subject])
11
+ source = test_definition[:source]
12
+ check_head_position = test_definition[:check_head_position]
13
+ show_initial_board = test_definition.fetch(:show_initial_board, true)
14
+
15
+ begin
16
+ [test_definition[:examples].map do |example_definition|
17
+ run_example!(example_definition, check_head_position, show_initial_board, source, subject)
18
+ end]
19
+ rescue Gobstones::AbortedError => e
20
+ test_definition[:expect_endless_while] ? [e.message, :passed] : [e.message, e.status]
21
+ rescue Gobstones::Error => e
22
+ [e.message, e.status]
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def run_example!(example_definition, check_head_position, show_initial_board, source, subject)
29
+ example = Example.new(subject, example_definition)
30
+
31
+ data = example.execution_data source
32
+ files = {
33
+ source: Gobstones.source_code_extension,
34
+ initial_board: Gobstones.board_extension,
35
+ final_board: Gobstones.board_extension
36
+ }.map_values { |name, extension| write_tempfile(data[name], extension) }
37
+
38
+ execution = example.execute! files, @parser
39
+
40
+ example.result files, execution, Postcondition.from(example, check_head_position, show_initial_board)
41
+ ensure
42
+ files.each_value(&:unlink)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,21 @@
1
+ module StonesSpec
2
+ end
3
+
4
+ require 'stones'
5
+
6
+ require_relative './helpers/string'
7
+ require_relative './helpers/hash'
8
+ require_relative './helpers/with_tempfile'
9
+
10
+ require_relative './version'
11
+ require_relative './gobstones'
12
+
13
+ require_relative './renderers/html_board_renderer'
14
+ require_relative './renderers/with_gbb_html_rendering'
15
+
16
+ require_relative './precondition'
17
+ require_relative './postcondition/postcondition'
18
+
19
+ require_relative './example'
20
+ require_relative './subject'
21
+ require_relative './runner'
data/lib/subject.rb ADDED
@@ -0,0 +1,80 @@
1
+ module StonesSpec
2
+ module Subject
3
+ def self.from(name)
4
+ if name
5
+ infer_subject_type_for(name).new(name)
6
+ else
7
+ Program
8
+ end
9
+ end
10
+
11
+ def self.infer_subject_type_for(string)
12
+ string.start_with_lowercase? ? StonesSpec::Subject::Function : StonesSpec::Subject::Procedure
13
+ end
14
+
15
+ module Program
16
+ def self.test_program(source, _arguments)
17
+ source
18
+ end
19
+
20
+ def self.default_title(_arguments)
21
+ nil
22
+ end
23
+
24
+ def self.ast_regexp
25
+ /AST\(entrypoint\s*program/
26
+ end
27
+
28
+ def self.default_expectations
29
+ [{ 'binding' => 'program', 'inspection' => 'HasBinding' }]
30
+ end
31
+ end
32
+
33
+ class Callable
34
+ def initialize(name)
35
+ @name = name
36
+ end
37
+
38
+ def call_string(arguments)
39
+ "#{@name}(#{arguments.join(', ')})"
40
+ end
41
+
42
+ def default_title(arguments)
43
+ call_string arguments
44
+ end
45
+
46
+ def default_expectations
47
+ [ { 'binding' => 'program', 'inspection' => 'Not:HasBinding' },
48
+ { 'binding' => "#{@name}", 'inspection' => 'HasBinding' } ]
49
+ end
50
+ end
51
+
52
+ class Procedure < Callable
53
+ def test_program(source, arguments)
54
+ "program {
55
+ #{call_string arguments}
56
+ }
57
+
58
+ #{source}"
59
+ end
60
+
61
+ def ast_regexp
62
+ /AST\(procedure\s*#{@name}$/
63
+ end
64
+ end
65
+
66
+ class Function < Callable
67
+ def test_program(source, arguments)
68
+ "program {
69
+ return (#{call_string arguments})
70
+ }
71
+
72
+ #{source}"
73
+ end
74
+
75
+ def ast_regexp
76
+ /AST\(function\s*#{@name}/
77
+ end
78
+ end
79
+ end
80
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module StonesSpec
2
+ VERSION = '1.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stones-spec
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mumuki
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby-stones
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.4'
55
+ description:
56
+ email:
57
+ - mumuki@mumuki.org
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - lib/example.rb
63
+ - lib/gobstones.rb
64
+ - lib/helpers/hash.rb
65
+ - lib/helpers/string.rb
66
+ - lib/helpers/with_tempfile.rb
67
+ - lib/postcondition/expected_boom.rb
68
+ - lib/postcondition/expected_final_board.rb
69
+ - lib/postcondition/expected_return_value.rb
70
+ - lib/postcondition/postcondition.rb
71
+ - lib/precondition.rb
72
+ - lib/renderers/html_board_renderer.rb
73
+ - lib/renderers/with_gbb_html_rendering.rb
74
+ - lib/renderers/with_gobstones_css.rb
75
+ - lib/runner.rb
76
+ - lib/stones-spec.rb
77
+ - lib/subject.rb
78
+ - lib/version.rb
79
+ homepage: http://github.com/mumuki/stones-spec
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.5.1
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Minimal test framework for Gobstones
103
+ test_files: []