Skip to content

Commit

Permalink
Use a runnable prefix in test programs.
Browse files Browse the repository at this point in the history
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.
rust-lang/rust#46379
  • Loading branch information
David Fifield committed Sep 5, 2021
1 parent f3629d9 commit 43c0bf1
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 27 deletions.
43 changes: 43 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -22,6 +23,48 @@ pub fn maybe_save_exe<P: AsRef<path::Path>>(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<Item = u8> + 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<Item = u8> + 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<Item = u8> + Clone {
compressible_iter().take(16).chain(incompressible_iter().take(16)).cycle()
}

pub fn compressible_text(len: usize) -> Vec<u8> {
PREFIX.iter().cloned().chain(compressible_iter()).take(len).collect()
}

pub fn incompressible_text(len: usize) -> Vec<u8> {
PREFIX.iter().cloned().chain(incompressible_iter()).take(len).collect()
}

pub fn semicompressible_text(len: usize) -> Vec<u8> {
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
Expand Down
31 changes: 6 additions & 25 deletions tests/pack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item = u8> + 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<Item = u8> + 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<Item = u8> + Clone {
compressible_body().take(16).chain(incompressible_body().take(16)).cycle()
}
pub mod common;

fn make_exe(body: Vec<u8>, relocs: Vec<Pointer>) -> exe::Exe {
exe::Exe {
Expand All @@ -42,22 +23,22 @@ fn make_exe(body: Vec<u8>, relocs: Vec<Pointer>) -> exe::Exe {
}

fn make_relocs(n: usize) -> Vec<Pointer> {
(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]
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/unpack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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()
};
Expand Down

0 comments on commit 43c0bf1

Please sign in to comment.