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`.
compile_time_assert_clone<T: 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`.
compile_time_assert_copy<T: 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`.
compile_time_assert_eq<T: 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`.
compile_time_assert_send<T: 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`.
compile_time_assert_sync<T: 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")]
compile_time_assert_std_error_error<T: std::error::Error>()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.
consume_bool(&mut self, key: &str) -> bool166     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.
consume_digest_alg(&mut self, key: &str) -> Option<&'static digest::Algorithm>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 "".
consume_bytes(&mut self, key: &str) -> Vec<u8>195     pub fn consume_bytes(&mut self, key: &str) -> Vec<u8> {
196         self.consume_optional_bytes(key)
197             .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.
consume_optional_bytes(&mut self, key: &str) -> Option<Vec<u8>>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.
consume_usize(&mut self, key: &str) -> usize259     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.
consume_usize_bits(&mut self, key: &str) -> bits::BitLength266     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.
consume_string(&mut self, key: &str) -> String274     pub fn consume_string(&mut self, key: &str) -> String {
275         self.consume_optional_string(key)
276             .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.
consume_optional_string(&mut self, key: &str) -> Option<String>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.
run<F>(test_file: File, mut f: F) where F: FnMut(&str, &mut TestCase) -> Result<(), error::Unspecified>,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.
from_hex(hex_str: &str) -> Result<Vec<u8>, String>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 
from_hex_digit(d: u8) -> Result<u8, String>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 
parse_test_case( current_section: &mut String, lines: &mut dyn Iterator<Item = &str>, ) -> Option<TestCase>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 {
fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>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<'_> {
fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>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<'_> {
fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>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<'_> {
drop(&mut self)536         fn drop(&mut self) {
537             // Ensure that `fill()` was called exactly the right number of
538             // times.
539             assert_eq!(unsafe { *self.current.get() }, self.bytes.len());
540         }
541     }
542 }
543 
544 #[cfg(test)]
545 mod tests {
546     use crate::{error, test};
547 
548     #[test]
one_ok()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.")]
one_err()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!")]
one_panics()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.")]
first_err()576     fn first_err() {
577         err_one(0)
578     }
579 
580     #[test]
581     #[should_panic(expected = "Test failed.")]
middle_err()582     fn middle_err() {
583         err_one(1)
584     }
585 
586     #[test]
587     #[should_panic(expected = "Test failed.")]
last_err()588     fn last_err() {
589         err_one(2)
590     }
591 
err_one(test_to_fail: usize)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!")]
first_panic()608     fn first_panic() {
609         panic_one(0)
610     }
611 
612     #[test]
613     #[should_panic(expected = "Oh Noes!")]
middle_panic()614     fn middle_panic() {
615         panic_one(1)
616     }
617 
618     #[test]
619     #[should_panic(expected = "Oh Noes!")]
last_panic()620     fn last_panic() {
621         panic_one(2)
622     }
623 
panic_one(test_to_fail: usize)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.")]
syntax_error()638     fn syntax_error() {
639         test::run(test_file!("test_1_syntax_error_tests.txt"), |_, _| Ok(()));
640     }
641 }
642