Restructured tests so that tests for validator and language version
[voodoo-lang.git] / test / test.rb
blobef7660553cb7be17f295575c3fb4b81257612cfc
1 #### Common functionality for running tests.
3 # Make sure that the lib directory is in $LOAD_PATH
4 $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
6 require 'thread'
8 $errors = 0
9 $errors_mutex = Mutex.new
10 $tests = Queue.new
12 # Atomically increment $errors
13 def increment_errors
14   $errors_mutex.synchronize { $errors = $errors + 1 }
15 end
17 # Runs block and verifies that it returns the given value.
18 # If it doesn't, prints an error message and increments $errors.
19 def expect name, value, &block
20   begin
21     print "#{name}..."
22     result = yield
23     if result == value
24       puts "pass"
25       true
26     else
27       puts "FAIL: expected #{value.inspect}, but got #{result.inspect}"
28       increment_errors
29     end
30   rescue Exception => e
31     puts "FAIL: Unexpected #{e.class}\n#{e.message}"
32     increment_errors
33   end
34 end
36 # Runs block and verifies that it returns true.
37 # If it doesn't, prints an error message and increments $errors.
38 def expect_true name, &block
39   expect name, true, &block
40 end
42 # Runs block and verifies that it throws an exception of type
43 # +exception_type+. If the block doesn't throw such an exception,
44 # prints an error message and increments $errors.
45 def expect_exception name, exception_type, &block
46   begin
47     message = "#{name}..."
48     yield
49     message << "FAIL: Expected exception of type #{exception_type}," +
50       " but no exception was raised"
51     increment_errors
52   rescue Exception => e
53     if e.kind_of? exception_type
54       message << "pass"
55       true
56     else
57       message << "FAIL: Expected #{exception_type}, but got #{e.class}"
58       increment_errors
59     end
60   end
61   puts message
62 end
64 $RUBY = ENV['RUBY'] || 'ruby'
65 $VOODOOC = 'env RUBYLIB=../lib ../bin/voodooc'
67 # Runs a command with exec.
68 # With no block given, returns
69 # [status, stdin, stdout, stderr]
70 # With block given, passes
71 # status, stdin, stdout, stderr to block
72 def popen4 *command
73   pw = IO::pipe   # pipe[0] for read, pipe[1] for write
74   pr = IO::pipe
75   pe = IO::pipe
76   
77   pid = fork do
78     # child
79     pw[1].close
80     STDIN.reopen(pw[0])
81     pw[0].close
82     
83     pr[0].close
84     STDOUT.reopen(pr[1])
85     pr[1].close
86     
87     pe[0].close
88     STDERR.reopen(pe[1])
89     pe[1].close
90     
91     exec *command
92   end
94   # parent
95   pw[0].close
96   pr[1].close
97   pe[1].close
98   dummy, status = Process.wait2 pid
99   result = [status, pw[1], pr[0], pe[0]]
100   pw[1].sync = true
101   if block_given?
102     begin
103       return yield(*result)
104     ensure
105       [pw[1], pr[0], pe[0]].each { |p| p.close unless p.closed? }
106     end
107   end
108   result
111 def add_test program, command, expected_output = '', params = {}
112   $tests << [program, command, expected_output, params]
115 def run_test program, command, expected_output = '', params = {}
116   input = params.has_key?(:input) ? params[:input] : nil
117   expected_status = params.has_key?(:expected_status) ?
118     params[:expected_status] : 0
119   expected_errors = params.has_key?(:expected_errors) ?
120     params[:expected_errors] : ''
122   message = "#{program}..."
123   status, stdin, stdout, stderr = popen4 command
124   if input
125     stdin.write input
126   end
127   exitstatus = status.exitstatus
128   output = stdout.read
129   err_output = stderr.read
130   if exitstatus != expected_status
131     message << "FAIL: exit status is #{exitstatus}, expected #{expected_status}"
132     increment_errors
133   elsif output != expected_output
134     message << "FAIL: wrong output"
135     increment_errors
136   elsif err_output != expected_errors
137     message << "FAIL: wrong error output"
138     $stderr.puts "--- ERRORS ---\n#{err_output}\n--- EXPECTED ---\n#{expected_errors}\n--- END ERRORS ---"
139     increment_errors
140   else
141     message << 'pass'
142   end
143   puts message
146 def add_test2 program, expected_output
147   add_test program, "./#{program}", expected_output
150 def add_test1 program
151   add_test2 program, `cat #{program}.out`
154 # Reports tests results.
155 # Returns 0 if all tests passed, 1 if some failed.
156 def report_test_results
157   if $errors == 0
158     puts "All tests passed"
159     return 0
160   else
161     puts "#{$errors} tests failed"
162     return 1
163   end
166 # Runs all tests in the given queue.
167 # Each test is described by a list
168 # [name, command, expected_output, params].
169 # Those parameters are passed to run_test.
170 def run_tests queue, nthreads = 1
171   threads = []
172   nthreads.times do
173     threads << Thread.new do
174       until queue.empty?
175         name, command, output, params = queue.pop
176         run_test name, command, output, params
177       end
178     end
179   end
180   threads.each { |t| t.join }