Coverage Report

Created: 2025-12-04 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vtpm-td/deps/spdm-rs/external/ring/src/test.rs
Line
Count
Source
1
// Copyright 2015-2016 Brian Smith.
2
//
3
// Permission to use, copy, modify, and/or distribute this software for any
4
// purpose with or without fee is hereby granted, provided that the above
5
// copyright notice and this permission notice appear in all copies.
6
//
7
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15
//! Testing framework.
16
//!
17
//! Unlike the rest of *ring*, this testing framework uses panics pretty
18
//! liberally. It was originally designed for internal use--it drives most of
19
//! *ring*'s internal tests, and so it is optimized for getting *ring*'s tests
20
//! written quickly at the expense of some usability. The documentation is
21
//! lacking. The best way to learn it is to look at some examples. The digest
22
//! tests are the most complicated because they use named sections. Other tests
23
//! avoid named sections and so are easier to understand.
24
//!
25
//! # Examples
26
//!
27
//! ## Writing Tests
28
//!
29
//! Input files look like this:
30
//!
31
//! ```text
32
//! # This is a comment.
33
//!
34
//! HMAC = SHA1
35
//! Input = "My test data"
36
//! Key = ""
37
//! Output = 61afdecb95429ef494d61fdee15990cabf0826fc
38
//!
39
//! HMAC = SHA256
40
//! Input = "Sample message for keylen<blocklen"
41
//! Key = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F
42
//! Output = A28CF43130EE696A98F14A37678B56BCFCBDD9E5CF69717FECF5480F0EBDF790
43
//! ```
44
//!
45
//! Test cases are separated with blank lines. Note how the bytes of the `Key`
46
//! attribute are specified as a quoted string in the first test case and as
47
//! hex in the second test case; you can use whichever form is more convenient
48
//! and you can mix and match within the same file. The empty sequence of bytes
49
//! can only be represented with the quoted string form (`""`).
50
//!
51
//! Here's how you would consume the test data:
52
//!
53
//! ```ignore
54
//! use ring::test;
55
//!
56
//! test::run(test::test_file!("hmac_tests.txt"), |section, test_case| {
57
//!     assert_eq!(section, ""); // This test doesn't use named sections.
58
//!
59
//!     let digest_alg = test_case.consume_digest_alg("HMAC");
60
//!     let input = test_case.consume_bytes("Input");
61
//!     let key = test_case.consume_bytes("Key");
62
//!     let output = test_case.consume_bytes("Output");
63
//!
64
//!     // Do the actual testing here
65
//! });
66
//! ```
67
//!
68
//! Note that `consume_digest_alg` automatically maps the string "SHA1" to a
69
//! reference to `digest::SHA1_FOR_LEGACY_USE_ONLY`, "SHA256" to
70
//! `digest::SHA256`, etc.
71
//!
72
//! ## Output When a Test Fails
73
//!
74
//! When a test case fails, the framework automatically prints out the test
75
//! case. If the test case failed with a panic, then the backtrace of the panic
76
//! will be printed too. For example, let's say the failing test case looks
77
//! like this:
78
//!
79
//! ```text
80
//! Curve = P-256
81
//! a = 2b11cb945c8cf152ffa4c9c2b1c965b019b35d0b7626919ef0ae6cb9d232f8af
82
//! b = 18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c
83
//! r = 18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c
84
//! ```
85
//! If the test fails, this will be printed (if `$RUST_BACKTRACE` is `1`):
86
//!
87
//! ```text
88
//! src/example_tests.txt: Test panicked.
89
//! Curve = P-256
90
//! a = 2b11cb945c8cf152ffa4c9c2b1c965b019b35d0b7626919ef0ae6cb9d232f8af
91
//! b = 18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c
92
//! r = 18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c
93
//! thread 'example_test' panicked at 'Test failed.', src\test.rs:206
94
//! stack backtrace:
95
//!    0:     0x7ff654a05c7c - std::rt::lang_start::h61f4934e780b4dfc
96
//!    1:     0x7ff654a04f32 - std::rt::lang_start::h61f4934e780b4dfc
97
//!    2:     0x7ff6549f505d - std::panicking::rust_panic_with_hook::hfe203e3083c2b544
98
//!    3:     0x7ff654a0825b - rust_begin_unwind
99
//!    4:     0x7ff6549f63af - std::panicking::begin_panic_fmt::h484cd47786497f03
100
//!    5:     0x7ff654a07e9b - rust_begin_unwind
101
//!    6:     0x7ff654a0ae95 - core::panicking::panic_fmt::h257ceb0aa351d801
102
//!    7:     0x7ff654a0b190 - core::panicking::panic::h4bb1497076d04ab9
103
//!    8:     0x7ff65496dc41 - from_file<closure>
104
//!                         at C:\Users\Example\example\<core macros>:4
105
//!    9:     0x7ff65496d49c - example_test
106
//!                         at C:\Users\Example\example\src\example.rs:652
107
//!   10:     0x7ff6549d192a - test::stats::Summary::new::ha139494ed2e4e01f
108
//!   11:     0x7ff6549d51a2 - test::stats::Summary::new::ha139494ed2e4e01f
109
//!   12:     0x7ff654a0a911 - _rust_maybe_catch_panic
110
//!   13:     0x7ff6549d56dd - test::stats::Summary::new::ha139494ed2e4e01f
111
//!   14:     0x7ff654a03783 - std::sys::thread::Thread::new::h2b08da6cd2517f79
112
//!   15:     0x7ff968518101 - BaseThreadInitThunk
113
//! ```
114
//!
115
//! Notice that the output shows the name of the data file
116
//! (`src/example_tests.txt`), the test inputs that led to the failure, and the
117
//! stack trace to the line in the test code that panicked: entry 9 in the
118
//! stack trace pointing to line 652 of the file `example.rs`.
119
120
extern crate alloc;
121
122
use alloc::{format, string::String, vec::Vec};
123
124
use crate::{bits, digest, error};
125
126
#[cfg(any(feature = "std", feature = "test_logging"))]
127
extern crate std;
128
129
/// `compile_time_assert_clone::<T>();` fails to compile if `T` doesn't
130
/// implement `Clone`.
131
pub fn compile_time_assert_clone<T: Clone>() {}
132
133
/// `compile_time_assert_copy::<T>();` fails to compile if `T` doesn't
134
/// implement `Copy`.
135
pub fn compile_time_assert_copy<T: Copy>() {}
136
137
/// `compile_time_assert_eq::<T>();` fails to compile if `T` doesn't
138
/// implement `Eq`.
139
pub fn compile_time_assert_eq<T: Eq>() {}
140
141
/// `compile_time_assert_send::<T>();` fails to compile if `T` doesn't
142
/// implement `Send`.
143
pub fn compile_time_assert_send<T: Send>() {}
144
145
/// `compile_time_assert_sync::<T>();` fails to compile if `T` doesn't
146
/// implement `Sync`.
147
pub fn compile_time_assert_sync<T: Sync>() {}
148
149
/// `compile_time_assert_std_error_error::<T>();` fails to compile if `T`
150
/// doesn't implement `std::error::Error`.
151
#[cfg(feature = "std")]
152
pub fn compile_time_assert_std_error_error<T: std::error::Error>() {}
153
154
/// A test case. A test case consists of a set of named attributes. Every
155
/// attribute in the test case must be consumed exactly once; this helps catch
156
/// typos and omissions.
157
///
158
/// Requires the `alloc` default feature to be enabled.
159
#[derive(Debug)]
160
pub struct TestCase {
161
    attributes: Vec<(String, String, bool)>,
162
}
163
164
impl TestCase {
165
    /// Maps the string "true" to true and the string "false" to false.
166
    pub fn consume_bool(&mut self, key: &str) -> bool {
167
        match self.consume_string(key).as_ref() {
168
            "true" => true,
169
            "false" => false,
170
            s => panic!("Invalid bool value: {}", s),
171
        }
172
    }
173
174
    /// Maps the strings "SHA1", "SHA256", "SHA384", and "SHA512" to digest
175
    /// algorithms, maps "SHA224" to `None`, and panics on other (erroneous)
176
    /// inputs. "SHA224" is mapped to None because *ring* intentionally does
177
    /// not support SHA224, but we need to consume test vectors from NIST that
178
    /// have SHA224 vectors in them.
179
    pub fn consume_digest_alg(&mut self, key: &str) -> Option<&'static digest::Algorithm> {
180
        let name = self.consume_string(key);
181
        match name.as_ref() {
182
            "SHA1" => Some(&digest::SHA1_FOR_LEGACY_USE_ONLY),
183
            "SHA224" => None, // We actively skip SHA-224 support.
184
            "SHA256" => Some(&digest::SHA256),
185
            "SHA384" => Some(&digest::SHA384),
186
            "SHA512" => Some(&digest::SHA512),
187
            "SHA512_256" => Some(&digest::SHA512_256),
188
            _ => panic!("Unsupported digest algorithm: {}", name),
189
        }
190
    }
