use drcov::{from_reader, Error};
use std::io::Cursor;
#[test]
fn test_malformed_headers() {
let no_version = "DRCOV FLAVOR: test\nModule Table: 0\nBB Table: 0 bbs\n";
assert!(matches!(
from_reader(Cursor::new(no_version)),
Err(Error::InvalidFormat(_))
));
let bad_version = "DRCOV VERSION: invalid\nDRCOV FLAVOR: test\n";
assert!(matches!(
from_reader(Cursor::new(bad_version)),
Err(Error::InvalidFormat(_))
));
let no_flavor = "DRCOV VERSION: 2\nModule Table: 0\nBB Table: 0 bbs\n";
assert!(matches!(
from_reader(Cursor::new(no_flavor)),
Err(Error::InvalidFormat(_))
));
let wrong_prefix = "WRONG VERSION: 2\nDRCOV FLAVOR: test\n";
assert!(matches!(
from_reader(Cursor::new(wrong_prefix)),
Err(Error::InvalidFormat(_))
));
let whitespace =
"DRCOV VERSION: 2 \n DRCOV FLAVOR: test \nModule Table: 0\nBB Table: 0 bbs\n";
assert!(matches!(
from_reader(Cursor::new(whitespace)),
Err(Error::InvalidFormat(_))
));
}
#[test]
fn test_malformed_module_table() {
let bad_count = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: invalid\n";
assert!(matches!(
from_reader(Cursor::new(bad_count)),
Err(Error::InvalidModuleTable(_))
));
let no_module_table = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nBB Table: 0 bbs\n";
assert!(matches!(
from_reader(Cursor::new(no_module_table)),
Err(Error::InvalidModuleTable(_))
));
let bad_versioned = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: version\n";
assert!(matches!(
from_reader(Cursor::new(bad_versioned)),
Err(Error::InvalidModuleTable(_))
));
let missing_count = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: version 2\n";
assert!(matches!(
from_reader(Cursor::new(missing_count)),
Err(Error::InvalidModuleTable(_))
));
let bad_version_num =
"DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: version abc, count 0\n";
assert!(matches!(
from_reader(Cursor::new(bad_version_num)),
Err(Error::InvalidModuleTable(_))
));
let unsupported = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: version 99, count 0\n";
assert!(matches!(
from_reader(Cursor::new(unsupported)),
Err(Error::InvalidModuleTable(_))
));
}
#[test]
fn test_malformed_module_entries() {
let too_few =
"DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000\nBB Table: 0 bbs\n";
assert!(matches!(
from_reader(Cursor::new(too_few)),
Err(Error::InvalidModuleTable(_))
));
let too_many = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, /bin/test, extra\nBB Table: 0 bbs\n";
let result = from_reader(Cursor::new(too_many));
assert!(result.is_ok());
let non_sequential = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 2\n1, 0x400000, 0x500000, 0x401000, /bin/test\n0, 0x600000, 0x700000, 0x601000, /bin/test2\nBB Table: 0 bbs\n";
assert!(matches!(
from_reader(Cursor::new(non_sequential)),
Err(Error::InvalidModuleTable(_))
));
let bad_id = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\nabc, 0x400000, 0x500000, 0x401000, /bin/test\nBB Table: 0 bbs\n";
assert!(matches!(
from_reader(Cursor::new(bad_id)),
Err(Error::InvalidModuleTable(_))
));
let missing_columns = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: version 4, count 1\n0, -1, 0x400000, 0x500000, 0x401000, 0x0, /bin/test\nBB Table: 0 bbs\n";
assert!(matches!(
from_reader(Cursor::new(missing_columns)),
Err(Error::InvalidModuleTable(_))
));
}
#[test]
fn test_malformed_basic_block_table() {
let bad_bb_count = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, /bin/test\nBB Table: invalid bbs\n";
assert!(matches!(
from_reader(Cursor::new(bad_bb_count)),
Err(Error::InvalidBbTable(_))
));
let no_bb_table = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, /bin/test\n";
assert!(from_reader(Cursor::new(no_bb_table)).is_ok());
let wrong_bb_prefix = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, /bin/test\nWRONG Table: 0 bbs\n";
assert!(matches!(
from_reader(Cursor::new(wrong_bb_prefix)),
Err(Error::InvalidBbTable(_))
));
}
#[test]
fn test_truncated_files() {
let truncated_modules = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 2\n0, 0x400000, 0x500000, 0x401000, /bin/test\n";
let result = from_reader(Cursor::new(truncated_modules));
assert!(result.is_err());
let truncated_bb = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, /bin/test\nBB Table: 2 bbs\n";
let mut data = Vec::new();
data.extend_from_slice(truncated_bb.as_bytes());
data.extend_from_slice(&[0x00, 0x10, 0x00, 0x00]); assert!(matches!(from_reader(Cursor::new(data)), Err(Error::Io(_))));
let partial_bb = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, /bin/test\nBB Table: 1 bbs\n";
let mut data = Vec::new();
data.extend_from_slice(partial_bb.as_bytes());
data.extend_from_slice(&[0x00, 0x10, 0x00]); assert!(matches!(from_reader(Cursor::new(data)), Err(Error::Io(_))));
}
#[test]
fn test_hex_parsing_edge_cases() {
let hex_prefix = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x0000000000400000, 0x0000000000500000, 0x0000000000401000, /bin/test\nBB Table: 0 bbs\n";
let coverage = from_reader(Cursor::new(hex_prefix)).unwrap();
assert_eq!(coverage.modules[0].base, 0x400000);
let no_prefix = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 4194304, 5242880, 4198400, /bin/test\nBB Table: 0 bbs\n";
assert!(from_reader(Cursor::new(no_prefix)).is_ok());
let mixed_case = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, /bin/test\nBB Table: 0 bbs\n";
assert!(from_reader(Cursor::new(mixed_case)).is_ok());
}
#[test]
fn test_empty_and_whitespace_handling() {
let empty_path = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, \nBB Table: 0 bbs\n";
let coverage = from_reader(Cursor::new(empty_path)).unwrap();
assert_eq!(coverage.modules[0].path, "");
let spaced_path = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, /path with spaces/test\nBB Table: 0 bbs\n";
let coverage = from_reader(Cursor::new(spaced_path)).unwrap();
assert_eq!(coverage.modules[0].path, "/path with spaces/test");
let extra_spaces = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n 0 , 0x400000 , 0x500000 , 0x401000 , /bin/test \nBB Table: 0 bbs\n";
assert!(from_reader(Cursor::new(extra_spaces)).is_ok());
}
#[test]
fn test_line_ending_variations() {
let windows_endings =
"DRCOV VERSION: 2\r\nDRCOV FLAVOR: test\r\nModule Table: 0\r\nBB Table: 0 bbs\r\n";
let result = from_reader(Cursor::new(windows_endings));
assert!(result.is_err() || result.is_ok());
let mixed_endings =
"DRCOV VERSION: 2\r\nDRCOV FLAVOR: test\nModule Table: 0\r\nBB Table: 0 bbs\n";
let result = from_reader(Cursor::new(mixed_endings));
assert!(result.is_err() || result.is_ok());
let no_final_newline = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 0\nBB Table: 0 bbs";
assert!(from_reader(Cursor::new(no_final_newline)).is_ok());
}
#[test]
fn test_column_mismatch_scenarios() {
let missing_v4_columns = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: version 4, count 1\nColumns: id, containing_id, start, end, entry, path\n0, -1, 0x400000, 0x500000, 0x401000, /bin/test\nBB Table: 0 bbs\n";
assert!(from_reader(Cursor::new(missing_v4_columns)).is_ok());
let extra_columns = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: version 2, count 1\nColumns: id, base, end, entry, path\n0, 0x400000, 0x500000, 0x401000, /bin/test, extra_field\nBB Table: 0 bbs\n";
let result = from_reader(Cursor::new(extra_columns));
assert!(result.is_ok());
let semicolon_columns = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: version 2, count 1\nColumns: id; base; end; entry; path\n0, 0x400000, 0x500000, 0x401000, /bin/test\nBB Table: 0 bbs\n";
assert!(matches!(
from_reader(Cursor::new(semicolon_columns)),
Err(Error::InvalidModuleTable(_))
));
}
#[test]
fn test_special_characters_in_paths() {
let unicode_path = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, /测试/тест/🦀\nBB Table: 0 bbs\n";
let coverage = from_reader(Cursor::new(unicode_path)).unwrap();
assert_eq!(coverage.modules[0].path, "/测试/тест/🦀");
let windows_path = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, C:\\Program Files\\test.exe\nBB Table: 0 bbs\n";
let coverage = from_reader(Cursor::new(windows_path)).unwrap();
assert_eq!(coverage.modules[0].path, "C:\\Program Files\\test.exe");
let comma_path = "DRCOV VERSION: 2\nDRCOV FLAVOR: test\nModule Table: 1\n0, 0x400000, 0x500000, 0x401000, /path,with,commas/test\nBB Table: 0 bbs\n";
let coverage = from_reader(Cursor::new(comma_path)).unwrap();
assert_eq!(coverage.modules[0].path, "/path,with,commas/test");
}