#[cfg(feature = "use_heap")]
use bits;
use {digest, error};
use std;
use std::string::String;
use std::vec::Vec;
use std::io::BufRead;
pub struct TestCase {
attributes: Vec<(String, String, bool)>,
}
impl TestCase {
pub fn consume_digest_alg(&mut self, key: &str)
-> Option<&'static digest::Algorithm> {
let name = self.consume_string(key);
match name.as_ref() {
"SHA1" => Some(&digest::SHA1),
"SHA224" => None, "SHA256" => Some(&digest::SHA256),
"SHA384" => Some(&digest::SHA384),
"SHA512" => Some(&digest::SHA512),
_ => panic!("Unsupported digest algorithm: {}", name),
}
}
pub fn consume_bytes(&mut self, key: &str) -> Vec<u8> {
let mut s = self.consume_string(key);
if s.starts_with('\"') {
if !s.ends_with('\"') {
panic!("expected quoted string, found {}", s);
}
let _ = s.pop();
let _ = s.remove(0);
Vec::from(s.as_bytes())
} else {
match from_hex(&s) {
Ok(s) => s,
Err(ref err_str) => {
panic!("{} in {}", err_str, s);
},
}
}
}
pub fn consume_usize(&mut self, key: &str) -> usize {
let s = self.consume_string(key);
s.parse::<usize>().unwrap()
}
#[cfg(feature = "use_heap")]
pub fn consume_usize_bits(&mut self, key: &str) -> bits::BitLength {
let s = self.consume_string(key);
let bits = s.parse::<usize>().unwrap();
bits::BitLength::from_usize_bits(bits)
}
pub fn consume_string(&mut self, key: &str) -> String {
self.consume_optional_string(key)
.unwrap_or_else(|| panic!("No attribute named \"{}\"", key))
}
pub fn consume_optional_string(&mut self, key: &str) -> Option<String> {
for &mut (ref name, ref value, ref mut consumed) in
&mut self.attributes {
if key == name {
if *consumed {
panic!("Attribute {} was already consumed", key);
}
*consumed = true;
return Some(value.clone());
}
}
None
}
}
#[cfg(target_os = "ios")]
pub fn ring_src_path() -> std::path::PathBuf {
std::env::current_exe().unwrap().parent().unwrap().join("src")
}
#[cfg(not(target_os = "ios"))]
pub fn ring_src_path() -> std::path::PathBuf {
std::path::PathBuf::from(".")
}
pub fn from_file<F>(test_data_relative_file_path: &str, mut f: F)
where F: FnMut(&str, &mut TestCase)
-> Result<(), error::Unspecified> {
let path = ring_src_path().join(test_data_relative_file_path);
let file = std::fs::File::open(path).unwrap();
let mut lines = std::io::BufReader::new(&file).lines();
let mut current_section = String::from("");
let mut failed = false;
while let Some(mut test_case) = parse_test_case(&mut current_section,
&mut lines) {
let result =
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
f(¤t_section, &mut test_case)
}));
let result = match result {
Ok(Ok(())) => {
if !test_case.attributes.iter().any(
|&(_, _, ref consumed)| !consumed) {
Ok(())
} else {
failed = true;
Err("Test didn't consume all attributes.")
}
},
Ok(Err(_)) => Err("Test returned Err(error::Unspecified)."),
Err(_) => Err("Test panicked."),
};
if let Err(msg) = result {
failed = true;
println!("{}: {}", test_data_relative_file_path, msg);
for (ref name, ref value, ref consumed) in test_case.attributes {
let consumed_str = if *consumed { "" } else { " (unconsumed)" };
println!("{}{} = {}", name, consumed_str, value);
}
};
}
if failed {
panic!("Test failed.")
}
}
pub fn from_hex(hex_str: &str) -> Result<Vec<u8>, String> {
if hex_str.len() % 2 != 0 {
return Err(
String::from("Hex string does not have an even number of digits"));
}
fn from_hex_digit(d: u8) -> Result<u8, String> {
if d >= b'0' && d <= b'9' {
Ok(d - b'0')
} else if d >= b'a' && d <= b'f' {
Ok(d - b'a' + 10u8)
} else if d >= b'A' && d <= b'F' {
Ok(d - b'A' + 10u8)
} else {
Err(format!("Invalid hex digit '{}'", d as char))
}
}
let mut result = Vec::with_capacity(hex_str.len() / 2);
for digits in hex_str.as_bytes().chunks(2) {
let hi = try!(from_hex_digit(digits[0]));
let lo = try!(from_hex_digit(digits[1]));
result.push((hi * 0x10) | lo);
}
Ok(result)
}
type FileLines<'a> = std::io::Lines<std::io::BufReader<&'a std::fs::File>>;
fn parse_test_case(current_section: &mut String, lines: &mut FileLines)
-> Option<TestCase> {
let mut attributes = Vec::new();
let mut is_first_line = true;
loop {
let line = match lines.next() {
None => None,
Some(result) => Some(result.unwrap()),
};
if cfg!(feature = "test_logging") {
if let Some(ref text) = line {
println!("Line: {}", text);
}
}
match line {
None if is_first_line => {
return None;
},
None => {
return Some(TestCase { attributes });
},
Some(ref line) if line.is_empty() => {
if !is_first_line {
return Some(TestCase { attributes });
}
},
Some(ref line) if line.starts_with('#') => {},
Some(ref line) if line.starts_with('[') => {
assert!(is_first_line);
assert!(line.ends_with(']'));
current_section.truncate(0);
current_section.push_str(line);
let _ = current_section.pop();
let _ = current_section.remove(0);
},
Some(ref line) => {
is_first_line = false;
let parts: Vec<&str> = line.splitn(2, " = ").collect();
if parts.len() != 2 {
panic!("Syntax error: Expected Key = Value.");
};
let key = parts[0].trim();
let value = parts[1].trim();
assert_ne!(value.len(), 0);
attributes.push((String::from(key), String::from(value), false));
},
}
}
}
#[allow(missing_docs)]
pub mod rand {
use core;
use {error, polyfill, rand};
pub struct FixedByteRandom {
pub byte: u8,
}
impl rand::SecureRandom for FixedByteRandom {
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
polyfill::slice::fill(dest, self.byte);
Ok(())
}
}
pub struct FixedSliceRandom<'a> {
pub bytes: &'a [u8],
}
impl<'a> rand::SecureRandom for FixedSliceRandom<'a> {
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
dest.copy_from_slice(self.bytes);
Ok(())
}
}
pub struct FixedSliceSequenceRandom<'a> {
pub bytes: &'a [&'a [u8]],
pub current: core::cell::UnsafeCell<usize>,
}
impl<'a> rand::SecureRandom for FixedSliceSequenceRandom<'a> {
fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
let current = unsafe { *self.current.get() };
let bytes = self.bytes[current];
dest.copy_from_slice(bytes);
unsafe { *self.current.get() += 1 };
Ok(())
}
}
impl<'a> Drop for FixedSliceSequenceRandom<'a> {
fn drop(&mut self) {
assert_eq!(unsafe { *self.current.get() }, self.bytes.len());
}
}
}
#[cfg(test)]
mod tests {
use {error, test};
#[test]
fn one_ok() {
test::from_file("src/test_1_tests.txt", |_, test_case| {
let _ = test_case.consume_string("Key");
Ok(())
});
}
#[test]
#[should_panic(expected = "Test failed.")]
fn one_err() {
test::from_file("src/test_1_tests.txt", |_, test_case| {
let _ = test_case.consume_string("Key");
Err(error::Unspecified)
});
}
#[test]
#[should_panic(expected = "Test failed.")]
fn one_panics() {
test::from_file("src/test_1_tests.txt", |_, test_case| {
let _ = test_case.consume_string("Key");
panic!("");
});
}
#[test]
#[should_panic(expected = "Test failed.")]
fn first_err() { err_one(0) }
#[test]
#[should_panic(expected = "Test failed.")]
fn middle_err() { err_one(1) }
#[test]
#[should_panic(expected = "Test failed.")]
fn last_err() { err_one(2) }
fn err_one(test_to_fail: usize) {
let mut n = 0;
test::from_file("src/test_3_tests.txt", |_, test_case| {
let _ = test_case.consume_string("Key");
let result = if n != test_to_fail {
Ok(())
} else {
Err(error::Unspecified)
};
n += 1;
result
});
}
#[test]
#[should_panic(expected = "Test failed.")]
fn first_panic() { panic_one(0) }
#[test]
#[should_panic(expected = "Test failed.")]
fn middle_panic() { panic_one(1) }
#[test]
#[should_panic(expected = "Test failed.")]
fn last_panic() { panic_one(2) }
fn panic_one(test_to_fail: usize) {
let mut n = 0;
test::from_file("src/test_3_tests.txt", |_, test_case| {
let _ = test_case.consume_string("Key");
if n == test_to_fail {
panic!("Oh Noes!");
};
n += 1;
Ok(())
});
}
#[test]
#[should_panic(expected = "Syntax error: Expected Key = Value.")]
fn syntax_error() {
test::from_file("src/test_1_syntax_error_tests.txt", |_, _| Ok(()));
}
#[test]
#[should_panic]
fn file_not_found() {
test::from_file("src/test_file_not_found_tests.txt", |_, _| Ok(()));
}
}