191
192
    /// Returns the value of an attribute that is encoded as a sequence of an
193
    /// even number of hex digits, or as a double-quoted UTF-8 string. The
194
    /// empty (zero-length) value is represented as "".
195
    pub fn consume_bytes(&mut self, key: &str) -> Vec<u8> {
196
        self.consume_optional_bytes(key)
197
0
            .unwrap_or_else(|| panic!("No attribute named \"{}\"", key))
198
    }
199
200
    /// Like `consume_bytes()` except it returns `None` if the test case
201
    /// doesn't have the attribute.
202
    pub fn consume_optional_bytes(&mut self, key: &str) -> Option<Vec<u8>> {
203
        let s = self.consume_optional_string(key)?;
204
        let result = if s.starts_with('\"') {
205
            // The value is a quoted UTF-8 string.
206
207
            let mut bytes = Vec::with_capacity(s.as_bytes().len() - 2);
208
            let mut s = s.as_bytes().iter().skip(1);
209
            loop {
210
                let b = match s.next() {
211
                    Some(b'\\') => {
212
                        match s.next() {
213
                            // We don't allow all octal escape sequences, only "\0" for null.
214
                            Some(b'0') => 0u8,
215
                            Some(b't') => b'\t',
216
                            Some(b'n') => b'\n',
217
                            // "\xHH"
218
                            Some(b'x') => {
219
                                let hi = s.next().expect("Invalid hex escape sequence in string.");
220
                                let lo = s.next().expect("Invalid hex escape sequence in string.");
221
                                if let (Ok(hi), Ok(lo)) = (from_hex_digit(*hi), from_hex_digit(*lo))
222
                                {
223
                                    (hi << 4) | lo
224
                                } else {
225
                                    panic!("Invalid hex escape sequence in string.");
226
                                }
227
                            }
228
                            _ => {
229
                                panic!("Invalid hex escape sequence in string.");
230
                            }
231
                        }
232
                    }
233
                    Some(b'"') => {
234
                        if s.next().is_some() {
235
                            panic!("characters after the closing quote of a quoted string.");
236
                        }
237
                        break;
238
                    }
239
                    Some(b) => *b,
240
                    None => panic!("Missing terminating '\"' in string literal."),
241
                };
242
                bytes.push(b);
243
            }
244
            bytes
245
        } else {
246
            // The value is hex encoded.
247
            match from_hex(&s) {
248
                Ok(s) => s,
249
                Err(err_str) => {
250
                    panic!("{} in {}", err_str, s);
251
                }
252
            }
253
        };
254
        Some(result)
255
    }
