| ... | ... | @@ -34,7 +34,7 @@ pub const TestContext = struct { |
| 34 | 34 | /// effects of the incremental compilation. |
| 35 | 35 | src: [:0]const u8, |
| 36 | 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 | 38 | /// the expected output ZIR. |
| 39 | 39 | Transformation: [:0]const u8, |
| 40 | 40 | /// An error update attempts to compile bad code, and ensures that it |
| ... | ... | @@ -43,6 +43,7 @@ pub const TestContext = struct { |
| 43 | 43 | Error: []const ErrorMsg, |
| 44 | 44 | /// An execution update compiles and runs the input, testing the |
| 45 | 45 | /// stdout against the expected results |
| 46 | /// This is a slice containing the expected message. |
| 46 | 47 | Execution: []const u8, |
| 47 | 48 | }, |
| 48 | 49 | }; |
| ... | ... | @@ -56,16 +57,20 @@ pub const TestContext = struct { |
| 56 | 57 | /// update, so each update's source is treated as a single file being |
| 57 | 58 | /// updated by the test harness and incrementally compiled. |
| 58 | 59 | pub const Case = struct { |
| 60 | /// The name of the test case. This is shown if a test fails, and |
| 61 | /// otherwise ignored. |
| 59 | 62 | name: []const u8, |
| 60 | 63 | /// The platform the test targets. For non-native platforms, an emulator |
| 61 | 64 | /// such as QEMU is required for tests to complete. |
| 62 | 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 | 68 | output_mode: std.builtin.OutputMode, |
| 64 | 69 | updates: std.ArrayList(Update), |
| 65 | 70 | @"type": TestType, |
| 66 | 71 | |
| 67 | | /// Adds a subcase in which the module is updated with new ZIR, and the |
| 68 | | /// resulting ZIR is validated. |
| 72 | /// Adds a subcase in which the module is updated with `src`, and the |
| 73 | /// resulting ZIR is validated against `result`. |
| 69 | 74 | pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void { |
| 70 | 75 | self.updates.append(.{ |
| 71 | 76 | .src = src, |
| ... | ... | @@ -73,6 +78,8 @@ pub const TestContext = struct { |
| 73 | 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 | 83 | pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void { |
| 77 | 84 | self.updates.append(.{ |
| 78 | 85 | .src = src, |
| ... | ... | @@ -80,10 +87,10 @@ pub const TestContext = struct { |
| 80 | 87 | }) catch unreachable; |
| 81 | 88 | } |
| 82 | 89 | |
| 83 | | /// Adds a subcase in which the module is updated with invalid ZIR, and |
| 84 | | /// ensures that compilation fails for the expected reasons. |
| 85 | | /// |
| 86 | | /// Errors must be specified in sequential order. |
| 90 | /// Adds a subcase in which the module is updated with `src`, which |
| 91 | /// should contain invalid input, and ensures that compilation fails |
| 92 | /// for the expected reasons, given in sequential order in `errors` in |
| 93 | /// the form `:error: line:column: message`. |
| 87 | 94 | pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void { |
| 88 | 95 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; |
| 89 | 96 | for (errors) |e, i| { |
| ... | ... | @@ -121,6 +128,8 @@ pub const TestContext = struct { |
| 121 | 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 | 133 | pub fn compiles(self: *Case, src: [:0]const u8) void { |
| 125 | 134 | self.addError(src, &[_][]const u8{}); |
| 126 | 135 | } |
| ... | ... | @@ -142,10 +151,12 @@ pub const TestContext = struct { |
| 142 | 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 | 155 | pub fn exe(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { |
| 146 | 156 | return ctx.addExe(name, target, .Zig); |
| 147 | 157 | } |
| 148 | 158 | |
| 159 | /// Adds a test case for ZIR input, producing an executable |
| 149 | 160 | pub fn exeZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { |
| 150 | 161 | return ctx.addExe(name, target, .ZIR); |
| 151 | 162 | } |
| ... | ... | @@ -166,10 +177,12 @@ pub const TestContext = struct { |
| 166 | 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 | 181 | pub fn obj(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { |
| 170 | 182 | return ctx.addObj(name, target, .Zig); |
| 171 | 183 | } |
| 172 | 184 | |
| 185 | /// Adds a test case for ZIR input, producing an object file |
| 173 | 186 | pub fn objZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case { |
| 174 | 187 | return ctx.addObj(name, target, .ZIR); |
| 175 | 188 | } |
| ... | ... | @@ -184,6 +197,8 @@ pub const TestContext = struct { |
| 184 | 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 | 202 | pub fn compareOutput( |
| 188 | 203 | ctx: *TestContext, |
| 189 | 204 | name: []const u8, |
| ... | ... | @@ -193,6 +208,8 @@ pub const TestContext = struct { |
| 193 | 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 | 213 | pub fn compareOutputZIR( |
| 197 | 214 | ctx: *TestContext, |
| 198 | 215 | name: []const u8, |
| ... | ... | @@ -213,6 +230,8 @@ pub const TestContext = struct { |
| 213 | 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 | 235 | pub fn transform( |
| 217 | 236 | ctx: *TestContext, |
| 218 | 237 | name: []const u8, |
| ... | ... | @@ -223,6 +242,8 @@ pub const TestContext = struct { |
| 223 | 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 | 247 | pub fn transformZIR( |
| 227 | 248 | ctx: *TestContext, |
| 228 | 249 | name: []const u8, |
| ... | ... | @@ -244,6 +265,9 @@ pub const TestContext = struct { |
| 244 | 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 | 271 | pub fn compileError( |
| 248 | 272 | ctx: *TestContext, |
| 249 | 273 | name: []const u8, |
| ... | ... | @@ -254,6 +278,9 @@ pub const TestContext = struct { |
| 254 | 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 | 284 | pub fn compileErrorZIR( |
| 258 | 285 | ctx: *TestContext, |
| 259 | 286 | name: []const u8, |
| ... | ... | @@ -274,48 +301,62 @@ pub const TestContext = struct { |
| 274 | 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 | 307 | ctx: *TestContext, |
| 279 | 308 | name: []const u8, |
| 280 | 309 | target: std.zig.CrossTarget, |
| 281 | 310 | src: [:0]const u8, |
| 282 | | expected_errors: []const []const u8, |
| 283 | | fixed_src: [:0]const u8, |
| 284 | 311 | ) void { |
| 285 | | var case = ctx.addObj(name, target, .Zig); |
| 286 | | case.addError(src, expected_errors); |
| 287 | | case.compiles(fixed_src); |
| 312 | ctx.addCompiles(name, target, .Zig, 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 | 318 | ctx: *TestContext, |
| 292 | 319 | name: []const u8, |
| 293 | 320 | target: std.zig.CrossTarget, |
| 294 | 321 | src: [:0]const u8, |
| 295 | | expected_errors: []const []const u8, |
| 296 | | fixed_src: [:0]const u8, |
| 297 | 322 | ) void { |
| 298 | | var case = ctx.addObj(name, target, .ZIR); |
| 299 | | case.addError(src, expected_errors); |
| 300 | | case.compiles(fixed_src); |
| 323 | ctx.addCompiles(name, target, .ZIR, 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 | 332 | ctx: *TestContext, |
| 305 | 333 | name: []const u8, |
| 306 | 334 | target: std.zig.CrossTarget, |
| 307 | 335 | src: [:0]const u8, |
| 336 | expected_errors: []const []const u8, |
| 337 | fixed_src: [:0]const u8, |
| 308 | 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 | 350 | ctx: *TestContext, |
| 314 | 351 | name: []const u8, |
| 315 | 352 | target: std.zig.CrossTarget, |
| 316 | 353 | src: [:0]const u8, |
| 354 | expected_errors: []const []const u8, |
| 355 | fixed_src: [:0]const u8, |
| 317 | 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 | 362 | fn init() TestContext { |