| ... | @@ -34,7 +34,7 @@ pub const TestContext = struct { | ... | @@ -34,7 +34,7 @@ pub const TestContext = struct { |
| 34 | /// effects of the incremental compilation. | 34 | /// effects of the incremental compilation. |
| 35 | src: [:0]const u8, | 35 | src: [:0]const u8, |
| 36 | case: union(enum) { | 36 | case: union(enum) { |
| 37 | /// A transformation update transforms the input ZIR and tests against | 37 | /// A transformation update transforms the input and tests against |
| 38 | /// the expected output ZIR. | 38 | /// the expected output ZIR. |
| 39 | Transformation: [:0]const u8, | 39 | Transformation: [:0]const u8, |
| 40 | /// An error update attempts to compile bad code, and ensures that it | 40 | /// An error update attempts to compile bad code, and ensures that it |
| ... | @@ -43,6 +43,7 @@ pub const TestContext = struct { | ... | @@ -43,6 +43,7 @@ pub const TestContext = struct { |
| 43 | Error: []const ErrorMsg, | 43 | Error: []const ErrorMsg, |
| 44 | /// An execution update compiles and runs the input, testing the | 44 | /// An execution update compiles and runs the input, testing the |
| 45 | /// stdout against the expected results | 45 | /// stdout against the expected results |
| | 46 | /// This is a slice containing the expected message. |
| 46 | Execution: []const u8, | 47 | Execution: []const u8, |
| 47 | }, | 48 | }, |
| 48 | }; | 49 | }; |
| ... | @@ -56,16 +57,20 @@ pub const TestContext = struct { | ... | @@ -56,16 +57,20 @@ pub const TestContext = struct { |
| 56 | /// update, so each update's source is treated as a single file being | 57 | /// update, so each update's source is treated as a single file being |
| 57 | /// updated by the test harness and incrementally compiled. | 58 | /// updated by the test harness and incrementally compiled. |
| 58 | pub const Case = struct { | 59 | pub const Case = struct { |
| | 60 | /// The name of the test case. This is shown if a test fails, and |
| | 61 | /// otherwise ignored. |
| 59 | name: []const u8, | 62 | name: []const u8, |
| 60 | /// The platform the test targets. For non-native platforms, an emulator | 63 | /// The platform the test targets. For non-native platforms, an emulator |
| 61 | /// such as QEMU is required for tests to complete. | 64 | /// such as QEMU is required for tests to complete. |
| 62 | target: std.zig.CrossTarget, | 65 | target: std.zig.CrossTarget, |
| | 66 | /// In order to be able to run e.g. Execution updates, this must be set |
| | 67 | /// to Executable. |
| 63 | output_mode: std.builtin.OutputMode, | 68 | output_mode: std.builtin.OutputMode, |
| 64 | updates: std.ArrayList(Update), | 69 | updates: std.ArrayList(Update), |
| 65 | @"type": TestType, | 70 | @"type": TestType, |
| 66 | | 71 | |
| 67 | /// Adds a subcase in which the module is updated with new ZIR, and the | 72 | /// Adds a subcase in which the module is updated with `src`, and the |
| 68 | /// resulting ZIR is validated. | 73 | /// resulting ZIR is validated against `result`. |
| 69 | pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void { | 74 | pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void { |
| 70 | self.updates.append(.{ | 75 | self.updates.append(.{ |
| 71 | .src = src, | 76 | .src = src, |
| ... | @@ -73,6 +78,8 @@ pub const TestContext = struct { | ... | @@ -73,6 +78,8 @@ pub const TestContext = struct { |
| 73 | }) catch unreachable; | 78 | }) catch unreachable; |
| 74 | } | 79 | } |
| 75 | | 80 | |
| | 81 | /// Adds a subcase in which the module is updated with `src`, compiled, |
| | 82 | /// run, and the output is tested against `result`. |
| 76 | pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void { | 83 | pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void { |
| 77 | self.updates.append(.{ | 84 | self.updates.append(.{ |
| 78 | .src = src, | 85 | .src = src, |
| ... | @@ -80,10 +87,10 @@ pub const TestContext = struct { | ... | @@ -80,10 +87,10 @@ pub const TestContext = struct { |
| 80 | }) catch unreachable; | 87 | }) catch unreachable; |
| 81 | } | 88 | } |
| 82 | | 89 | |
| 83 | /// Adds a subcase in which the module is updated with invalid ZIR, and | 90 | /// Adds a subcase in which the module is updated with `src`, which |
| 84 | /// ensures that compilation fails for the expected reasons. | 91 | /// should contain invalid input, and ensures that compilation fails |
| 85 | /// | 92 | /// for the expected reasons, given in sequential order in `errors` in |
| 86 | /// Errors must be specified in sequential order. | 93 | /// the form `:error: line:column: message`. |
| 87 | pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { | 94 | pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { |
| 88 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; | 95 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; |
| 89 | for (errors) |e, i| { | 96 | for (errors) |e, i| { |
| ... | @@ -121,6 +128,8 @@ pub const TestContext = struct { | ... | @@ -121,6 +128,8 @@ pub const TestContext = struct { |
| 121 | self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable; | 128 | self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable; |
| 122 | } | 129 | } |
| 123 | | 130 | |
| | 131 | /// Adds a subcase in which the module is updated with `src`, and |
| | 132 | /// asserts that it compiles without issue |
| 124 | pub fn compiles(self: *Case, src: [:0]const u8) void { | 133 | pub fn compiles(self: *Case, src: [:0]const u8) void { |
| 125 | self.addError(src, &[_][]const u8{}); | 134 | self.addError(src, &[_][]const u8{}); |
| 126 | } | 135 | } |
| ... | @@ -142,10 +151,12 @@ pub const TestContext = struct { | ... | @@ -142,10 +151,12 @@ pub const TestContext = struct { |
| 142 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 151 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 143 | } | 152 | } |
| 144 | | 153 | |
| | 154 | /// Adds a test case for Zig input, producing an executable |
| 145 | pub fn exe(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { | 155 | pub fn exe(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { |
| 146 | return ctx.addExe(name, target, .Zig); | 156 | return ctx.addExe(name, target, .Zig); |
| 147 | } | 157 | } |
| 148 | | 158 | |
| | 159 | /// Adds a test case for ZIR input, producing an executable |
| 149 | pub fn exeZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { | 160 | pub fn exeZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { |
| 150 | return ctx.addExe(name, target, .ZIR); | 161 | return ctx.addExe(name, target, .ZIR); |
| 151 | } | 162 | } |
| ... | @@ -166,10 +177,12 @@ pub const TestContext = struct { | ... | @@ -166,10 +177,12 @@ pub const TestContext = struct { |
| 166 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 177 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 167 | } | 178 | } |
| 168 | | 179 | |
| | 180 | /// Adds a test case for Zig input, producing an object file |
| 169 | pub fn obj(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { | 181 | pub fn obj(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { |
| 170 | return ctx.addObj(name, target, .Zig); | 182 | return ctx.addObj(name, target, .Zig); |
| 171 | } | 183 | } |
| 172 | | 184 | |
| | 185 | /// Adds a test case for ZIR input, producing an object file |
| 173 | pub fn objZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { | 186 | pub fn objZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { |
| 174 | return ctx.addObj(name, target, .ZIR); | 187 | return ctx.addObj(name, target, .ZIR); |
| 175 | } | 188 | } |
| ... | @@ -184,6 +197,8 @@ pub const TestContext = struct { | ... | @@ -184,6 +197,8 @@ pub const TestContext = struct { |
| 184 | ctx.addExe(name, .{}, T).addCompareOutput(src, expected_stdout); | 197 | ctx.addExe(name, .{}, T).addCompareOutput(src, expected_stdout); |
| 185 | } | 198 | } |
| 186 | | 199 | |
| | 200 | /// Adds a test case that compiles the Zig source given in `src`, executes |
| | 201 | /// it, runs it, and tests the output against `expected_stdout` |
| 187 | pub fn compareOutput( | 202 | pub fn compareOutput( |
| 188 | ctx: *TestContext, | 203 | ctx: *TestContext, |
| 189 | name: []const u8, | 204 | name: []const u8, |
| ... | @@ -193,6 +208,8 @@ pub const TestContext = struct { | ... | @@ -193,6 +208,8 @@ pub const TestContext = struct { |
| 193 | return ctx.addCompareOutput(name, .Zig, src, expected_stdout); | 208 | return ctx.addCompareOutput(name, .Zig, src, expected_stdout); |
| 194 | } | 209 | } |
| 195 | | 210 | |
| | 211 | /// Adds a test case that compiles the ZIR source given in `src`, executes |
| | 212 | /// it, runs it, and tests the output against `expected_stdout` |
| 196 | pub fn compareOutputZIR( | 213 | pub fn compareOutputZIR( |
| 197 | ctx: *TestContext, | 214 | ctx: *TestContext, |
| 198 | name: []const u8, | 215 | name: []const u8, |
| ... | @@ -213,6 +230,8 @@ pub const TestContext = struct { | ... | @@ -213,6 +230,8 @@ pub const TestContext = struct { |
| 213 | ctx.addObj(name, target, T).addTransform(src, result); | 230 | ctx.addObj(name, target, T).addTransform(src, result); |
| 214 | } | 231 | } |
| 215 | | 232 | |
| | 233 | /// Adds a test case that compiles the Zig given in `src` to ZIR and tests |
| | 234 | /// the ZIR against `result` |
| 216 | pub fn transform( | 235 | pub fn transform( |
| 217 | ctx: *TestContext, | 236 | ctx: *TestContext, |
| 218 | name: []const u8, | 237 | name: []const u8, |
| ... | @@ -223,6 +242,8 @@ pub const TestContext = struct { | ... | @@ -223,6 +242,8 @@ pub const TestContext = struct { |
| 223 | ctx.addTransform(name, target, .Zig, src, result); | 242 | ctx.addTransform(name, target, .Zig, src, result); |
| 224 | } | 243 | } |
| 225 | | 244 | |
| | 245 | /// Adds a test case that cleans up the ZIR source given in `src`, and |
| | 246 | /// tests the resulting ZIR against `result` |
| 226 | pub fn transformZIR( | 247 | pub fn transformZIR( |
| 227 | ctx: *TestContext, | 248 | ctx: *TestContext, |
| 228 | name: []const u8, | 249 | name: []const u8, |
| ... | @@ -244,6 +265,9 @@ pub const TestContext = struct { | ... | @@ -244,6 +265,9 @@ pub const TestContext = struct { |
| 244 | ctx.addObj(name, target, T).addError(src, expected_errors); | 265 | ctx.addObj(name, target, T).addError(src, expected_errors); |
| 245 | } | 266 | } |
| 246 | | 267 | |
| | 268 | /// Adds a test case that ensures that the Zig given in `src` fails to |
| | 269 | /// compile for the expected reasons, given in sequential order in |
| | 270 | /// `expected_errors` in the form `:error: line:column: message`. |
| 247 | pub fn compileError( | 271 | pub fn compileError( |
| 248 | ctx: *TestContext, | 272 | ctx: *TestContext, |
| 249 | name: []const u8, | 273 | name: []const u8, |
| ... | @@ -254,6 +278,9 @@ pub const TestContext = struct { | ... | @@ -254,6 +278,9 @@ pub const TestContext = struct { |
| 254 | ctx.addError(name, target, .Zig, src, expected_errors); | 278 | ctx.addError(name, target, .Zig, src, expected_errors); |
| 255 | } | 279 | } |
| 256 | | 280 | |
| | 281 | /// Adds a test case that ensures that the ZIR given in `src` fails to |
| | 282 | /// compile for the expected reasons, given in sequential order in |
| | 283 | /// `expected_errors` in the form `:error: line:column: message`. |
| 257 | pub fn compileErrorZIR( | 284 | pub fn compileErrorZIR( |
| 258 | ctx: *TestContext, | 285 | ctx: *TestContext, |
| 259 | name: []const u8, | 286 | name: []const u8, |
| ... | @@ -274,48 +301,62 @@ pub const TestContext = struct { | ... | @@ -274,48 +301,62 @@ pub const TestContext = struct { |
| 274 | ctx.addObj(name, target, T).compiles(src); | 301 | ctx.addObj(name, target, T).compiles(src); |
| 275 | } | 302 | } |
| 276 | | 303 | |
| 277 | pub fn incrementalFailure( | 304 | /// Adds a test case that asserts that the Zig given in `src` compiles |
| | 305 | /// without any errors. |
| | 306 | pub fn compiles( |
| 278 | ctx: *TestContext, | 307 | ctx: *TestContext, |
| 279 | name: []const u8, | 308 | name: []const u8, |
| 280 | target: std.zig.CrossTarget, | 309 | target: std.zig.CrossTarget, |
| 281 | src: [:0]const u8, | 310 | src: [:0]const u8, |
| 282 | expected_errors: []const []const u8, | | |
| 283 | fixed_src: [:0]const u8, | | |
| 284 | ) void { | 311 | ) void { |
| 285 | var case = ctx.addObj(name, target, .Zig); | 312 | ctx.addCompiles(name, target, .Zig, src); |
| 286 | case.addError(src, expected_errors); | | |
| 287 | case.compiles(fixed_src); | | |
| 288 | } | 313 | } |
| 289 | | 314 | |
| 290 | pub fn incrementalFailureZIR( | 315 | /// Adds a test case that asserts that the ZIR given in `src` compiles |
| | 316 | /// without any errors. |
| | 317 | pub fn compilesZIR( |
| 291 | ctx: *TestContext, | 318 | ctx: *TestContext, |
| 292 | name: []const u8, | 319 | name: []const u8, |
| 293 | target: std.zig.CrossTarget, | 320 | target: std.zig.CrossTarget, |
| 294 | src: [:0]const u8, | 321 | src: [:0]const u8, |
| 295 | expected_errors: []const []const u8, | | |
| 296 | fixed_src: [:0]const u8, | | |
| 297 | ) void { | 322 | ) void { |
| 298 | var case = ctx.addObj(name, target, .ZIR); | 323 | ctx.addCompiles(name, target, .ZIR, src); |
| 299 | case.addError(src, expected_errors); | | |
| 300 | case.compiles(fixed_src); | | |
| 301 | } | 324 | } |
| 302 | | 325 | |
| 303 | pub fn compiles( | 326 | /// Adds a test case that first ensures that the Zig given in `src` fails |
| | 327 | /// to compile for the reasons given in sequential order in |
| | 328 | /// `expected_errors` in the form `:error: line:column: message`, then |
| | 329 | /// asserts that fixing the source (updating with `fixed_src`) isn't broken |
| | 330 | /// by incremental compilation. |
| | 331 | pub fn incrementalFailure( |
| 304 | ctx: *TestContext, | 332 | ctx: *TestContext, |
| 305 | name: []const u8, | 333 | name: []const u8, |
| 306 | target: std.zig.CrossTarget, | 334 | target: std.zig.CrossTarget, |
| 307 | src: [:0]const u8, | 335 | src: [:0]const u8, |
| | 336 | expected_errors: []const []const u8, |
| | 337 | fixed_src: [:0]const u8, |
| 308 | ) void { | 338 | ) void { |
| 309 | ctx.addCompiles(name, target, .Zig, src); | 339 | var case = ctx.addObj(name, target, .Zig); |
| | 340 | case.addError(src, expected_errors); |
| | 341 | case.compiles(fixed_src); |
| 310 | } | 342 | } |
| 311 | | 343 | |
| 312 | pub fn compilesZIR( | 344 | /// Adds a test case that first ensures that the ZIR given in `src` fails |
| | 345 | /// to compile for the reasons given in sequential order in |
| | 346 | /// `expected_errors` in the form `:error: line:column: message`, then |
| | 347 | /// asserts that fixing the source (updating with `fixed_src`) isn't broken |
| | 348 | /// by incremental compilation. |
| | 349 | pub fn incrementalFailureZIR( |
| 313 | ctx: *TestContext, | 350 | ctx: *TestContext, |
| 314 | name: []const u8, | 351 | name: []const u8, |
| 315 | target: std.zig.CrossTarget, | 352 | target: std.zig.CrossTarget, |
| 316 | src: [:0]const u8, | 353 | src: [:0]const u8, |
| | 354 | expected_errors: []const []const u8, |
| | 355 | fixed_src: [:0]const u8, |
| 317 | ) void { | 356 | ) void { |
| 318 | ctx.addCompiles(name, target, .ZIR, src); | 357 | var case = ctx.addObj(name, target, .ZIR); |
| | 358 | case.addError(src, expected_errors); |
| | 359 | case.compiles(fixed_src); |
| 319 | } | 360 | } |
| 320 | | 361 | |
| 321 | fn init() TestContext { | 362 | fn init() TestContext { |