use drcov::{from_reader, to_writer, CoverageData, ModuleTableVersion};
use std::io::Cursor;
use tempfile::NamedTempFile;
#[test]
fn test_file_roundtrip() {
let original = CoverageData::builder()
.flavor("integration_test")
.module_version(ModuleTableVersion::V4)
.add_module("/usr/bin/test", 0x400000, 0x500000)
.add_module(
"/lib/x86_64-linux-gnu/libc.so.6",
0x7ffff7a00000,
0x7ffff7c00000,
)
.add_coverage(0, 0x1000, 32)
.add_coverage(0, 0x2000, 16)
.add_coverage(1, 0x10000, 64)
.build()
.unwrap();
let temp_file = NamedTempFile::new().unwrap();
drcov::to_file(&original, temp_file.path()).unwrap();
let parsed = drcov::from_file(temp_file.path()).unwrap();
assert_eq!(original.header.version, parsed.header.version);
assert_eq!(original.header.flavor, parsed.header.flavor);
assert_eq!(original.module_version, parsed.module_version);
assert_eq!(original.modules.len(), parsed.modules.len());
assert_eq!(original.basic_blocks.len(), parsed.basic_blocks.len());
for (orig_mod, parsed_mod) in original.modules.iter().zip(parsed.modules.iter()) {
assert_eq!(orig_mod.id, parsed_mod.id);
assert_eq!(orig_mod.base, parsed_mod.base);
assert_eq!(orig_mod.end, parsed_mod.end);
assert_eq!(orig_mod.path, parsed_mod.path);
}
for (orig_bb, parsed_bb) in original.basic_blocks.iter().zip(parsed.basic_blocks.iter()) {
assert_eq!(orig_bb.start, parsed_bb.start);
assert_eq!(orig_bb.size, parsed_bb.size);
assert_eq!(orig_bb.module_id, parsed_bb.module_id);
}
}
#[test]
fn test_legacy_format_support() {
let legacy_drcov = "DRCOV VERSION: 2
DRCOV FLAVOR: legacy_test
Module Table: 2
0, 0x0000000000400000, 0x0000000000500000, 0x0000000000401000, /usr/bin/test
1, 0x00007ffff7a00000, 0x00007ffff7c00000, 0x00007ffff7a01000, /lib/x86_64-linux-gnu/libc.so.6
BB Table: 1 bbs
";
let mut data = Vec::new();
data.extend_from_slice(legacy_drcov.as_bytes());
data.extend_from_slice(&0x1000u32.to_le_bytes()); data.extend_from_slice(&32u16.to_le_bytes()); data.extend_from_slice(&0u16.to_le_bytes());
let coverage = from_reader(Cursor::new(data)).unwrap();
assert_eq!(coverage.header.flavor, "legacy_test");
assert_eq!(coverage.module_version, ModuleTableVersion::Legacy);
assert_eq!(coverage.modules.len(), 2);
assert_eq!(coverage.basic_blocks.len(), 1);
assert_eq!(coverage.modules[0].path, "/usr/bin/test");
assert_eq!(coverage.modules[1].path, "/lib/x86_64-linux-gnu/libc.so.6");
}
#[test]
fn test_v2_module_table() {
let v2_drcov = "DRCOV VERSION: 2
DRCOV FLAVOR: v2_test
Module Table: version 2, count 1
Columns: id, base, end, entry, checksum, timestamp, path
0, 0x0000000000400000, 0x0000000000500000, 0x0000000000401000, 0x12345678, 0x87654321, /usr/bin/test
BB Table: 0 bbs
";
let coverage = from_reader(Cursor::new(v2_drcov)).unwrap();
assert_eq!(coverage.module_version, ModuleTableVersion::V2);
assert_eq!(coverage.modules[0].checksum, Some(0x12345678));
assert_eq!(coverage.modules[0].timestamp, Some(0x87654321));
}
#[test]
fn test_v3_module_table() {
let v3_drcov = "DRCOV VERSION: 2
DRCOV FLAVOR: v3_test
Module Table: version 3, count 1
Columns: id, containing_id, start, end, entry, path
0, -1, 0x0000000000400000, 0x0000000000500000, 0x0000000000401000, /usr/bin/test
BB Table: 0 bbs
";
let coverage = from_reader(Cursor::new(v3_drcov)).unwrap();
assert_eq!(coverage.module_version, ModuleTableVersion::V3);
assert_eq!(coverage.modules[0].containing_id, Some(-1));
}
#[test]
fn test_large_file_handling() {
let mut builder = CoverageData::builder()
.flavor("large_test")
.module_version(ModuleTableVersion::V4);
for i in 0..100 {
let base = 0x400000 + i * 0x100000;
builder = builder.add_module(&format!("/usr/lib/module_{i}.so"), base, base + 0x50000);
}
for i in 0..1000 {
let module_id = (i % 100) as u16;
let offset = 0x1000 + (i / 100) * 0x100;
builder = builder.add_coverage(module_id, offset as u32, 32);
}
let coverage = builder.build().unwrap();
assert_eq!(coverage.modules.len(), 100);
assert_eq!(coverage.basic_blocks.len(), 1000);
let mut buffer = Vec::new();
to_writer(&coverage, &mut buffer).unwrap();
let parsed = from_reader(Cursor::new(buffer)).unwrap();
assert_eq!(coverage.modules.len(), parsed.modules.len());
assert_eq!(coverage.basic_blocks.len(), parsed.basic_blocks.len());
}
#[test]
fn test_empty_basic_blocks() {
let empty_bb_drcov = "DRCOV VERSION: 2
DRCOV FLAVOR: empty_test
Module Table: 1
0, 0x0000000000400000, 0x0000000000500000, 0x0000000000401000, /usr/bin/test
BB Table: 0 bbs
";
let coverage = from_reader(Cursor::new(empty_bb_drcov)).unwrap();
assert_eq!(coverage.basic_blocks.len(), 0);
assert_eq!(coverage.modules.len(), 1);
}
#[test]
fn test_error_handling() {
let invalid_version = "DRCOV VERSION: 999
DRCOV FLAVOR: test
";
assert!(from_reader(Cursor::new(invalid_version)).is_err());
let no_header = "NOT A DRCOV FILE
";
assert!(from_reader(Cursor::new(no_header)).is_err());
let truncated = "DRCOV VERSION: 2
DRCOV FLAVOR: test
Module Table: 1
";
assert!(from_reader(Cursor::new(truncated)).is_err());
}
#[test]
fn test_coverage_analysis() {
let coverage = CoverageData::builder()
.add_module("/bin/main", 0x400000, 0x500000)
.add_module("/lib/helper.so", 0x7fff00000000, 0x7fff00100000)
.add_coverage(0, 0x1000, 32)
.add_coverage(0, 0x2000, 16)
.add_coverage(0, 0x3000, 64)
.add_coverage(1, 0x5000, 8)
.build()
.unwrap();
let stats = coverage.get_coverage_stats();
assert_eq!(stats.get(&0), Some(&3)); assert_eq!(stats.get(&1), Some(&1));
let main_module = coverage.find_module_by_address(0x420000).unwrap();
assert_eq!(main_module.path, "/bin/main");
let helper_module = coverage.find_module_by_address(0x7fff00050000).unwrap();
assert_eq!(helper_module.path, "/lib/helper.so");
assert!(coverage.find_module_by_address(0x600000).is_none());
}
#[test]
fn test_builder_validation() {
let invalid_coverage = CoverageData::builder()
.add_module("/bin/test", 0x400000, 0x500000)
.add_coverage(1, 0x1000, 32) .build();
assert!(invalid_coverage.is_err());
}