diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/error.lua | 264 |
1 files changed, 224 insertions, 40 deletions
diff --git a/tests/error.lua b/tests/error.lua index 91ccebc..126161e 100644 --- a/tests/error.lua +++ b/tests/error.lua | |||
@@ -1,58 +1,242 @@ | |||
1 | -- | 1 | -- |
2 | -- Error reporting | 2 | -- Error reporting |
3 | -- | 3 | -- |
4 | -- Note: this code is supposed to end in errors; not included in 'make test' | 4 | local which_tests, remaining_tests = {}, {} |
5 | -- | 5 | for k,v in ipairs{...} do |
6 | print("got arg:", type(v), tostring(v)) | ||
7 | which_tests[v] = true | ||
8 | remaining_tests[v] = true | ||
9 | end | ||
10 | |||
11 | --################################################################################################## | ||
12 | |||
13 | local lanes = require "lanes".configure{with_timers = false} | ||
14 | local null = lanes.null | ||
15 | |||
16 | --################################################################################################## | ||
17 | |||
18 | -- Lua51 support | ||
19 | local table_unpack = unpack or table.unpack | ||
6 | 20 | ||
7 | local lanes = require "lanes".configure{ with_timers = false} | 21 | --################################################################################################## |
8 | 22 | ||
9 | local function lane( mode_) | 23 | local WR = function(...) |
10 | set_error_reporting( mode_) | 24 | local str="" |
11 | local subf= function() -- this so that we can see the call stack | 25 | for i=1,select('#',...) do |
12 | error "aa" | 26 | local v = select(i,...) |
13 | --error({}) | 27 | if type(v) == "function" then |
14 | --error(error) | 28 | local infos = debug.getinfo(v) |
29 | --[[for k,v in pairs(infos) do | ||
30 | print(k,v) | ||
31 | end]] | ||
32 | v = infos.source..":"..infos.linedefined | ||
33 | end | ||
34 | str= str..tostring(v).."\t" | ||
15 | end | 35 | end |
16 | local subf2= function() | 36 | if io then |
17 | subf() | 37 | io.stderr:write(str.."\n") |
18 | end | 38 | end |
19 | subf2() | ||
20 | end | 39 | end |
21 | 40 | ||
22 | local function cleanup(err) | 41 | --################################################################################################## |
42 | |||
43 | local lane_body = function(error_value_, finalizer_, finalizer_error_value_) | ||
44 | WR( "In Lane body: EV: ", error_value_, " F: ", finalizer_, " FEV: ", finalizer_error_value_) | ||
45 | if finalizer_ then | ||
46 | local finalizer = function(err_, stack_) | ||
47 | finalizer_(err_, stack_) | ||
48 | if finalizer_error_value_ then | ||
49 | WR ("Finalizer raises ", finalizer_error_value_) | ||
50 | error(finalizer_error_value_, 0) -- 0 so that error() doesn't alter the error value | ||
51 | end | ||
52 | end | ||
53 | set_finalizer(finalizer) | ||
54 | end | ||
55 | |||
56 | local subf = function() -- this so that we can see the call stack | ||
57 | if error_value_ then | ||
58 | error(error_value_, 0) -- 0 so that error() doesn't alter the error value | ||
59 | end | ||
60 | return "success" | ||
61 | end | ||
62 | local subf2 = function(b_) | ||
63 | return b_ or subf() -- prevent tail call | ||
64 | end | ||
65 | local subf3 = function(b_) | ||
66 | return b_ or subf2() -- prevent tail call | ||
67 | end | ||
68 | return subf3(false) | ||
23 | end | 69 | end |
24 | 70 | ||
25 | local lgen = lanes.gen("*", { --[[finalizer=cleanup]] }, lane) | 71 | --################################################################################################## |
26 | 72 | ||
27 | --- | 73 | local lane_finalizer = function(err_, stack_) |
28 | io.stderr:write( "\n** Error catching **\n" ) | 74 | WR("In finalizer: ", err_, stack_) |
29 | -- | 75 | end |
30 | 76 | ||
31 | local error_reporting_mode = "extended" | 77 | --################################################################################################## |
32 | local h= lgen( error_reporting_mode) | 78 | |
33 | local _,err,stack= h:join() -- wait for the lane (no automatic error propagation) | 79 | local start_lane = function(error_reporting_mode_, error_value_, finalizer_, finalizer_error_value_) |
80 | return lanes.gen("*", {error_trace_level = error_reporting_mode_}, lane_body)(error_value_, finalizer_, finalizer_error_value_) | ||
81 | end | ||
34 | 82 | ||
35 | if err then | 83 | --################################################################################################## |
36 | assert( type(stack)=="table" ) -- only true if lanes was compiled with ERROR_FULL_STACK == 1 | 84 | --################################################################################################## |
37 | io.stderr:write( "Lane error: "..tostring(err).."\n" ) | ||
38 | 85 | ||
39 | if error_reporting_mode == "basic" then -- each stack line is a string in basic mode | 86 | local make_table_error_mt = function() |
40 | io.stderr:write( "\t", table.concat(stack,"\n\t"), "\n" ); | 87 | return { |
41 | else -- each stack line is a table in extended mode | 88 | __tostring = function() return "{error as table}" end, |
42 | for line, details in pairs( stack) do | 89 | __eq = function(t1_, t2_) |
43 | io.stderr:write( "\t", tostring( line), "\n" ); | 90 | -- because tables transfered through linda/error reporting are identical, but not equal |
44 | for detail, value in pairs( details) do | 91 | -- however, due to metatable caching by Lanes, their metatables should be the same |
45 | io.stderr:write( "\t\t", tostring( detail), ":", tostring( value), "\n") | 92 | return getmetatable(t1_) == getmetatable(t2_) |
46 | end | 93 | end |
47 | end | 94 | } |
48 | end | ||
49 | end | 95 | end |
50 | 96 | ||
51 | --- | 97 | local lane_error_as_string = "'lane error as string'" |
52 | io.stderr:write( "\n** Error propagation **\n" ) | 98 | local lane_error_as_table = setmetatable({}, make_table_error_mt()) |
53 | -- | 99 | local lane_error_as_linda = lanes.linda("'lane error'") |
54 | local h2= lgen( error_reporting_mode) | 100 | |
55 | local _= h2[0] | 101 | local finalizer_error_as_string = "'lane error as string'" |
56 | assert(false) -- does NOT get here | 102 | local finalizer_error_as_table = setmetatable({}, make_table_error_mt()) |
103 | local finalizer_error_as_linda = lanes.linda("'lane error'") | ||
104 | |||
105 | local test_settings = {} | ||
106 | local configure_tests = function() | ||
107 | local append_test = function(level, lane_error, finalizer, finalizer_error) | ||
108 | -- convert back null to nil | ||
109 | lane_error = lane_error ~= null and lane_error or nil | ||
110 | finalizer = finalizer ~= null and finalizer or nil | ||
111 | finalizer_error = finalizer_error ~= null and finalizer_error or nil | ||
112 | -- compose test description string | ||
113 | local test_header = table.concat({ | ||
114 | level .. " error reporting", | ||
115 | (lane_error and tostring(lane_error) or "no error") .. " in lane", | ||
116 | (finalizer and "with" or "without").. " finalizer" .. ((finalizer and finalizer_error) and " raising " .. tostring(finalizer_error) or "") | ||
117 | }, ", ") | ||
118 | print(test_header) | ||
119 | test_settings[test_header] = { level, lane_error, finalizer, finalizer_error } | ||
120 | end | ||
121 | -- can't store nil in tables, use null instead | ||
122 | local levels = { | ||
123 | "minimal", | ||
124 | "basic", | ||
125 | "extended", | ||
126 | } | ||
127 | local errors = { | ||
128 | null, | ||
129 | lane_error_as_string, | ||
130 | lane_error_as_table, | ||
131 | lane_error_as_linda, | ||
132 | } | ||
133 | local finalizers = { | ||
134 | null, | ||
135 | lane_finalizer, | ||
136 | } | ||
137 | local finalizer_errors = { | ||
138 | null, | ||
139 | finalizer_error_as_string, | ||
140 | finalizer_error_as_table, | ||
141 | finalizer_error_as_linda, | ||
142 | } | ||
143 | |||
144 | for _, level in ipairs(levels) do | ||
145 | for _, lane_error in ipairs(errors) do | ||
146 | for _, finalizer in ipairs(finalizers) do | ||
147 | for _, finalizer_error in ipairs(finalizer_errors) do | ||
148 | append_test(level, lane_error, finalizer, finalizer_error) | ||
149 | end | ||
150 | end | ||
151 | end | ||
152 | end | ||
153 | end | ||
154 | |||
155 | configure_tests() | ||
156 | -- do return end | ||
157 | |||
158 | --################################################################################################## | ||
159 | --################################################################################################## | ||
160 | |||
161 | local do_error_catching_test = function(error_reporting_mode_, error_value_, finalizer_, finalizer_error_value_) | ||
162 | local h = start_lane(error_reporting_mode_, error_value_, finalizer_, finalizer_error_value_) | ||
163 | local ret,err,stack= h:join() -- wait for the lane (no automatic error propagation) | ||
164 | WR("Processing results for {", error_reporting_mode_, error_value_, finalizer_, finalizer_error_value_, "}") | ||
165 | if err then | ||
166 | assert(ret == nil) | ||
167 | assert(error_reporting_mode_ == "minimal" or type(stack)=="table") -- only true if lane was configured with error_trace_level ~= "minimal" | ||
168 | if err == error_value_ then | ||
169 | WR("Lane regular error: ", err) | ||
170 | elseif err == finalizer_error_value_ then | ||
171 | WR("Lane finalizer error: ", err) | ||
172 | else | ||
173 | WR("Unknown error: ", type(err), err) | ||
174 | assert(false) | ||
175 | end | ||
176 | if error_reporting_mode_ == "minimal" then | ||
177 | assert(stack == nil, table.concat{"stack is a ", type(stack)}) | ||
178 | elseif error_reporting_mode_ == "basic" then -- each stack line is a string in basic mode | ||
179 | WR("STACK:\n", table.concat(stack,"\n\t")); | ||
180 | else -- each stack line is a table in extended mode | ||
181 | WR "STACK:" | ||
182 | for line, details in pairs(stack) do | ||
183 | WR("\t", line); | ||
184 | for detail, value in pairs(details) do | ||
185 | WR("\t\t", detail, ": ", value) | ||
186 | end | ||
187 | end | ||
188 | end | ||
189 | else -- no error | ||
190 | assert(ret == "success") | ||
191 | WR("No error in lane: ", ret) | ||
192 | end | ||
193 | WR "TEST OK" | ||
194 | end | ||
195 | |||
196 | --################################################################################################## | ||
197 | |||
198 | local do_error_propagation_test = function(error_reporting_mode_, error_value_, finalizer_, finalizer_error_value_) | ||
199 | local wrapper = function() | ||
200 | local raises_error = (error_value_ or finalizer_error_value_) and true or false | ||
201 | local h = start_lane(error_reporting_mode_, error_value_, finalizer_, finalizer_error_value_) | ||
202 | local _ = h[0] | ||
203 | -- if the lane is configured to raise an error, we should not get here | ||
204 | assert(not raises_error, "should not get here") | ||
205 | end | ||
206 | local err, msg = pcall(wrapper) | ||
207 | WR("Processing results for {", error_reporting_mode_, error_value_, finalizer_, finalizer_error_value_, "}") | ||
208 | if err then | ||
209 | WR("Lane error: ", msg) | ||
210 | end | ||
211 | WR "TEST OK" | ||
212 | end | ||
213 | |||
214 | --################################################################################################## | ||
215 | |||
216 | local perform_test = function(title_, test_) | ||
217 | WR "###########################################################################" | ||
218 | WR ("** " .. title_ .. " **") | ||
219 | for desc, settings in pairs(test_settings) do | ||
220 | WR "---------------------------------------------------------------------------" | ||
221 | WR(desc) | ||
222 | test_(table_unpack(settings)) | ||
223 | end | ||
224 | end | ||
225 | |||
226 | if not next(which_tests) or which_tests.catch then | ||
227 | remaining_tests.catch = nil | ||
228 | perform_test("Error catching", do_error_catching_test) | ||
229 | end | ||
230 | |||
231 | --################################################################################################## | ||
232 | if not next(which_tests) or which_tests.propagate then | ||
233 | remaining_tests.propagate = nil | ||
234 | perform_test("Error propagation", do_error_propagation_test) | ||
235 | end | ||
236 | |||
237 | -- ################################################################################################## | ||
238 | |||
239 | local unknown_test, val = next(remaining_tests) | ||
240 | assert(not unknown_test, tostring(unknown_test) .. " test is unknown") | ||
57 | 241 | ||
58 | --never ends | 242 | print "\nTHE END" |