3 require File.dirname(__FILE__) + '/test'
4 require 'voodoo/parser'
6 # Class that provides a getc method that reads from a string.
24 # Like StringReader, but will throw an error after n characters.
25 class BrokenStringReader < StringReader
33 raise StandardError, "Error generated by BrokenStringReader"
39 # Runs a test on the parser.
41 # +name+ Name of the test
42 # +input+ Input to the test
44 # If given a block, returns the result of yielding to the block.
45 # The block receives two arguments:
46 # +result+ Result from the parser (nil if an error was raised)
47 # +error+ Error that was raised (nil if no error was raised)
48 def parser_test_ex name, input
54 if input.respond_to? :getc
57 reader = StringReader.new input
59 parser = Voodoo::Parser.new reader
60 while (x = parser.parse_top_level) != nil
71 # Runs a test on the parser.
73 # +name+ Name of the test
74 # +input+ Input to the test
75 # +expected+ Expected return value from the parser
76 # +exception+ Expected exception class, or nil if no exception expected
77 def parser_test name, input, expected, exception = nil
78 parser_test_ex(name, input) do |result, error|
80 # Not expecting exception.
86 fail_test("result did not match expected result",
87 "Got:\n#{result.inspect}\nExpected:\n#{expected.inspect}")
90 # Wasnt't expecting exception, but got one.
91 fail_test("unexpected exception of type #{error.class}",
92 "#{error.class}:#{error.message}\n" +
93 (error.kind_of?(Voodoo::Parser::Error) ?
94 "#{error.start_line}: #{error.text}\n" : '') +
95 error.backtrace.join("\n"))
98 # Expecting exception.
101 fail_test "expected exception of class #{exception.class}, " +
103 elsif error.class != exception.class
104 # Got exception, but of wrong type.
105 fail_test("expected exception of class #{exception.class}, " +
106 "but got one of type #{error.class}",
107 "#{error.class}:#{error.message}\n" +
108 (error.kind_of?(Voodoo::Parser::Error) ?
109 "#{error.start_line}: #{error.text}\n" : '') +
110 error.backtrace.join("\n"))
112 # Got exception of expected type.
119 # Runs a test on the parser and verifies that the parser
120 # throws an exception of the expected type, with the expected
121 # line and column information.
123 # +name+ Name of the test
124 # +input+ Input to the test
125 # +error_type+ Expected error type
126 # +line+ Expected line number
127 # +column+ Expected column number
128 # +text+ Expected text from the source code
129 def parser_test_error name, input, error_type, line, column, text
130 # Test I/O error on the first character.
131 parser_test_ex(name, input) do |result, error|
132 if error.class != error_type
133 fail_test("expecting exception of class #{error_type}, " +
134 "but got #{error.class}",
135 "#{error.class}:#{error.message}\n" +
136 error.backtrace.join("\n"))
137 elsif error.start_line != line || error.start_column != column
138 fail_test("wrong position. Expected line #{line}, column #{column}" +
139 "; got line #{error.start_line}, column #{error.start_column}")
140 elsif error.text != text
141 fail_test("wrong text.",
142 "===== ACTUAL =====\n#{error.text}\n" +
143 "===== EXPECTED =====\n#{text}\n")
144 $stderr.puts error.backtrace
152 parser_test "empty", "", []
154 parser_test "newline", "\n", []
156 parser_test "comment", "# only a comment", []
158 parser_test "comment_newline", "# only a comment\n", []
160 parser_test "multiline_comment", "# only a comment\n# multiline", []
162 parser_test "multiline_comment_newline",
163 "# only a comment\n# multiline\n", []
165 parser_test "section", "section functions", [[:section, :functions]]
167 parser_test "section_newline", "section functions\n",
168 [[:section, :functions]]
170 parser_test "section_comment", "section functions # functions section",
171 [[:section, :functions]]
173 parser_test "function_noargs", "function\nreturn 0\nend function\n",
174 [[:function, [], [:return, 0]]]
176 parser_test "function_onearg", "function x\nreturn x\nend function\n",
177 [[:function, [:x], [:return, :x]]]
179 parser_test "function_twoargs",
180 "function x y\nreturn add x y\nend function\n",
181 [[:function, [:x, :y], [:return, :add, :x, :y]]]
183 parser_test "continued", "call foo \\\n bar", [[:call, :foo, :bar]]
185 parser_test "continued_number", "call foo \\\n 42", [[:call, :foo, 42]]
187 parser_test_error("io_error_start",
188 BrokenStringReader.new("section data", 0),
189 Voodoo::Parser::ParserInternalError,
193 parser_test_error("io_error_first_char",
194 BrokenStringReader.new("section data", 1),
195 Voodoo::Parser::ParserInternalError,
199 parser_test_error("io_error_second_line",
200 BrokenStringReader.new("section data\nfoo:", 13),
201 Voodoo::Parser::ParserInternalError,
205 parser_test_ex("multiple_errors", "block\nlet\nset\n") do |result,error|
206 expected_message = <<EOT
209 2: let requires a symbol and an expression
213 3: set requires a symbol or at-expression followed by an expression
217 4: End of input while inside block
219 expected_text = <<EOT
226 if !error.kind_of? Voodoo::Parser::MultipleErrors
227 fail_test "Expected MultipleErrors, but got #{error.class}"
228 elsif error.errors.length != 3
229 fail_test "Expected 3 errors, but got #{error.errors.length}"
230 elsif error.errors[0].start_line != 2
231 fail_test "Expected first error on line 2" +
232 ", but got it on line #{error.errors[0].start_line}"
233 elsif error.errors[1].start_line != 3
234 fail_test "Expected second error on line 3" +
235 ", but got it on line #{error.errors[1].start_line}"
236 elsif error.errors[2].start_line != 4
237 fail_test "Expected third error on line 4" +
238 ", but got it on line #{error.errors[2].start_line}"
239 elsif error.message != expected_message
240 fail_test("Error message differs from expected",
241 "===== ACTUAL =====\n#{error.message}\n" +
242 "===== EXPECTED =====\n#{expected_message}\n")
243 elsif error.text != expected_text
244 fail_test("Error text differs from expected",
245 "===== ACTUAL =====\n#{error.text}\n" +
246 "===== EXPECTED =====\n#{expected_text}\n")
255 exit report_test_results