256
257
    /// Returns the value of an attribute that is an integer, in decimal
258
    /// notation.
259
    pub fn consume_usize(&mut self, key: &str) -> usize {
260
        let s = self.consume_string(key);
261
        s.parse::<usize>().unwrap()
262
    }
263
264
    /// Returns the value of an attribute that is an integer, in decimal
265
    /// notation, as a bit length.
266
    pub fn consume_usize_bits(&mut self, key: &str) -> bits::BitLength {
267
        let s = self.consume_string(key);
268
        let bits = s.parse::<usize>().unwrap();
269
        bits::BitLength::from_usize_bits(bits)
270
    }
271
272
    /// Returns the raw value of an attribute, without any unquoting or
273
    /// other interpretation.
274
    pub fn consume_string(&mut self, key: &str) -> String {
275
        self.consume_optional_string(key)
276
0
            .unwrap_or_else(|| panic!("No attribute named \"{}\"", key))
277
    }
278
279
    /// Like `consume_string()` except it returns `None` if the test case
280
    /// doesn't have the attribute.
281
    pub fn consume_optional_string(&mut self, key: &str) -> Option<String> {
282
        for (name, value, consumed) in &mut self.attributes {
283
            if key == name {
284
                if *consumed {
285
                    panic!("Attribute {} was already consumed", key);
286
                }
287
                *consumed = true;
288
                return Some(value.clone());
289
            }
290
        }
291
        None
292
    }
