use itertools::iproduct;
use rusqlite::Connection;
use rusqlite::types::ValueRef;
use testresult::TestResult;
use super::*;
fn sqlite3_parse(conn: &Connection, repr: &str) -> TestResult<Vec<u8>> {
let v = conn.query_row(
&format!("SELECT {repr}"), [],
|row| {
let v: ValueRef = row.get_ref(0)?;
let v = v.as_bytes()?.to_owned();
Ok(v)
}
)?;
Ok(v)
}
fn minidump_print(data: &[u8]) -> TestResult<String> {
let mut repr: Vec<u8> = vec![];
write_text(&mut repr, data)?;
let repr = String::from_utf8(repr)?;
Ok(repr)
}
fn test_roundtrip(
conn: &Connection,
data: &[u8],
debug: &mut impl DebugWrite,
) -> TestResult<()> {
if debug.enabled() {
let s = String::from_utf8(
data.iter()
.map(|c| c.escape_ascii())
.flatten().collect()
)?;
write!(debug, "\"{s}\"\t\t{}\t\t", HexFmt(data))?;
}
let repr = minidump_print(data)?;
writeln!(debug, "{repr}")?;
let reparsed = sqlite3_parse(conn, &repr)?;
assert_eq!(data, reparsed);
Ok(())
}
static CASE_FRAGS_DECOMPOSE: &[&[u8]] = &[
b"\0",
b"\n",
b"\t",
b"\r",
b"'",
b"a",
b" ",
b"abcd",
b"abcdefgh",
&[0xff],
&[0xc3], "é".as_bytes(),
"één".as_bytes(),
&[0xf0, 0x9f, 0x96, 0x8d], &[0xf0, 0x9f, 0x96 ], &[0xf3, 0xa0, 0x81, 0xbf], ];
static CASE_FRAGS_REASSEMBLE: &[&[u8]] = &[
b"'",
b"\0", b"\x7f", &[0xf3, 0xa0, 0x81, 0xbf], b"a",
b"ab",
b"abcd",
b"abcdefgh",
&[0xf0, 0x9f, 0x96, 0x8d], &[0xff],
&[0xc3], ];
fn roundtrips<FRAGS>(
max_l: usize,
#[allow(unused_variables)]
max_l_release: usize,
frags: FRAGS,
debug: &mut impl DebugWrite,
) -> TestResult<()>
where
FRAGS: IntoIterator<Item = &'static &'static [u8]> + Clone,
FRAGS::IntoIter: Clone,
{
let conn = Connection::open_in_memory()?;
#[cfg(not(debug_assertions))]
let max_l = {
let _ = max_l;
max_l_release
};
for l in 0..max_l {
for case in Itertools::multi_cartesian_product(
iter::repeat(frags.clone()).take(l)
) {
let data: Vec<u8> = case.into_iter()
.copied().flatten().copied().collect();
test_roundtrip(&conn, &data, debug)?;
}
}
Ok(())
}
#[test]
fn roundtrips_decompose() -> TestResult<()> {
roundtrips(3, 6, CASE_FRAGS_DECOMPOSE, &mut io::sink())
}
#[test]
fn roundtrips_reassemble() -> TestResult<()> {
roundtrips(5, 7, CASE_FRAGS_REASSEMBLE, &mut io::sink())
}
struct EprintWriter;
impl io::Write for EprintWriter {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
let s = str::from_utf8(data)
.expect("EprintlnWriter can only do UTF-8 in each write!");
eprint!("{s}");
Ok(data.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
#[test]
fn roundtrips_report() -> TestResult<()> {
let conn = Connection::open_in_memory()?;
let mut debug = io::LineWriter::new(EprintWriter);
for (d0, d1) in iproduct!(
[b"", &[0xff][..]],
chain!(
CASE_FRAGS_DECOMPOSE,
CASE_FRAGS_REASSEMBLE,
).unique()
) {
let data: Vec<u8> = chain!(
d0,
d1.into_iter(),
).copied().collect();
test_roundtrip(&conn, &data, &mut debug)?;
}
Ok(())
}