Coverage Report

Created: 2026-03-31 07:09

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