293
}
294
295
/// References a test input file.
296
#[macro_export]
297
macro_rules! test_file {
298
    ($file_name:expr) => {
299
        $crate::test::File {
300
            file_name: $file_name,
301
            contents: include_str!($file_name),
302
        }
303
    };
304
}
305
306
/// A test input file.
307
pub struct File<'a> {
308
    /// The name (path) of the file.
309
    pub file_name: &'a str,
310
311
    /// The contents of the file.
312
    pub contents: &'a str,
313
}
314
315
/// Parses test cases out of the given file, calling `f` on each vector until
316
/// `f` fails or until all the test vectors have been read. `f` can indicate
317
/// failure either by returning `Err()` or by panicking.
318
pub fn run<F>(test_file: File, mut f: F)
319
where
320
    F: FnMut(&str, &mut TestCase) -> Result<(), error::Unspecified>,
321
{
322
    let lines = &mut test_file.contents.lines();
323
324
    let mut current_section = String::from("");
325
    let mut failed = false;
326
327
    while let Some(mut test_case) = parse_test_case(&mut current_section, lines) {
328
        let result = match f(&current_section, &mut test_case) {
329
            Ok(()) => {
330
                if !test_case
331
                    .attributes
332
                    .iter()
333
                    .any(|&(_, _, consumed)| !consumed)
334
                {
335
                    Ok(())
336
                } else {
337
                    failed = true;
338
                    Err("Test didn't consume all attributes.")
339
                }
340
            }
341
            Err(error::Unspecified) => Err("Test returned Err(error::Unspecified)."),
342
        };
343
344
        if result.is_err() {
345
            failed = true;
346
        }
347
348
        #[cfg(feature = "test_logging")]
349
        {
350
            if let Err(msg) = result {
351
                std::println!("{}: {}", test_file.file_name, msg);
352
353
                for (name, value, consumed) in test_case.attributes {
354
                    let consumed_str = if consumed { "" } else { " (unconsumed)" };
355
                    std::println!("{}{} = {}", name, consumed_str, value);
356
                }
357
            };
358
        }
359
    }
360
361
    if failed {
362
        panic!("Test failed.")
363
    }
364
}
365
366
/// Decode an string of hex digits into a sequence of bytes. The input must
367
/// have an even number of digits.
368
pub fn from_hex(hex_str: &str) -> Result<Vec<u8>, String> {
369
    if hex_str.len() % 2 != 0 {
370
        return Err(String::from(
371
            "Hex string does not have an even number of digits",
372
        ));
373
    }
374
375
    let mut result = Vec::with_capacity(hex_str.len() / 2);
376
    for digits in hex_str.as_bytes().chunks(2) {
377
        let hi = from_hex_digit(digits[0])?;
378
        let lo = from_hex_digit(digits[1])?;
379
        result.push((hi * 0x10) | lo);
380
    }
381
    Ok(result)
382
}
383
384
fn from_hex_digit(d: u8) -> Result<u8, String> {
385
    use core::ops::RangeInclusive;
386
    const DECIMAL: (u8, RangeInclusive<u8>) = (0, b'0'..=b'9');
387
    const HEX_LOWER: (u8, RangeInclusive<u8>) = (10, b'a'..=b'f');
388
    const HEX_UPPER: (u8, RangeInclusive<u8>) = (10, b'A'..=b'F');
389
    for (offset, range) in &[DECIMAL, HEX_LOWER, HEX_UPPER] {
390
        if range.contains(&d) {
391
            return Ok(d - range.start() + offset);
392
        }
393
    }
394
    Err(format!("Invalid hex digit '{}'", d as char))
395
}
396
397
fn parse_test_case(
398
    current_section: &mut String,
399
    lines: &mut dyn Iterator<Item = &str>,
400
) -> Option<TestCase> {
401
    let mut attributes = Vec::new();
402
403
    let mut is_first_line = true;
404
    loop {
405
        let line = lines.next();
406
407
        #[cfg(feature = "test_logging")]
408
        {
409
            if let Some(text) = &line {
410
                std::println!("Line: {}", text);
411
            }
412
        }
413
414
        match line {
415
            // If we get to EOF when we're not in the middle of a test case,
416
            // then we're done.
417
            None if is_first_line => {
418
                return None;
419
            }
420
421
            // End of the file on a non-empty test cases ends the test case.
422
            None => {
423
                return Some(TestCase { attributes });
424
            }
425
426
            // A blank line ends a test case if the test case isn't empty.
427
            Some(line) if line.is_empty() => {
428
                if !is_first_line {
429
                    return Some(TestCase { attributes });
430
                }
431
                // Ignore leading blank lines.
432
            }
433
434
            // Comments start with '#'; ignore them.
435
            Some(line) if line.starts_with('#') => (),
436
437
            Some(line) if line.starts_with('[') => {
438
                assert!(is_first_line);
439
                assert!(line.ends_with(']'));
440
                current_section.truncate(0);
441
                current_section.push_str(line);
442
                let _ = current_section.pop();
443
                let _ = current_section.remove(0);
444
            }
445
446
            Some(line) => {
447
                is_first_line = false;
448
449
                let parts: Vec<&str> = line.splitn(2, " = ").collect();
450
                if parts.len() != 2 {
451
                    panic!("Syntax error: Expected Key = Value.");
452
                };
453
454
                let key = parts[0].trim();
455
                let value = parts[1].trim();
456
457
                // Don't allow the value to be ommitted. An empty value can be
458
                // represented as an empty quoted string.
459
                assert_ne!(value.len(), 0);
460
461
                // Checking is_none() ensures we don't accept duplicate keys.
462
                attributes.push((String::from(key), String::from(value), false));
463
            }
464
        }
465
    }
466
}
467
468
/// Deterministic implementations of `ring::rand::SecureRandom`.
469
///
470
/// These implementations are particularly useful for testing implementations
471
/// of randomized algorithms & protocols using known-answer-tests where the
472
/// test vectors contain the random seed to use. They are also especially
473
/// useful for some types of fuzzing.
474
#[doc(hidden)]
475
pub mod rand {
476
    use crate::{error, rand};
477
478
    /// An implementation of `SecureRandom` that always fills the output slice
479
    /// with the given byte.
480
    #[derive(Debug)]
481
    pub struct FixedByteRandom {
482
        pub byte: u8,
483
    }
484
485
    impl rand::sealed::SecureRandom for FixedByteRandom {
486
        fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
487
            dest.fill(self.byte);
488
            Ok(())
489
        }
490
    }
