| author | |
| committer | |
| log | 9b7f438882b4f283d252d8ca364607fae385cc1a |
| tree | bb0458781d3bd0f4bb1a4a991690022d900fb767 |
| parent | d1e01e43d3b2078bfb07defb693d819e99eaa6c5 |
4 files changed, 410 insertions(+), 331 deletions(-)
build.zig+1| ... | ... | @@ -39,4 +39,5 @@ pub fn build(b: &Builder) { |
| 39 | 39 | test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); |
| 40 | 40 | test_step.dependOn(tests.addCompileErrorTests(b, test_filter)); |
| 41 | 41 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter)); |
| 42 | test_step.dependOn(tests.addDebugSafetyTests(b, test_filter)); | |
| 42 | 43 | } |
test/debug_safety.zig created+234| ... | ... | @@ -0,0 +1,234 @@ |
| 1 | const tests = @import("tests.zig"); | |
| 2 | ||
| 3 | pub fn addCases(cases: &tests.CompareOutputContext) { | |
| 4 | cases.addDebugSafety("calling panic", | |
| 5 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 6 | \\ @breakpoint(); | |
| 7 | \\ while (true) {} | |
| 8 | \\} | |
| 9 | \\pub fn main() -> %void { | |
| 10 | \\ @panic("oh no"); | |
| 11 | \\} | |
| 12 | ); | |
| 13 | ||
| 14 | cases.addDebugSafety("out of bounds slice access", | |
| 15 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 16 | \\ @breakpoint(); | |
| 17 | \\ while (true) {} | |
| 18 | \\} | |
| 19 | \\pub fn main() -> %void { | |
| 20 | \\ const a = []i32{1, 2, 3, 4}; | |
| 21 | \\ baz(bar(a)); | |
| 22 | \\} | |
| 23 | \\fn bar(a: []const i32) -> i32 { | |
| 24 | \\ a[4] | |
| 25 | \\} | |
| 26 | \\fn baz(a: i32) { } | |
| 27 | ); | |
| 28 | ||
| 29 | cases.addDebugSafety("integer addition overflow", | |
| 30 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 31 | \\ @breakpoint(); | |
| 32 | \\ while (true) {} | |
| 33 | \\} | |
| 34 | \\error Whatever; | |
| 35 | \\pub fn main() -> %void { | |
| 36 | \\ const x = add(65530, 10); | |
| 37 | \\ if (x == 0) return error.Whatever; | |
| 38 | \\} | |
| 39 | \\fn add(a: u16, b: u16) -> u16 { | |
| 40 | \\ a + b | |
| 41 | \\} | |
| 42 | ); | |
| 43 | ||
| 44 | cases.addDebugSafety("integer subtraction overflow", | |
| 45 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 46 | \\ @breakpoint(); | |
| 47 | \\ while (true) {} | |
| 48 | \\} | |
| 49 | \\error Whatever; | |
| 50 | \\pub fn main() -> %void { | |
| 51 | \\ const x = sub(10, 20); | |
| 52 | \\ if (x == 0) return error.Whatever; | |
| 53 | \\} | |
| 54 | \\fn sub(a: u16, b: u16) -> u16 { | |
| 55 | \\ a - b | |
| 56 | \\} | |
| 57 | ); | |
| 58 | ||
| 59 | cases.addDebugSafety("integer multiplication overflow", | |
| 60 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 61 | \\ @breakpoint(); | |
| 62 | \\ while (true) {} | |
| 63 | \\} | |
| 64 | \\error Whatever; | |
| 65 | \\pub fn main() -> %void { | |
| 66 | \\ const x = mul(300, 6000); | |
| 67 | \\ if (x == 0) return error.Whatever; | |
| 68 | \\} | |
| 69 | \\fn mul(a: u16, b: u16) -> u16 { | |
| 70 | \\ a * b | |
| 71 | \\} | |
| 72 | ); | |
| 73 | ||
| 74 | cases.addDebugSafety("integer negation overflow", | |
| 75 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 76 | \\ @breakpoint(); | |
| 77 | \\ while (true) {} | |
| 78 | \\} | |
| 79 | \\error Whatever; | |
| 80 | \\pub fn main() -> %void { | |
| 81 | \\ const x = neg(-32768); | |
| 82 | \\ if (x == 32767) return error.Whatever; | |
| 83 | \\} | |
| 84 | \\fn neg(a: i16) -> i16 { | |
| 85 | \\ -a | |
| 86 | \\} | |
| 87 | ); | |
| 88 | ||
| 89 | cases.addDebugSafety("signed integer division overflow", | |
| 90 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 91 | \\ @breakpoint(); | |
| 92 | \\ while (true) {} | |
| 93 | \\} | |
| 94 | \\error Whatever; | |
| 95 | \\pub fn main() -> %void { | |
| 96 | \\ const x = div(-32768, -1); | |
| 97 | \\ if (x == 32767) return error.Whatever; | |
| 98 | \\} | |
| 99 | \\fn div(a: i16, b: i16) -> i16 { | |
| 100 | \\ a / b | |
| 101 | \\} | |
| 102 | ); | |
| 103 | ||
| 104 | cases.addDebugSafety("signed shift left overflow", | |
| 105 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 106 | \\ @breakpoint(); | |
| 107 | \\ while (true) {} | |
| 108 | \\} | |
| 109 | \\error Whatever; | |
| 110 | \\pub fn main() -> %void { | |
| 111 | \\ const x = shl(-16385, 1); | |
| 112 | \\ if (x == 0) return error.Whatever; | |
| 113 | \\} | |
| 114 | \\fn shl(a: i16, b: i16) -> i16 { | |
| 115 | \\ a << b | |
| 116 | \\} | |
| 117 | ); | |
| 118 | ||
| 119 | cases.addDebugSafety("unsigned shift left overflow", | |
| 120 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 121 | \\ @breakpoint(); | |
| 122 | \\ while (true) {} | |
| 123 | \\} | |
| 124 | \\error Whatever; | |
| 125 | \\pub fn main() -> %void { | |
| 126 | \\ const x = shl(0b0010111111111111, 3); | |
| 127 | \\ if (x == 0) return error.Whatever; | |
| 128 | \\} | |
| 129 | \\fn shl(a: u16, b: u16) -> u16 { | |
| 130 | \\ a << b | |
| 131 | \\} | |
| 132 | ); | |
| 133 | ||
| 134 | cases.addDebugSafety("integer division by zero", | |
| 135 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 136 | \\ @breakpoint(); | |
| 137 | \\ while (true) {} | |
| 138 | \\} | |
| 139 | \\error Whatever; | |
| 140 | \\pub fn main() -> %void { | |
| 141 | \\ const x = div0(999, 0); | |
| 142 | \\} | |
| 143 | \\fn div0(a: i32, b: i32) -> i32 { | |
| 144 | \\ a / b | |
| 145 | \\} | |
| 146 | ); | |
| 147 | ||
| 148 | cases.addDebugSafety("exact division failure", | |
| 149 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 150 | \\ @breakpoint(); | |
| 151 | \\ while (true) {} | |
| 152 | \\} | |
| 153 | \\error Whatever; | |
| 154 | \\pub fn main() -> %void { | |
| 155 | \\ const x = divExact(10, 3); | |
| 156 | \\ if (x == 0) return error.Whatever; | |
| 157 | \\} | |
| 158 | \\fn divExact(a: i32, b: i32) -> i32 { | |
| 159 | \\ @divExact(a, b) | |
| 160 | \\} | |
| 161 | ); | |
| 162 | ||
| 163 | cases.addDebugSafety("cast []u8 to bigger slice of wrong size", | |
| 164 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 165 | \\ @breakpoint(); | |
| 166 | \\ while (true) {} | |
| 167 | \\} | |
| 168 | \\error Whatever; | |
| 169 | \\pub fn main() -> %void { | |
| 170 | \\ const x = widenSlice([]u8{1, 2, 3, 4, 5}); | |
| 171 | \\ if (x.len == 0) return error.Whatever; | |
| 172 | \\} | |
| 173 | \\fn widenSlice(slice: []const u8) -> []const i32 { | |
| 174 | \\ ([]const i32)(slice) | |
| 175 | \\} | |
| 176 | ); | |
| 177 | ||
| 178 | cases.addDebugSafety("value does not fit in shortening cast", | |
| 179 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 180 | \\ @breakpoint(); | |
| 181 | \\ while (true) {} | |
| 182 | \\} | |
| 183 | \\error Whatever; | |
| 184 | \\pub fn main() -> %void { | |
| 185 | \\ const x = shorten_cast(200); | |
| 186 | \\ if (x == 0) return error.Whatever; | |
| 187 | \\} | |
| 188 | \\fn shorten_cast(x: i32) -> i8 { | |
| 189 | \\ i8(x) | |
| 190 | \\} | |
| 191 | ); | |
| 192 | ||
| 193 | cases.addDebugSafety("signed integer not fitting in cast to unsigned integer", | |
| 194 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 195 | \\ @breakpoint(); | |
| 196 | \\ while (true) {} | |
| 197 | \\} | |
| 198 | \\error Whatever; | |
| 199 | \\pub fn main() -> %void { | |
| 200 | \\ const x = unsigned_cast(-10); | |
| 201 | \\ if (x == 0) return error.Whatever; | |
| 202 | \\} | |
| 203 | \\fn unsigned_cast(x: i32) -> u32 { | |
| 204 | \\ u32(x) | |
| 205 | \\} | |
| 206 | ); | |
| 207 | ||
| 208 | cases.addDebugSafety("unwrap error", | |
| 209 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 210 | \\ @breakpoint(); | |
| 211 | \\ while (true) {} | |
| 212 | \\} | |
| 213 | \\error Whatever; | |
| 214 | \\pub fn main() -> %void { | |
| 215 | \\ %%bar(); | |
| 216 | \\} | |
| 217 | \\fn bar() -> %void { | |
| 218 | \\ return error.Whatever; | |
| 219 | \\} | |
| 220 | ); | |
| 221 | ||
| 222 | cases.addDebugSafety("cast integer to error and no code matches", | |
| 223 | \\pub fn panic(message: []const u8) -> noreturn { | |
| 224 | \\ @breakpoint(); | |
| 225 | \\ while (true) {} | |
| 226 | \\} | |
| 227 | \\pub fn main() -> %void { | |
| 228 | \\ _ = bar(9999); | |
| 229 | \\} | |
| 230 | \\fn bar(x: u32) -> error { | |
| 231 | \\ return error(x); | |
| 232 | \\} | |
| 233 | ); | |
| 234 | } |
test/run_tests.cpp+17-292| ... | ... | @@ -40,7 +40,6 @@ struct TestCase { |
| 40 | 40 | bool is_parseh; |
| 41 | 41 | TestSpecial special; |
| 42 | 42 | bool is_release_mode; |
| 43 | bool is_debug_safety; | |
| 44 | 43 | AllowWarnings allow_warnings; |
| 45 | 44 | }; |
| 46 | 45 | |
| ... | ... | @@ -58,26 +57,6 @@ static const char *zig_exe = "./zig"; |
| 58 | 57 | #define NL "\n" |
| 59 | 58 | #endif |
| 60 | 59 | |
| 61 | static void add_debug_safety_case(const char *case_name, const char *source) { | |
| 62 | TestCase *test_case = allocate<TestCase>(1); | |
| 63 | test_case->is_debug_safety = true; | |
| 64 | test_case->case_name = buf_ptr(buf_sprintf("%s", case_name)); | |
| 65 | test_case->source_files.resize(1); | |
| 66 | test_case->source_files.at(0).relative_path = tmp_source_path; | |
| 67 | test_case->source_files.at(0).source_code = source; | |
| 68 | ||
| 69 | test_case->compiler_args.append("build_exe"); | |
| 70 | test_case->compiler_args.append(tmp_source_path); | |
| 71 | ||
| 72 | test_case->compiler_args.append("--name"); | |
| 73 | test_case->compiler_args.append("test"); | |
| 74 | ||
| 75 | test_case->compiler_args.append("--output"); | |
| 76 | test_case->compiler_args.append(tmp_exe_path); | |
| 77 | ||
| 78 | test_cases.append(test_case); | |
| 79 | } | |
| 80 | ||
| 81 | 60 | static TestCase *add_parseh_case(const char *case_name, AllowWarnings allow_warnings, |
| 82 | 61 | const char *source, size_t count, ...) |
| 83 | 62 | { |
| ... | ... | @@ -107,240 +86,6 @@ static TestCase *add_parseh_case(const char *case_name, AllowWarnings allow_warn |
| 107 | 86 | va_end(ap); |
| 108 | 87 | return test_case; |
| 109 | 88 | } |
| 110 | ////////////////////////////////////////////////////////////////////////////// | |
| 111 | ||
| 112 | static void add_debug_safety_test_cases(void) { | |
| 113 | add_debug_safety_case("calling panic", R"SOURCE( | |
| 114 | pub fn panic(message: []const u8) -> noreturn { | |
| 115 | @breakpoint(); | |
| 116 | while (true) {} | |
| 117 | } | |
| 118 | pub fn main() -> %void { | |
| 119 | @panic("oh no"); | |
| 120 | } | |
| 121 | )SOURCE"); | |
| 122 | ||
| 123 | add_debug_safety_case("out of bounds slice access", R"SOURCE( | |
| 124 | pub fn panic(message: []const u8) -> noreturn { | |
| 125 | @breakpoint(); | |
| 126 | while (true) {} | |
| 127 | } | |
| 128 | pub fn main() -> %void { | |
| 129 | const a = []i32{1, 2, 3, 4}; | |
| 130 | baz(bar(a)); | |
| 131 | } | |
| 132 | fn bar(a: []const i32) -> i32 { | |
| 133 | a[4] | |
| 134 | } | |
| 135 | fn baz(a: i32) { } | |
| 136 | )SOURCE"); | |
| 137 | ||
| 138 | add_debug_safety_case("integer addition overflow", R"SOURCE( | |
| 139 | pub fn panic(message: []const u8) -> noreturn { | |
| 140 | @breakpoint(); | |
| 141 | while (true) {} | |
| 142 | } | |
| 143 | error Whatever; | |
| 144 | pub fn main() -> %void { | |
| 145 | const x = add(65530, 10); | |
| 146 | if (x == 0) return error.Whatever; | |
| 147 | } | |
| 148 | fn add(a: u16, b: u16) -> u16 { | |
| 149 | a + b | |
| 150 | } | |
| 151 | )SOURCE"); | |
| 152 | ||
| 153 | add_debug_safety_case("integer subtraction overflow", R"SOURCE( | |
| 154 | pub fn panic(message: []const u8) -> noreturn { | |
| 155 | @breakpoint(); | |
| 156 | while (true) {} | |
| 157 | } | |
| 158 | error Whatever; | |
| 159 | pub fn main() -> %void { | |
| 160 | const x = sub(10, 20); | |
| 161 | if (x == 0) return error.Whatever; | |
| 162 | } | |
| 163 | fn sub(a: u16, b: u16) -> u16 { | |
| 164 | a - b | |
| 165 | } | |
| 166 | )SOURCE"); | |
| 167 | ||
| 168 | add_debug_safety_case("integer multiplication overflow", R"SOURCE( | |
| 169 | pub fn panic(message: []const u8) -> noreturn { | |
| 170 | @breakpoint(); | |
| 171 | while (true) {} | |
| 172 | } | |
| 173 | error Whatever; | |
| 174 | pub fn main() -> %void { | |
| 175 | const x = mul(300, 6000); | |
| 176 | if (x == 0) return error.Whatever; | |
| 177 | } | |
| 178 | fn mul(a: u16, b: u16) -> u16 { | |
| 179 | a * b | |
| 180 | } | |
| 181 | )SOURCE"); | |
| 182 | ||
| 183 | add_debug_safety_case("integer negation overflow", R"SOURCE( | |
| 184 | pub fn panic(message: []const u8) -> noreturn { | |
| 185 | @breakpoint(); | |
| 186 | while (true) {} | |
| 187 | } | |
| 188 | error Whatever; | |
| 189 | pub fn main() -> %void { | |
| 190 | const x = neg(-32768); | |
| 191 | if (x == 32767) return error.Whatever; | |
| 192 | } | |
| 193 | fn neg(a: i16) -> i16 { | |
| 194 | -a | |
| 195 | } | |
| 196 | )SOURCE"); | |
| 197 | ||
| 198 | add_debug_safety_case("signed integer division overflow", R"SOURCE( | |
| 199 | pub fn panic(message: []const u8) -> noreturn { | |
| 200 | @breakpoint(); | |
| 201 | while (true) {} | |
| 202 | } | |
| 203 | error Whatever; | |
| 204 | pub fn main() -> %void { | |
| 205 | const x = div(-32768, -1); | |
| 206 | if (x == 32767) return error.Whatever; | |
| 207 | } | |
| 208 | fn div(a: i16, b: i16) -> i16 { | |
| 209 | a / b | |
| 210 | } | |
| 211 | )SOURCE"); | |
| 212 | ||
| 213 | add_debug_safety_case("signed shift left overflow", R"SOURCE( | |
| 214 | pub fn panic(message: []const u8) -> noreturn { | |
| 215 | @breakpoint(); | |
| 216 | while (true) {} | |
| 217 | } | |
| 218 | error Whatever; | |
| 219 | pub fn main() -> %void { | |
| 220 | const x = shl(-16385, 1); | |
| 221 | if (x == 0) return error.Whatever; | |
| 222 | } | |
| 223 | fn shl(a: i16, b: i16) -> i16 { | |
| 224 | a << b | |
| 225 | } | |
| 226 | )SOURCE"); | |
| 227 | ||
| 228 | add_debug_safety_case("unsigned shift left overflow", R"SOURCE( | |
| 229 | pub fn panic(message: []const u8) -> noreturn { | |
| 230 | @breakpoint(); | |
| 231 | while (true) {} | |
| 232 | } | |
| 233 | error Whatever; | |
| 234 | pub fn main() -> %void { | |
| 235 | const x = shl(0b0010111111111111, 3); | |
| 236 | if (x == 0) return error.Whatever; | |
| 237 | } | |
| 238 | fn shl(a: u16, b: u16) -> u16 { | |
| 239 | a << b | |
| 240 | } | |
| 241 | )SOURCE"); | |
| 242 | ||
| 243 | add_debug_safety_case("integer division by zero", R"SOURCE( | |
| 244 | pub fn panic(message: []const u8) -> noreturn { | |
| 245 | @breakpoint(); | |
| 246 | while (true) {} | |
| 247 | } | |
| 248 | error Whatever; | |
| 249 | pub fn main() -> %void { | |
| 250 | const x = div0(999, 0); | |
| 251 | } | |
| 252 | fn div0(a: i32, b: i32) -> i32 { | |
| 253 | a / b | |
| 254 | } | |
| 255 | )SOURCE"); | |
| 256 | ||
| 257 | add_debug_safety_case("exact division failure", R"SOURCE( | |
| 258 | pub fn panic(message: []const u8) -> noreturn { | |
| 259 | @breakpoint(); | |
| 260 | while (true) {} | |
| 261 | } | |
| 262 | error Whatever; | |
| 263 | pub fn main() -> %void { | |
| 264 | const x = divExact(10, 3); | |
| 265 | if (x == 0) return error.Whatever; | |
| 266 | } | |
| 267 | fn divExact(a: i32, b: i32) -> i32 { | |
| 268 | @divExact(a, b) | |
| 269 | } | |
| 270 | )SOURCE"); | |
| 271 | ||
| 272 | add_debug_safety_case("cast []u8 to bigger slice of wrong size", R"SOURCE( | |
| 273 | pub fn panic(message: []const u8) -> noreturn { | |
| 274 | @breakpoint(); | |
| 275 | while (true) {} | |
| 276 | } | |
| 277 | error Whatever; | |
| 278 | pub fn main() -> %void { | |
| 279 | const x = widenSlice([]u8{1, 2, 3, 4, 5}); | |
| 280 | if (x.len == 0) return error.Whatever; | |
| 281 | } | |
| 282 | fn widenSlice(slice: []const u8) -> []const i32 { | |
| 283 | ([]const i32)(slice) | |
| 284 | } | |
| 285 | )SOURCE"); | |
| 286 | ||
| 287 | add_debug_safety_case("value does not fit in shortening cast", R"SOURCE( | |
| 288 | pub fn panic(message: []const u8) -> noreturn { | |
| 289 | @breakpoint(); | |
| 290 | while (true) {} | |
| 291 | } | |
| 292 | error Whatever; | |
| 293 | pub fn main() -> %void { | |
| 294 | const x = shorten_cast(200); | |
| 295 | if (x == 0) return error.Whatever; | |
| 296 | } | |
| 297 | fn shorten_cast(x: i32) -> i8 { | |
| 298 | i8(x) | |
| 299 | } | |
| 300 | )SOURCE"); | |
| 301 | ||
| 302 | add_debug_safety_case("signed integer not fitting in cast to unsigned integer", R"SOURCE( | |
| 303 | pub fn panic(message: []const u8) -> noreturn { | |
| 304 | @breakpoint(); | |
| 305 | while (true) {} | |
| 306 | } | |
| 307 | error Whatever; | |
| 308 | pub fn main() -> %void { | |
| 309 | const x = unsigned_cast(-10); | |
| 310 | if (x == 0) return error.Whatever; | |
| 311 | } | |
| 312 | fn unsigned_cast(x: i32) -> u32 { | |
| 313 | u32(x) | |
| 314 | } | |
| 315 | )SOURCE"); | |
| 316 | ||
| 317 | add_debug_safety_case("unwrap error", R"SOURCE( | |
| 318 | pub fn panic(message: []const u8) -> noreturn { | |
| 319 | @breakpoint(); | |
| 320 | while (true) {} | |
| 321 | } | |
| 322 | error Whatever; | |
| 323 | pub fn main() -> %void { | |
| 324 | %%bar(); | |
| 325 | } | |
| 326 | fn bar() -> %void { | |
| 327 | return error.Whatever; | |
| 328 | } | |
| 329 | )SOURCE"); | |
| 330 | ||
| 331 | add_debug_safety_case("cast integer to error and no code matches", R"SOURCE( | |
| 332 | pub fn panic(message: []const u8) -> noreturn { | |
| 333 | @breakpoint(); | |
| 334 | while (true) {} | |
| 335 | } | |
| 336 | pub fn main() -> %void { | |
| 337 | _ = bar(9999); | |
| 338 | } | |
| 339 | fn bar(x: u32) -> error { | |
| 340 | return error(x); | |
| 341 | } | |
| 342 | )SOURCE"); | |
| 343 | } | |
| 344 | 89 | |
| 345 | 90 | ////////////////////////////////////////////////////////////////////////////// |
| 346 | 91 | |
| ... | ... | @@ -653,43 +398,24 @@ static void run_test(TestCase *test_case) { |
| 653 | 398 | Buf program_stdout = BUF_INIT; |
| 654 | 399 | os_exec_process(tmp_exe_path, test_case->program_args, &term, &program_stderr, &program_stdout); |
| 655 | 400 | |
| 656 | if (test_case->is_debug_safety) { | |
| 657 | int debug_trap_signal = 5; | |
| 658 | if (term.how != TerminationIdSignaled || term.code != debug_trap_signal) { | |
| 659 | if (term.how == TerminationIdClean) { | |
| 660 | printf("\nProgram expected to hit debug trap (signal %d) but exited with return code %d\n", | |
| 661 | debug_trap_signal, term.code); | |
| 662 | } else if (term.how == TerminationIdSignaled) { | |
| 663 | printf("\nProgram expected to hit debug trap (signal %d) but signaled with code %d\n", | |
| 664 | debug_trap_signal, term.code); | |
| 665 | } else { | |
| 666 | printf("\nProgram expected to hit debug trap (signal %d) exited in an unexpected way\n", | |
| 667 | debug_trap_signal); | |
| 668 | } | |
| 669 | print_compiler_invocation(test_case); | |
| 670 | print_exe_invocation(test_case); | |
| 671 | exit(1); | |
| 672 | } | |
| 673 | } else { | |
| 674 | if (term.how != TerminationIdClean || term.code != 0) { | |
| 675 | printf("\nProgram exited with error\n"); | |
| 676 | print_compiler_invocation(test_case); | |
| 677 | print_exe_invocation(test_case); | |
| 678 | printf("%s\n", buf_ptr(&program_stderr)); | |
| 679 | exit(1); | |
| 680 | } | |
| 401 | if (term.how != TerminationIdClean || term.code != 0) { | |
| 402 | printf("\nProgram exited with error\n"); | |
| 403 | print_compiler_invocation(test_case); | |
| 404 | print_exe_invocation(test_case); | |
| 405 | printf("%s\n", buf_ptr(&program_stderr)); | |
| 406 | exit(1); | |
| 407 | } | |
| 681 | 408 | |
| 682 | if (test_case->output != nullptr && !buf_eql_str(&program_stdout, test_case->output)) { | |
| 683 | printf("\n"); | |
| 684 | print_compiler_invocation(test_case); | |
| 685 | print_exe_invocation(test_case); | |
| 686 | printf("==== Test failed. Expected output: ====\n"); | |
| 687 | printf("%s\n", test_case->output); | |
| 688 | printf("========= Actual output: ==============\n"); | |
| 689 | printf("%s\n", buf_ptr(&program_stdout)); | |
| 690 | printf("=======================================\n"); | |
| 691 | exit(1); | |
| 692 | } | |
| 409 | if (test_case->output != nullptr && !buf_eql_str(&program_stdout, test_case->output)) { | |
| 410 | printf("\n"); | |
| 411 | print_compiler_invocation(test_case); | |
| 412 | print_exe_invocation(test_case); | |
| 413 | printf("==== Test failed. Expected output: ====\n"); | |
| 414 | printf("%s\n", test_case->output); | |
| 415 | printf("========= Actual output: ==============\n"); | |
| 416 | printf("%s\n", buf_ptr(&program_stdout)); | |
| 417 | printf("=======================================\n"); | |
| 418 | exit(1); | |
| 693 | 419 | } |
| 694 | 420 | } |
| 695 | 421 | |
| ... | ... | @@ -740,7 +466,6 @@ int main(int argc, char **argv) { |
| 740 | 466 | } |
| 741 | 467 | } |
| 742 | 468 | } |
| 743 | add_debug_safety_test_cases(); | |
| 744 | 469 | add_parseh_test_cases(); |
| 745 | 470 | run_all_tests(grep_text); |
| 746 | 471 | cleanup(); |
test/tests.zig+158-39| ... | ... | @@ -16,6 +16,7 @@ pub const compare_output = @import("compare_output.zig"); |
| 16 | 16 | pub const build_examples = @import("build_examples.zig"); |
| 17 | 17 | pub const compile_errors = @import("compile_errors.zig"); |
| 18 | 18 | pub const assemble_and_link = @import("assemble_and_link.zig"); |
| 19 | pub const debug_safety = @import("debug_safety.zig"); | |
| 19 | 20 | |
| 20 | 21 | pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { |
| 21 | 22 | const cases = %%b.allocator.create(CompareOutputContext); |
| ... | ... | @@ -31,6 +32,20 @@ pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &bu |
| 31 | 32 | return cases.step; |
| 32 | 33 | } |
| 33 | 34 | |
| 35 | pub fn addDebugSafetyTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { | |
| 36 | const cases = %%b.allocator.create(CompareOutputContext); | |
| 37 | *cases = CompareOutputContext { | |
| 38 | .b = b, | |
| 39 | .step = b.step("test-debug-safety", "Run the debug safety tests"), | |
| 40 | .test_index = 0, | |
| 41 | .test_filter = test_filter, | |
| 42 | }; | |
| 43 | ||
| 44 | debug_safety.addCases(cases); | |
| 45 | ||
| 46 | return cases.step; | |
| 47 | } | |
| 48 | ||
| 34 | 49 | pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { |
| 35 | 50 | const cases = %%b.allocator.create(CompileErrorContext); |
| 36 | 51 | *cases = CompileErrorContext { |
| ... | ... | @@ -79,12 +94,18 @@ pub const CompareOutputContext = struct { |
| 79 | 94 | test_index: usize, |
| 80 | 95 | test_filter: ?[]const u8, |
| 81 | 96 | |
| 97 | const Special = enum { | |
| 98 | None, | |
| 99 | Asm, | |
| 100 | DebugSafety, | |
| 101 | }; | |
| 102 | ||
| 82 | 103 | const TestCase = struct { |
| 83 | 104 | name: []const u8, |
| 84 | 105 | sources: List(SourceFile), |
| 85 | 106 | expected_output: []const u8, |
| 86 | 107 | link_libc: bool, |
| 87 | is_asm: bool, | |
| 108 | special: Special, | |
| 88 | 109 | |
| 89 | 110 | const SourceFile = struct { |
| 90 | 111 | filename: []const u8, |
| ... | ... | @@ -175,17 +196,83 @@ pub const CompareOutputContext = struct { |
| 175 | 196 | } |
| 176 | 197 | }; |
| 177 | 198 | |
| 199 | const DebugSafetyRunStep = struct { | |
| 200 | step: build.Step, | |
| 201 | context: &CompareOutputContext, | |
| 202 | exe_path: []const u8, | |
| 203 | name: []const u8, | |
| 204 | test_index: usize, | |
| 205 | ||
| 206 | pub fn create(context: &CompareOutputContext, exe_path: []const u8, | |
| 207 | name: []const u8) -> &DebugSafetyRunStep | |
| 208 | { | |
| 209 | const allocator = context.b.allocator; | |
| 210 | const ptr = %%allocator.create(DebugSafetyRunStep); | |
| 211 | *ptr = DebugSafetyRunStep { | |
| 212 | .context = context, | |
| 213 | .exe_path = exe_path, | |
| 214 | .name = name, | |
| 215 | .test_index = context.test_index, | |
| 216 | .step = build.Step.init("DebugSafetyRun", allocator, make), | |
| 217 | }; | |
| 218 | context.test_index += 1; | |
| 219 | return ptr; | |
| 220 | } | |
| 221 | ||
| 222 | fn make(step: &build.Step) -> %void { | |
| 223 | const self = @fieldParentPtr(DebugSafetyRunStep, "step", step); | |
| 224 | const b = self.context.b; | |
| 225 | ||
| 226 | const full_exe_path = b.pathFromRoot(self.exe_path); | |
| 227 | ||
| 228 | %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name); | |
| 229 | ||
| 230 | var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map, | |
| 231 | StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err| | |
| 232 | { | |
| 233 | debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); | |
| 234 | }; | |
| 235 | ||
| 236 | const term = child.wait() %% |err| { | |
| 237 | debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); | |
| 238 | }; | |
| 239 | ||
| 240 | const debug_trap_signal: i32 = 5; | |
| 241 | switch (term) { | |
| 242 | Term.Clean => |code| { | |
| 243 | %%io.stderr.printf("\nProgram expected to hit debug trap (signal {}) " ++ | |
| 244 | "but exited with return code {}\n", debug_trap_signal, code); | |
| 245 | return error.TestFailed; | |
| 246 | }, | |
| 247 | Term.Signal => |sig| { | |
| 248 | if (sig != debug_trap_signal) { | |
| 249 | %%io.stderr.printf("\nProgram expected to hit debug trap (signal {}) " ++ | |
| 250 | "but instead signaled {}\n", debug_trap_signal, sig); | |
| 251 | return error.TestFailed; | |
| 252 | } | |
| 253 | }, | |
| 254 | else => { | |
| 255 | %%io.stderr.printf("\nProgram expected to hit debug trap (signal {}) " ++ | |
| 256 | " but exited in an unexpected way\n", debug_trap_signal); | |
| 257 | return error.TestFailed; | |
| 258 | }, | |
| 259 | } | |
| 260 | ||
| 261 | %%io.stderr.printf("OK\n"); | |
| 262 | } | |
| 263 | }; | |
| 264 | ||
| 178 | 265 | pub fn createExtra(self: &CompareOutputContext, name: []const u8, source: []const u8, |
| 179 | expected_output: []const u8, is_asm: bool) -> TestCase | |
| 266 | expected_output: []const u8, special: Special) -> TestCase | |
| 180 | 267 | { |
| 181 | 268 | var tc = TestCase { |
| 182 | 269 | .name = name, |
| 183 | 270 | .sources = List(TestCase.SourceFile).init(self.b.allocator), |
| 184 | 271 | .expected_output = expected_output, |
| 185 | 272 | .link_libc = false, |
| 186 | .is_asm = is_asm, | |
| 273 | .special = special, | |
| 187 | 274 | }; |
| 188 | const root_src_name = if (is_asm) "source.s" else "source.zig"; | |
| 275 | const root_src_name = if (special == Special.Asm) "source.s" else "source.zig"; | |
| 189 | 276 | tc.addSourceFile(root_src_name, source); |
| 190 | 277 | return tc; |
| 191 | 278 | } |
| ... | ... | @@ -193,7 +280,7 @@ pub const CompareOutputContext = struct { |
| 193 | 280 | pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8, |
| 194 | 281 | expected_output: []const u8) -> TestCase |
| 195 | 282 | { |
| 196 | return createExtra(self, name, source, expected_output, false); | |
| 283 | return createExtra(self, name, source, expected_output, Special.None); | |
| 197 | 284 | } |
| 198 | 285 | |
| 199 | 286 | pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) { |
| ... | ... | @@ -208,7 +295,12 @@ pub const CompareOutputContext = struct { |
| 208 | 295 | } |
| 209 | 296 | |
| 210 | 297 | pub fn addAsm(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) { |
| 211 | const tc = self.createExtra(name, source, expected_output, true); | |
| 298 | const tc = self.createExtra(name, source, expected_output, Special.Asm); | |
| 299 | self.addCase(tc); | |
| 300 | } | |
| 301 | ||
| 302 | pub fn addDebugSafety(self: &CompareOutputContext, name: []const u8, source: []const u8) { | |
| 303 | const tc = self.createExtra(name, source, undefined, Special.DebugSafety); | |
| 212 | 304 | self.addCase(tc); |
| 213 | 305 | } |
| 214 | 306 | |
| ... | ... | @@ -218,45 +310,74 @@ pub const CompareOutputContext = struct { |
| 218 | 310 | const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename); |
| 219 | 311 | const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test"); |
| 220 | 312 | |
| 221 | if (case.is_asm) { | |
| 222 | const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o"); | |
| 223 | const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "assemble-and-link {}", case.name); | |
| 224 | if (const filter ?= self.test_filter) { | |
| 225 | if (mem.indexOf(u8, annotated_case_name, filter) == null) | |
| 226 | return; | |
| 227 | } | |
| 313 | switch (case.special) { | |
| 314 | Special.Asm => { | |
| 315 | const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o"); | |
| 316 | const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "assemble-and-link {}", case.name); | |
| 317 | if (const filter ?= self.test_filter) { | |
| 318 | if (mem.indexOf(u8, annotated_case_name, filter) == null) | |
| 319 | return; | |
| 320 | } | |
| 228 | 321 | |
| 229 | const obj = b.addAssemble("test", root_src); | |
| 230 | obj.setOutputPath(obj_path); | |
| 322 | const obj = b.addAssemble("test", root_src); | |
| 323 | obj.setOutputPath(obj_path); | |
| 231 | 324 | |
| 232 | for (case.sources.toSliceConst()) |src_file| { | |
| 233 | const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename); | |
| 234 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | |
| 235 | obj.step.dependOn(&write_src.step); | |
| 236 | } | |
| 325 | for (case.sources.toSliceConst()) |src_file| { | |
| 326 | const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename); | |
| 327 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | |
| 328 | obj.step.dependOn(&write_src.step); | |
| 329 | } | |
| 237 | 330 | |
| 238 | const exe = b.addLinkExecutable("test"); | |
| 239 | exe.step.dependOn(&obj.step); | |
| 240 | exe.addObjectFile(obj_path); | |
| 241 | exe.setOutputPath(exe_path); | |
| 331 | const exe = b.addLinkExecutable("test"); | |
| 332 | exe.step.dependOn(&obj.step); | |
| 333 | exe.addObjectFile(obj_path); | |
| 334 | exe.setOutputPath(exe_path); | |
| 242 | 335 | |
| 243 | const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name, | |
| 244 | case.expected_output); | |
| 245 | run_and_cmp_output.step.dependOn(&exe.step); | |
| 336 | const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name, | |
| 337 | case.expected_output); | |
| 338 | run_and_cmp_output.step.dependOn(&exe.step); | |
| 339 | ||
| 340 | self.step.dependOn(&run_and_cmp_output.step); | |
| 341 | }, | |
| 342 | Special.None => { | |
| 343 | for ([]bool{false, true}) |release| { | |
| 344 | const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})", | |
| 345 | case.name, if (release) "release" else "debug"); | |
| 346 | if (const filter ?= self.test_filter) { | |
| 347 | if (mem.indexOf(u8, annotated_case_name, filter) == null) | |
| 348 | continue; | |
| 349 | } | |
| 246 | 350 | |
| 247 | self.step.dependOn(&run_and_cmp_output.step); | |
| 248 | } else { | |
| 249 | for ([]bool{false, true}) |release| { | |
| 250 | const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})", | |
| 251 | case.name, if (release) "release" else "debug"); | |
| 351 | const exe = b.addExecutable("test", root_src); | |
| 352 | exe.setOutputPath(exe_path); | |
| 353 | exe.setRelease(release); | |
| 354 | if (case.link_libc) { | |
| 355 | exe.linkLibrary("c"); | |
| 356 | } | |
| 357 | ||
| 358 | for (case.sources.toSliceConst()) |src_file| { | |
| 359 | const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename); | |
| 360 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | |
| 361 | exe.step.dependOn(&write_src.step); | |
| 362 | } | |
| 363 | ||
| 364 | const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name, | |
| 365 | case.expected_output); | |
| 366 | run_and_cmp_output.step.dependOn(&exe.step); | |
| 367 | ||
| 368 | self.step.dependOn(&run_and_cmp_output.step); | |
| 369 | } | |
| 370 | }, | |
| 371 | Special.DebugSafety => { | |
| 372 | const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o"); | |
| 373 | const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "debug-safety {}", case.name); | |
| 252 | 374 | if (const filter ?= self.test_filter) { |
| 253 | 375 | if (mem.indexOf(u8, annotated_case_name, filter) == null) |
| 254 | continue; | |
| 376 | return; | |
| 255 | 377 | } |
| 256 | 378 | |
| 257 | 379 | const exe = b.addExecutable("test", root_src); |
| 258 | 380 | exe.setOutputPath(exe_path); |
| 259 | exe.setRelease(release); | |
| 260 | 381 | if (case.link_libc) { |
| 261 | 382 | exe.linkLibrary("c"); |
| 262 | 383 | } |
| ... | ... | @@ -267,14 +388,12 @@ pub const CompareOutputContext = struct { |
| 267 | 388 | exe.step.dependOn(&write_src.step); |
| 268 | 389 | } |
| 269 | 390 | |
| 270 | const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name, | |
| 271 | case.expected_output); | |
| 391 | const run_and_cmp_output = DebugSafetyRunStep.create(self, exe_path, annotated_case_name); | |
| 272 | 392 | run_and_cmp_output.step.dependOn(&exe.step); |
| 273 | 393 | |
| 274 | 394 | self.step.dependOn(&run_and_cmp_output.step); |
| 275 | } | |
| 276 | }; | |
| 277 | ||
| 395 | }, | |
| 396 | } | |
| 278 | 397 | } |
| 279 | 398 | }; |
| 280 | 399 |