| 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,4 +39,5 @@ pub fn build(b: &Builder) { |
| 39 | test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); | 39 | test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); |
| 40 | test_step.dependOn(tests.addCompileErrorTests(b, test_filter)); | 40 | test_step.dependOn(tests.addCompileErrorTests(b, test_filter)); |
| 41 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter)); | 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,7 +40,6 @@ struct TestCase { |
| 40 | bool is_parseh; | 40 | bool is_parseh; |
| 41 | TestSpecial special; | 41 | TestSpecial special; |
| 42 | bool is_release_mode; | 42 | bool is_release_mode; |
| 43 | bool is_debug_safety; | ||
| 44 | AllowWarnings allow_warnings; | 43 | AllowWarnings allow_warnings; |
| 45 | }; | 44 | }; |
| 46 | 45 | ||
| ... | @@ -58,26 +57,6 @@ static const char *zig_exe = "./zig"; | ... | @@ -58,26 +57,6 @@ static const char *zig_exe = "./zig"; |
| 58 | #define NL "\n" | 57 | #define NL "\n" |
| 59 | #endif | 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 | static TestCase *add_parseh_case(const char *case_name, AllowWarnings allow_warnings, | 60 | static TestCase *add_parseh_case(const char *case_name, AllowWarnings allow_warnings, |
| 82 | const char *source, size_t count, ...) | 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,240 +86,6 @@ static TestCase *add_parseh_case(const char *case_name, AllowWarnings allow_warn |
| 107 | va_end(ap); | 86 | va_end(ap); |
| 108 | return test_case; | 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,43 +398,24 @@ static void run_test(TestCase *test_case) { |
| 653 | Buf program_stdout = BUF_INIT; | 398 | Buf program_stdout = BUF_INIT; |
| 654 | os_exec_process(tmp_exe_path, test_case->program_args, &term, &program_stderr, &program_stdout); | 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) { | 401 | if (term.how != TerminationIdClean || term.code != 0) { |
| 657 | int debug_trap_signal = 5; | 402 | printf("\nProgram exited with error\n"); |
| 658 | if (term.how != TerminationIdSignaled || term.code != debug_trap_signal) { | 403 | print_compiler_invocation(test_case); |
| 659 | if (term.how == TerminationIdClean) { | 404 | print_exe_invocation(test_case); |
| 660 | printf("\nProgram expected to hit debug trap (signal %d) but exited with return code %d\n", | 405 | printf("%s\n", buf_ptr(&program_stderr)); |
| 661 | debug_trap_signal, term.code); | 406 | exit(1); |
| 662 | } else if (term.how == TerminationIdSignaled) { | 407 | } |
| 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 | } | ||
| 681 | 408 | ||
| 682 | if (test_case->output != nullptr && !buf_eql_str(&program_stdout, test_case->output)) { | 409 | if (test_case->output != nullptr && !buf_eql_str(&program_stdout, test_case->output)) { |
| 683 | printf("\n"); | 410 | printf("\n"); |
| 684 | print_compiler_invocation(test_case); | 411 | print_compiler_invocation(test_case); |
| 685 | print_exe_invocation(test_case); | 412 | print_exe_invocation(test_case); |
| 686 | printf("==== Test failed. Expected output: ====\n"); | 413 | printf("==== Test failed. Expected output: ====\n"); |
| 687 | printf("%s\n", test_case->output); | 414 | printf("%s\n", test_case->output); |
| 688 | printf("========= Actual output: ==============\n"); | 415 | printf("========= Actual output: ==============\n"); |
| 689 | printf("%s\n", buf_ptr(&program_stdout)); | 416 | printf("%s\n", buf_ptr(&program_stdout)); |
| 690 | printf("=======================================\n"); | 417 | printf("=======================================\n"); |
| 691 | exit(1); | 418 | exit(1); |
| 692 | } | ||
| 693 | } | 419 | } |
| 694 | } | 420 | } |
| 695 | 421 | ||
| ... | @@ -740,7 +466,6 @@ int main(int argc, char **argv) { | ... | @@ -740,7 +466,6 @@ int main(int argc, char **argv) { |
| 740 | } | 466 | } |
| 741 | } | 467 | } |
| 742 | } | 468 | } |
| 743 | add_debug_safety_test_cases(); | ||
| 744 | add_parseh_test_cases(); | 469 | add_parseh_test_cases(); |
| 745 | run_all_tests(grep_text); | 470 | run_all_tests(grep_text); |
| 746 | cleanup(); | 471 | cleanup(); |
test/tests.zig+158-39| ... | @@ -16,6 +16,7 @@ pub const compare_output = @import("compare_output.zig"); | ... | @@ -16,6 +16,7 @@ pub const compare_output = @import("compare_output.zig"); |
| 16 | pub const build_examples = @import("build_examples.zig"); | 16 | pub const build_examples = @import("build_examples.zig"); |
| 17 | pub const compile_errors = @import("compile_errors.zig"); | 17 | pub const compile_errors = @import("compile_errors.zig"); |
| 18 | pub const assemble_and_link = @import("assemble_and_link.zig"); | 18 | pub const assemble_and_link = @import("assemble_and_link.zig"); |
| 19 | pub const debug_safety = @import("debug_safety.zig"); | ||
| 19 | 20 | ||
| 20 | pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { | 21 | pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { |
| 21 | const cases = %%b.allocator.create(CompareOutputContext); | 22 | const cases = %%b.allocator.create(CompareOutputContext); |
| ... | @@ -31,6 +32,20 @@ pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &bu | ... | @@ -31,6 +32,20 @@ pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &bu |
| 31 | return cases.step; | 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 | pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { | 49 | pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step { |
| 35 | const cases = %%b.allocator.create(CompileErrorContext); | 50 | const cases = %%b.allocator.create(CompileErrorContext); |
| 36 | *cases = CompileErrorContext { | 51 | *cases = CompileErrorContext { |
| ... | @@ -79,12 +94,18 @@ pub const CompareOutputContext = struct { | ... | @@ -79,12 +94,18 @@ pub const CompareOutputContext = struct { |
| 79 | test_index: usize, | 94 | test_index: usize, |
| 80 | test_filter: ?[]const u8, | 95 | test_filter: ?[]const u8, |
| 81 | 96 | ||
| 97 | const Special = enum { | ||
| 98 | None, | ||
| 99 | Asm, | ||
| 100 | DebugSafety, | ||
| 101 | }; | ||
| 102 | |||
| 82 | const TestCase = struct { | 103 | const TestCase = struct { |
| 83 | name: []const u8, | 104 | name: []const u8, |
| 84 | sources: List(SourceFile), | 105 | sources: List(SourceFile), |
| 85 | expected_output: []const u8, | 106 | expected_output: []const u8, |
| 86 | link_libc: bool, | 107 | link_libc: bool, |
| 87 | is_asm: bool, | 108 | special: Special, |
| 88 | 109 | ||
| 89 | const SourceFile = struct { | 110 | const SourceFile = struct { |
| 90 | filename: []const u8, | 111 | filename: []const u8, |
| ... | @@ -175,17 +196,83 @@ pub const CompareOutputContext = struct { | ... | @@ -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 | pub fn createExtra(self: &CompareOutputContext, name: []const u8, source: []const u8, | 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 | var tc = TestCase { | 268 | var tc = TestCase { |
| 182 | .name = name, | 269 | .name = name, |
| 183 | .sources = List(TestCase.SourceFile).init(self.b.allocator), | 270 | .sources = List(TestCase.SourceFile).init(self.b.allocator), |
| 184 | .expected_output = expected_output, | 271 | .expected_output = expected_output, |
| 185 | .link_libc = false, | 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 | tc.addSourceFile(root_src_name, source); | 276 | tc.addSourceFile(root_src_name, source); |
| 190 | return tc; | 277 | return tc; |
| 191 | } | 278 | } |
| ... | @@ -193,7 +280,7 @@ pub const CompareOutputContext = struct { | ... | @@ -193,7 +280,7 @@ pub const CompareOutputContext = struct { |
| 193 | pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8, | 280 | pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8, |
| 194 | expected_output: []const u8) -> TestCase | 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 | pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) { | 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,7 +295,12 @@ pub const CompareOutputContext = struct { |
| 208 | } | 295 | } |
| 209 | 296 | ||
| 210 | pub fn addAsm(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) { | 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 | self.addCase(tc); | 304 | self.addCase(tc); |
| 213 | } | 305 | } |
| 214 | 306 | ||
| ... | @@ -218,45 +310,74 @@ pub const CompareOutputContext = struct { | ... | @@ -218,45 +310,74 @@ pub const CompareOutputContext = struct { |
| 218 | const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename); | 310 | const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename); |
| 219 | const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test"); | 311 | const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test"); |
| 220 | 312 | ||
| 221 | if (case.is_asm) { | 313 | switch (case.special) { |
| 222 | const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o"); | 314 | Special.Asm => { |
| 223 | const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "assemble-and-link {}", case.name); | 315 | const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o"); |
| 224 | if (const filter ?= self.test_filter) { | 316 | const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "assemble-and-link {}", case.name); |
| 225 | if (mem.indexOf(u8, annotated_case_name, filter) == null) | 317 | if (const filter ?= self.test_filter) { |
| 226 | return; | 318 | if (mem.indexOf(u8, annotated_case_name, filter) == null) |
| 227 | } | 319 | return; |
| 320 | } | ||
| 228 | 321 | ||
| 229 | const obj = b.addAssemble("test", root_src); | 322 | const obj = b.addAssemble("test", root_src); |
| 230 | obj.setOutputPath(obj_path); | 323 | obj.setOutputPath(obj_path); |
| 231 | 324 | ||
| 232 | for (case.sources.toSliceConst()) |src_file| { | 325 | for (case.sources.toSliceConst()) |src_file| { |
| 233 | const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename); | 326 | 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); | 327 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); |
| 235 | obj.step.dependOn(&write_src.step); | 328 | obj.step.dependOn(&write_src.step); |
| 236 | } | 329 | } |
| 237 | 330 | ||
| 238 | const exe = b.addLinkExecutable("test"); | 331 | const exe = b.addLinkExecutable("test"); |
| 239 | exe.step.dependOn(&obj.step); | 332 | exe.step.dependOn(&obj.step); |
| 240 | exe.addObjectFile(obj_path); | 333 | exe.addObjectFile(obj_path); |
| 241 | exe.setOutputPath(exe_path); | 334 | exe.setOutputPath(exe_path); |
| 242 | 335 | ||
| 243 | const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name, | 336 | const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name, |
| 244 | case.expected_output); | 337 | case.expected_output); |
| 245 | run_and_cmp_output.step.dependOn(&exe.step); | 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); | 351 | const exe = b.addExecutable("test", root_src); |
| 248 | } else { | 352 | exe.setOutputPath(exe_path); |
| 249 | for ([]bool{false, true}) |release| { | 353 | exe.setRelease(release); |
| 250 | const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})", | 354 | if (case.link_libc) { |
| 251 | case.name, if (release) "release" else "debug"); | 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 | if (const filter ?= self.test_filter) { | 374 | if (const filter ?= self.test_filter) { |
| 253 | if (mem.indexOf(u8, annotated_case_name, filter) == null) | 375 | if (mem.indexOf(u8, annotated_case_name, filter) == null) |
| 254 | continue; | 376 | return; |
| 255 | } | 377 | } |
| 256 | 378 | ||
| 257 | const exe = b.addExecutable("test", root_src); | 379 | const exe = b.addExecutable("test", root_src); |
| 258 | exe.setOutputPath(exe_path); | 380 | exe.setOutputPath(exe_path); |
| 259 | exe.setRelease(release); | ||
| 260 | if (case.link_libc) { | 381 | if (case.link_libc) { |
| 261 | exe.linkLibrary("c"); | 382 | exe.linkLibrary("c"); |
| 262 | } | 383 | } |
| ... | @@ -267,14 +388,12 @@ pub const CompareOutputContext = struct { | ... | @@ -267,14 +388,12 @@ pub const CompareOutputContext = struct { |
| 267 | exe.step.dependOn(&write_src.step); | 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, | 391 | const run_and_cmp_output = DebugSafetyRunStep.create(self, exe_path, annotated_case_name); |
| 271 | case.expected_output); | ||
| 272 | run_and_cmp_output.step.dependOn(&exe.step); | 392 | run_and_cmp_output.step.dependOn(&exe.step); |
| 273 | 393 | ||
| 274 | self.step.dependOn(&run_and_cmp_output.step); | 394 | self.step.dependOn(&run_and_cmp_output.step); |
| 275 | } | 395 | }, |
| 276 | }; | 396 | } |
| 277 | |||
| 278 | } | 397 | } |
| 279 | }; | 398 | }; |
| 280 | 399 |