491
492
    /// An implementation of `SecureRandom` that always fills the output slice
493
    /// with the slice in `bytes`. The length of the slice given to `slice`
494
    /// must match exactly.
495
    #[derive(Debug)]
496
    pub struct FixedSliceRandom<'a> {
497
        pub bytes: &'a [u8],
498
    }
499
500
    impl rand::sealed::SecureRandom for FixedSliceRandom<'_> {
501
        fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
502
            dest.copy_from_slice(self.bytes);
503
            Ok(())
504
        }
505
    }
506
507
    /// An implementation of `SecureRandom` where each slice in `bytes` is a
508
    /// test vector for one call to `fill()`. *Not thread-safe.*
509
    ///
510
    /// The first slice in `bytes` is the output for the first call to
511
    /// `fill()`, the second slice is the output for the second call to
512
    /// `fill()`, etc. The output slice passed to `fill()` must have exactly
513
    /// the length of the corresponding entry in `bytes`. `current` must be
514
    /// initialized to zero. `fill()` must be called exactly once for each
515
    /// entry in `bytes`.
516
    #[derive(Debug)]
517
    pub struct FixedSliceSequenceRandom<'a> {
518
        /// The value.
519
        pub bytes: &'a [&'a [u8]],
520
        pub current: core::cell::UnsafeCell<usize>,
