From 43c0bf11caa525b35aa36cb29346c5af8c8d342b Mon Sep 17 00:00:00 2001 From: David Fifield Date: Sun, 5 Sep 2021 17:27:51 -0600 Subject: [PATCH] Use a runnable prefix in test programs. Print "ok" and exit cleanly, rather than run off the end of a bunch of nops or nil bytes. DOSBox still cannot run some text executables for other reasons, such as (I suspect) large stack pointers or e_minalloc values. I had to do `pub` on the imports of the common module in tests, to avoid dead_code lints. https://github.com/rust-lang/rust/issues/46379 --- tests/common/mod.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ tests/pack.rs | 31 ++++++------------------------- tests/unpack.rs | 4 ++-- 3 files changed, 51 insertions(+), 27 deletions(-) diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 9e354e8..5436a84 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -2,6 +2,7 @@ use std::convert::TryFrom; use std::env; use std::fs; use std::io::{self, Write}; +use std::iter; use std::path; use exe; @@ -22,6 +23,48 @@ pub fn maybe_save_exe>(path: P, exe: &exe::Exe) -> Result<( Ok(()) } +// A short program to appear at the beginning of test programs to make them +// actually runnable in DOS. The bytes correspond to the assembly program +// ``` +// mov dx, (ok+0x100) ; ds points to PSP, 0x100 bytes before cs +// mov ah, 0x09 ; write string to standard output +// int 0x21 +// mov ax, 0x4c00 ; terminate with status 0 +// int 0x21 +// ok: +// db "ok$" +// ``` +const PREFIX: [u8; 15] = *b"\xba\x0c\x01\xb4\x09\xcd\x21\xb8\x00\x4c\xcd\x21ok$"; + +// Generates a stream of bytes that is easily compressible. +fn compressible_iter() -> impl iter::Iterator + Clone { + iter::repeat(b'X') +} + +// Generates a stream of bytes with no repetition, so its size cannot be smaller +// after compression. +fn incompressible_iter() -> impl iter::Iterator + Clone { + b"_-".iter().cloned().cycle() +} + +// Generates a stream of bytes that is somewhat compressible, but not as much as +// those generated by compressible_body. +fn semicompressible_iter() -> impl iter::Iterator + Clone { + compressible_iter().take(16).chain(incompressible_iter().take(16)).cycle() +} + +pub fn compressible_text(len: usize) -> Vec { + PREFIX.iter().cloned().chain(compressible_iter()).take(len).collect() +} + +pub fn incompressible_text(len: usize) -> Vec { + PREFIX.iter().cloned().chain(incompressible_iter()).take(len).collect() +} + +pub fn semicompressible_text(len: usize) -> Vec { + PREFIX.iter().cloned().chain(semicompressible_iter()).take(len).collect() +} + /// Panics if two `Exe`s are not equivalent. /// /// We don't ask for perfect identity. The packing/unpacking roundtrip may diff --git a/tests/pack.rs b/tests/pack.rs index 0359cc7..633960b 100644 --- a/tests/pack.rs +++ b/tests/pack.rs @@ -6,26 +6,7 @@ use exepack_crate::exe; use exepack_crate::exepack; use exepack_crate::pointer::Pointer; -mod common; - -// Generates a stream of bytes that is easily compressible. -fn compressible_body() -> impl iter::Iterator + Clone { - // 90 is a 1-byte NOP. - iter::repeat(0x90) -} - -// Generates a stream of bytes with no repetition, so its size cannot be smaller -// after compression. -fn incompressible_body() -> impl iter::Iterator + Clone { - // 66 90 is a 2-byte NOP. - [0x66, 0x90].iter().cloned().cycle() -} - -// Generates a stream of bytes that is somewhat compressible, but not as much as -// those generated by compressible_body. -fn semicompressible_body() -> impl iter::Iterator + Clone { - compressible_body().take(16).chain(incompressible_body().take(16)).cycle() -} +pub mod common; fn make_exe(body: Vec, relocs: Vec) -> exe::Exe { exe::Exe { @@ -42,22 +23,22 @@ fn make_exe(body: Vec, relocs: Vec) -> exe::Exe { } fn make_relocs(n: usize) -> Vec { - (0..n).map(|address| Pointer { + (0..n).map(|x| x + 256).map(|address| Pointer { segment: (address >> 4) as u16, offset: (address & 0xf) as u16, }).collect() } fn make_compressible_exe(body_len: usize, num_relocs: usize) -> exe::Exe { - make_exe(compressible_body().take(body_len).collect(), make_relocs(num_relocs)) + make_exe(common::compressible_text(body_len), make_relocs(num_relocs)) } fn make_incompressible_exe(body_len: usize, num_relocs: usize) -> exe::Exe { - make_exe(incompressible_body().take(body_len).collect(), make_relocs(num_relocs)) + make_exe(common::incompressible_text(body_len), make_relocs(num_relocs)) } fn make_semicompressible_exe(body_len: usize, num_relocs: usize) -> exe::Exe { - make_exe(semicompressible_body().take(body_len).collect(), make_relocs(num_relocs)) + make_exe(common::semicompressible_text(body_len), make_relocs(num_relocs)) } #[test] @@ -232,7 +213,7 @@ fn test_lengths() { // representing the size of the uncompressed output. { let len = 16 * 0xffff; - // We use a semicompressible_body here, because if we used a highly + // We use a semicompressible text here, because if we used a highly // compressible one, the compressed data would be so much smaller than // the uncompressed data that e_minalloc would become a tighter // constraint than dest_len, because it represents the difference is diff --git a/tests/unpack.rs b/tests/unpack.rs index acd902f..585c2eb 100644 --- a/tests/unpack.rs +++ b/tests/unpack.rs @@ -9,7 +9,7 @@ use exepack_crate::exe; use exepack_crate::exepack; use exepack_crate::pointer::Pointer; -mod common; +pub mod common; pub fn store_u16le(buf: &mut [u8], i: usize, v: u16) { buf[i..i + 2].clone_from_slice(&u16::to_le_bytes(v)); @@ -63,7 +63,7 @@ fn test_minalloc() { let packed = exe::Exe { e_minalloc: 0, ..exepack::pack(&exe::Exe { - body: vec![0; 1000], + body: common::compressible_text(1000), ..unpacked_sample() }).unwrap() };