521
    }
522
523
    impl rand::sealed::SecureRandom for FixedSliceSequenceRandom<'_> {
524
        fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
525
            let current = unsafe { *self.current.get() };
526
            let bytes = self.bytes[current];
527
            dest.copy_from_slice(bytes);
528
            // Remember that we returned this slice and prepare to return
529
            // the next one, if any.
530
            unsafe { *self.current.get() += 1 };
531
            Ok(())
532
        }
533
    }
534
535
    impl Drop for FixedSliceSequenceRandom<'_> {
536
0
        fn drop(&mut self) {
537
            // Ensure that `fill()` was called exactly the right number of
538
            // times.
539
0
            assert_eq!(unsafe { *self.current.get() }, self.bytes.len());
540
0
        }
541
    }
542
}
543
544
#[cfg(test)]
545
mod tests {
546
    use crate::{error, test};
547
548
    #[test]
549
    fn one_ok() {
550
        test::run(test_file!("test_1_tests.txt"), |_, test_case| {
551
            let _ = test_case.consume_string("Key");
552
            Ok(())
553
        });
554
    }
555
556
    #[test]
557
    #[should_panic(expected = "Test failed.")]
558
    fn one_err() {
559
        test::run(test_file!("test_1_tests.txt"), |_, test_case| {
560
            let _ = test_case.consume_string("Key");
561
            Err(error::Unspecified)
562
        });
563
    }
564
565
    #[test]
566
    #[should_panic(expected = "Oh noes!")]
567
    fn one_panics() {
568
        test::run(test_file!("test_1_tests.txt"), |_, test_case| {
569
            let _ = test_case.consume_string("Key");
570
            panic!("Oh noes!");
571
        });
572
    }
573
574
    #[test]
575
    #[should_panic(expected = "Test failed.")]
576
    fn first_err() {
577
        err_one(0)
578
    }
579
580
    #[test]
581
    #[should_panic(expected = "Test failed.")]
582
    fn middle_err() {
583
        err_one(1)
584
    }
585
586
    #[test]
587
    #[should_panic(expected = "Test failed.")]
588
    fn last_err() {
589
        err_one(2)
590
    }
591
592
    fn err_one(test_to_fail: usize) {
593
        let mut n = 0;
594
        test::run(test_file!("test_3_tests.txt"), |_, test_case| {
595
            let _ = test_case.consume_string("Key");
596
            let result = if n != test_to_fail {
597
                Ok(())
598
            } else {
599
                Err(error::Unspecified)
600
            };
601
            n += 1;
602
            result
603
        });
604
    }
605
606
    #[test]
607
    #[should_panic(expected = "Oh Noes!")]
608
    fn first_panic() {
609
        panic_one(0)
610
    }
611
612
    #[test]
613
    #[should_panic(expected = "Oh Noes!")]
614
    fn middle_panic() {
615
        panic_one(1)
616
    }
617
618
    #[test]
619
    #[should_panic(expected = "Oh Noes!")]
620
    fn last_panic() {
621
        panic_one(2)
622
    }
623
624
    fn panic_one(test_to_fail: usize) {
625
        let mut n = 0;
626
        test::run(test_file!("test_3_tests.txt"), |_, test_case| {
627
            let _ = test_case.consume_string("Key");
628
            if n == test_to_fail {
629
                panic!("Oh Noes!");
630
            };
631
            n += 1;
632
            Ok(())
633
        });
634
    }
635
636
    #[test]
637
    #[should_panic(expected = "Syntax error: Expected Key = Value.")]
638
    fn syntax_error() {
639
        test::run(test_file!("test_1_syntax_error_tests.txt"), |_, _| Ok(()));
640
    }
641
}