From 4c590a0dc23e50d9647f4209397d077b5b7e052f Mon Sep 17 00:00:00 2001 From: Marrub Date: Tue, 5 Feb 2019 00:12:10 -0500 Subject: [PATCH] add unit tests --- src/durandal/crc.rs | 6 + src/durandal/fx32.rs | 87 +++++----- src/durandal/image.rs | 2 +- src/durandal/machead.rs | 25 +-- src/durandal/mod.rs | 1 - src/durandal/text.rs | 39 ++++- src/lib.rs | 5 + src/main.rs | 10 +- src/marathon/map.rs | 5 +- src/marathon/mod.rs | 3 +- src/{durandal => marathon}/pict.rs | 24 +-- tests/clut.in | Bin 0 -> 2056 bytes tests/clut.out | 258 +++++++++++++++++++++++++++++ tests/clut.rs | 13 ++ 14 files changed, 393 insertions(+), 85 deletions(-) create mode 100644 src/lib.rs rename src/{durandal => marathon}/pict.rs (93%) create mode 100644 tests/clut.in create mode 100644 tests/clut.out create mode 100644 tests/clut.rs diff --git a/src/durandal/crc.rs b/src/durandal/crc.rs index 02b163a..24dd708 100644 --- a/src/durandal/crc.rs +++ b/src/durandal/crc.rs @@ -17,4 +17,10 @@ pub fn crc32(b: &[u8], s: u32) -> u32 !b.iter().fold(s, |a, &o| {a >> 8 ^ t[(a & 0xff ^ o as u32) as usize]}) } +#[test] +fn crc32_lorem_ipsum() +{ + assert_eq!(crc32(b"Lorem ipsum dolor sit amet", !0), 0x5F29D461); +} + // EOF diff --git a/src/durandal/fx32.rs b/src/durandal/fx32.rs index 1f39093..d96a952 100644 --- a/src/durandal/fx32.rs +++ b/src/durandal/fx32.rs @@ -6,52 +6,44 @@ impl Fx32 { const FRACBITS: u32 = 16; const FRACMASK: u32 = 0xFFFF; - const ONE: i32 = 1 << Fx32::FRACBITS; + const ONE: i32 = 1 << Fx32::FRACBITS; - pub fn to_bits(&self) -> u32 {self.0 as u32} - pub fn set_bits(&mut self, bits: u32) {self.0 = bits as i32} - pub fn from_bits(bits: u32) -> Fx32 {Fx32(bits as i32)} - pub fn integ(&self) -> i16 {(self.0 >> 16) as i16} + pub fn to_bits(&self) -> u32 {self.0 as u32} + pub fn set_bits(&mut self, bits: u32) {self.0 = bits as i32} + pub fn from_bits(bits: u32) -> Fx32 {Fx32(bits as i32)} + + pub fn integ(&self) -> i16 {(self.0 >> Fx32::FRACBITS) as i16} pub fn fract(&self) -> u16 {(self.0 as u32 & Fx32::FRACMASK) as u16} + + pub fn mul_i(&self, n: i32) -> Fx32 {Fx32(self.0 * n)} + pub fn div_i(&self, n: i32) -> Fx32 {Fx32(self.0 / n)} } +impl From for Fx32 {fn from(n: i32) -> Fx32 {Fx32(n << Fx32::FRACBITS)}} + impl ops::Add for Fx32 -{ - type Output = Fx32; - fn add(self, other: Fx32) -> Fx32 {Fx32(self.0 + other.0)} -} - + {type Output = Fx32; fn add(self, o: Fx32) -> Fx32 {Fx32(self.0 + o.0)}} impl ops::Sub for Fx32 -{ - type Output = Fx32; - fn sub(self, other: Fx32) -> Fx32 {Fx32(self.0 - other.0)} -} + {type Output = Fx32; fn sub(self, o: Fx32) -> Fx32 {Fx32(self.0 - o.0)}} impl ops::Mul for Fx32 { type Output = Fx32; - fn mul(self, other: Fx32) -> Fx32 - {Fx32((self.0 as i64 * other.0 as i64 / Fx32::ONE as i64) as i32)} + fn mul(self, o: Fx32) -> Fx32 + {Fx32((self.0 as i64 * o.0 as i64 / Fx32::ONE as i64) as i32)} } impl ops::Div for Fx32 { type Output = Fx32; - fn div(self, other: Fx32) -> Fx32 - {Fx32((self.0 as i64 * Fx32::ONE as i64 / other.0 as i64) as i32)} + fn div(self, o: Fx32) -> Fx32 + {Fx32((self.0 as i64 * Fx32::ONE as i64 / o.0 as i64) as i32)} } impl ops::Neg for Fx32 -{ - type Output = Fx32; - fn neg(self) -> Fx32 {Fx32(-self.0)} -} - + {type Output = Fx32; fn neg(self) -> Fx32 {Fx32(-self.0)}} impl ops::Not for Fx32 -{ - type Output = Fx32; - fn not(self) -> Fx32 {Fx32(!self.0)} -} + {type Output = Fx32; fn not(self) -> Fx32 {Fx32(!self.0)}} impl fmt::Display for Fx32 { @@ -77,22 +69,35 @@ impl fmt::Display for Fx32 impl fmt::Debug for Fx32 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result - { - let prec = f.precision().unwrap_or(1); - let widt = f.width().unwrap_or(0); - write!(f, "{:widt$}.", self.integ(), widt = widt)?; + {fmt::Display::fmt(self, f)} +} - let mut k = self.to_bits(); - for _ in 0..prec { - k &= Fx32::FRACMASK; - k *= 10; - let d = k >> Fx32::FRACBITS; - let d = d % 10; - f.write_char((d as u8 + b'0') as char)?; - } +#[test] +fn fx32_basic_ops() +{ + let seven_div_2 = 3 << Fx32::FRACBITS | Fx32::FRACMASK / 2 + 1; + assert_eq!((Fx32::from(1) + 1.into()).to_bits(), 2 << Fx32::FRACBITS); + assert_eq!((Fx32::from(2) - 1.into()).to_bits(), 1 << Fx32::FRACBITS); + assert_eq!((Fx32::from(6) * 2.into()).to_bits(), 12 << Fx32::FRACBITS); + assert_eq!((Fx32::from(6).mul_i(2) ).to_bits(), 12 << Fx32::FRACBITS); + assert_eq!((Fx32::from(7) / 2.into()).to_bits(), seven_div_2); + assert_eq!((Fx32::from(7).div_i(2) ).to_bits(), seven_div_2); +} - Ok(()) - } +#[test] +#[should_panic] +#[allow(unused_must_use)] +fn fx32_overflow() +{ + Fx32::from(32767) + 1.into(); +} + +#[test] +fn fx32_printing() +{ + assert_eq!(format!("{}", Fx32::from(6)), "6.0"); + assert_eq!(format!("{}", Fx32::from(7).div_i(2)), "3.5"); + assert_eq!(format!("{:7.7}", Fx32::from_bits(0xDEAD_BEEF)), " -8531.7458343"); } // EOF diff --git a/src/durandal/image.rs b/src/durandal/image.rs index 7aecbc8..2418b0b 100644 --- a/src/durandal/image.rs +++ b/src/durandal/image.rs @@ -3,7 +3,7 @@ use std::ops::{Index, IndexMut}; /// RGBA8 color. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Color { pub r: u8, diff --git a/src/durandal/machead.rs b/src/durandal/machead.rs index 95e9b31..dc321df 100644 --- a/src/durandal/machead.rs +++ b/src/durandal/machead.rs @@ -5,29 +5,33 @@ use crate::durandal::bin::*; /// Checks for an AppleSingle header. Returns offset to the resource fork. pub fn check_apple_single(b: &[u8]) -> Option { - if b.o_u32b(0)? != 0x51600 || b.o_u32b(4)? != 0x20000 {return None;} + // check magic numbers + if b[0..8] != [0, 5, 22, 0, 0, 2, 0, 0] {return None;} + // get the resource fork (entity 1) let num = b.o_u16b(24)? as usize; - for i in 0..num { - let p = 26 + (12 * i); - let fid = b.o_u32b(p )?; + let p = 26 + 12 * i; + let ent = b.o_u32b(p )?; let ofs = b.o_u32b(p+4)? as usize; let len = b.o_u32b(p+8)? as usize; - if fid == 1 {return if ofs + len > b.len() {None} else {Some(ofs)};} + if ent == 1 {return if ofs + len > b.len() {None} else {Some(ofs)};} } + // no resource fork None } -/// Checks for a MacBin header. Returns offset to the resource fork. -pub fn check_mac_bin(b: &[u8]) -> Option +/// Checks for a MacBinary II header. Returns offset to the resource fork. +pub fn check_macbin(b: &[u8]) -> Option { - if b[0] != 0 || b[1] > 63 || b[74] != 0 || b[123] > 0x81 {return None;} + // check legacy version, length, zero fill, and macbin2 version + if b[0] != 0 || b[1] > 63 || b[74] != 0 || b[123] > 129 {return None;} let mut crc = 0; + // check header crc for i in 0..124 { for j in 8..16 { let d = b[i] as (u16) << j; @@ -36,6 +40,7 @@ pub fn check_mac_bin(b: &[u8]) -> Option } } + // if ok, resource fork follows if crc == b.o_u16b(124)? {Some(128)} else {None} } @@ -43,8 +48,8 @@ pub fn check_mac_bin(b: &[u8]) -> Option /// offset from the start of the header to the resource fork (if one is found.) pub fn try_mac_header(b: &[u8]) -> usize { - let ofs = check_mac_bin(b).unwrap_or(0); - if ofs != 0 {ofs} else {check_apple_single(b).unwrap_or(0)} + if let Some(ofs) = check_macbin(b) {ofs} + else {check_apple_single(b).unwrap_or(0)} } // EOF diff --git a/src/durandal/mod.rs b/src/durandal/mod.rs index 45abb5d..6dd1ac0 100644 --- a/src/durandal/mod.rs +++ b/src/durandal/mod.rs @@ -8,7 +8,6 @@ pub mod err; pub mod fx32; pub mod image; pub mod machead; -pub mod pict; pub mod text; /// Creates an enumeration and function for converting a representation into diff --git a/src/durandal/text.rs b/src/durandal/text.rs index 907db7b..830a089 100644 --- a/src/durandal/text.rs +++ b/src/durandal/text.rs @@ -1,26 +1,23 @@ //! Text conversion utilities. -/// Dumps a slice of memory as text to stdout. +/// Dumps a slice of memory as text to stderr. pub fn dump_mem(b: &[u8]) { let mut p = 0; for &c in b { if p + 3 > 79 { - println!(""); + eprintln!(""); p = 0; } - if c.is_ascii_graphic() { - print!(" {} ", c as char); - } else { - print!("{:02X} ", c); - } + if c.is_ascii_graphic() {eprint!(" {} ", c as char);} + else {eprint!("{:02X} ", c);} p += 3; } - println!(""); + eprintln!(""); } /// Formats a binary size string for any given number. @@ -32,7 +29,7 @@ pub fn to_binsize(n: u64) -> String if n == 0 {return String::from("empty");} // terabytes, gigabytes, megabytes, kilobytes - for i in 4..=1 { + for i in (1..=4).rev() { if n >= 1000u64.pow(i) { let x = n as f64 / 1000f64.powi(i as i32); return format!("{:1}{}", x, NAMES[i as usize - 1]) @@ -106,4 +103,28 @@ const TR: [char; 128] = [ '\u{00b8}', '\u{02dd}', '\u{02db}', '\u{02c7}' ]; +#[test] +fn to_binsize_integrals() +{ + assert_eq!(to_binsize(0), "empty"); + assert_eq!(to_binsize(1), "1 byte"); + assert_eq!(to_binsize(2), "2 bytes"); + assert_eq!(to_binsize(999), "999 bytes"); + assert_eq!(to_binsize(1000), "1kB"); + assert_eq!(to_binsize(1000 * 7), "7kB"); + assert_eq!(to_binsize(1000 * 1000), "1MB"); + assert_eq!(to_binsize(1000 * 1000 * 7), "7MB"); + assert_eq!(to_binsize(1000 * 1000 * 1000), "1GB"); + assert_eq!(to_binsize(1000 * 1000 * 1000 * 7), "7GB"); + assert_eq!(to_binsize(1000 * 1000 * 1000 * 1000), "1TB"); + assert_eq!(to_binsize(1000 * 1000 * 1000 * 1000 * 7), "7TB"); +} + +#[test] +fn mac_roman_conv_basic_marathon_stuff() +{ + assert_eq!(mac_roman_conv(b"p\x8cth"), "påth"); + assert_eq!(mac_roman_conv(b"I\xd5ve"), "I’ve"); +} + // EOF diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..c983e0d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,5 @@ +#[macro_use] +pub mod durandal; +pub mod marathon; + +// EOF diff --git a/src/main.rs b/src/main.rs index d0bddef..2818567 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,5 @@ -#[macro_use] -pub mod durandal; -pub mod marathon; - -use crate::durandal::{chunk::*, err::*, image::Image, pict::load_pict}; -use crate::marathon::{map, wad, term}; +use maraiah::durandal::{chunk::*, err::*, image::Image}; +use maraiah::marathon::{map, pict, term, wad}; use memmap::Mmap; use std::{io, io::Write, fs, env}; @@ -35,7 +31,7 @@ fn main() -> ResultS<()> for (id, ent) in wad.entries { if let Some(b) = ent.chunks.get(b"PICT") { - let im = load_pict(b)?; + let im = pict::load_pict(b)?; println!("entry {} has PICT {}x{}", id, im.w(), im.h()); write_ppm(&format!("out_{}.ppm", id), &im)?; } diff --git a/src/marathon/map.rs b/src/marathon/map.rs index 5061f53..ae7236c 100644 --- a/src/marathon/map.rs +++ b/src/marathon/map.rs @@ -23,8 +23,7 @@ impl Chunked for Endpoint let flags = b.c_u16b(0)?; let adj_hi = b.c_i16b(2)?; let adj_lo = b.c_i16b(4)?; - let pos = Point::read(&b[ 6..10])?; - // xform = Point::read(&b[10..14])?; + let pos = Point::read(&b[6..10])?; let support = b.c_u16b(14)?; let flags = EndpointFlags::from_bits_truncate(flags); Ok(Endpoint{flags, adj_hi, adj_lo, pos, support}) @@ -87,7 +86,7 @@ impl Chunked for Side } else { None }; - let shade = Fx32::from_bits(shade); + let shade = Fx32::from_bits(shade); Ok(Side{stype, flags, tex_pri, tex_sec, tex_tra, ex_tleft, ex_trigh, ex_bleft, ex_brigh, paneltyp, paneldat, xfer_pri, xfer_sec, xfer_tra, shade}) diff --git a/src/marathon/mod.rs b/src/marathon/mod.rs index 672877a..525bd09 100644 --- a/src/marathon/mod.rs +++ b/src/marathon/mod.rs @@ -1,6 +1,7 @@ -//! Library for Marathon data formats. +//! Library for file data formats. pub mod map; +pub mod pict; pub mod term; pub mod wad; diff --git a/src/durandal/pict.rs b/src/marathon/pict.rs similarity index 93% rename from src/durandal/pict.rs rename to src/marathon/pict.rs index 2326903..7e5a953 100644 --- a/src/durandal/pict.rs +++ b/src/marathon/pict.rs @@ -10,7 +10,7 @@ const PACK_RLE16 : u16 = 3; const PACK_RLE32 : u16 = 4; /// Process a CopyBits operation. -fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> ResultS +pub fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> ResultS { let mut p = if !packed {4} else {0}; @@ -146,7 +146,7 @@ fn read_bitmap_area(mut im: Image, b: &[u8], packed: bool, clip: bool) -> Result } /// Process a CompressedQuickTime operation. -fn read_quicktime_c(_im: Image, _b: &[u8]) -> ResultS +pub fn read_quicktime_c(_im: Image, _b: &[u8]) -> ResultS { err_msg("compressed quicktime format not implemented") } @@ -230,25 +230,25 @@ pub fn load_pict(b: &[u8]) -> ResultS } /// Read a colorTable structure. -fn get_clut(b: &[u8]) -> ResultS<(Vec, usize)> +pub fn get_clut(b: &[u8]) -> ResultS<(Vec, usize)> { // sed = b.c_u32b(0)?; let dev = b.c_u16b(4)? & 0x8000 != 0; let num = b.c_u16b(6)? as usize + 1; let mut p = 8; - let mut clut = Vec::with_capacity(num); - - clut.resize(num, Color{r: 0, g: 0, b: 0, a: 0}); + let mut clut = vec![Color{r: 0, g: 0, b: 0, a: 0}; num]; for i in 0..num { // with device mapping, we ignore the index entirely - let n = if !dev {(b.c_u16b(p )? & 0xff) as usize} else {i}; - let r = (b.c_u16b(p+2)? >> 8) as u8; - let g = (b.c_u16b(p+4)? >> 8) as u8; - let b = (b.c_u16b(p+6)? >> 8) as u8; + let n = if !dev {b[p + 1] as usize} else {i}; + let r = b[p+2]; + let g = b[p+4]; + let b = b[p+6]; + if n >= clut.len() {return err_msg("bad clut index");} clut[n] = Color{r, g, b, a: 255}; + p += 8; } @@ -256,7 +256,7 @@ fn get_clut(b: &[u8]) -> ResultS<(Vec, usize)> } /// Read run-length encoded data. -fn read_rle(b: &[u8], pitch: usize, ln: bool) -> ResultS<(Vec, usize)> +pub fn read_rle(b: &[u8], pitch: usize, ln: bool) -> ResultS<(Vec, usize)> { let mut p = 0; let mut o = Vec::with_capacity(pitch); @@ -292,7 +292,7 @@ fn read_rle_data(cmp: bool, len: usize, out: &mut Vec, mut read: F) } /// Expand packed pixel data based on bit depth. -fn expand_data(b: Vec, depth: u16) -> ResultS> +pub fn expand_data(b: Vec, depth: u16) -> ResultS> { let mut o = Vec::with_capacity(match depth { 4 => b.len() * 2, diff --git a/tests/clut.in b/tests/clut.in new file mode 100644 index 0000000000000000000000000000000000000000..98c94d1052d3a4af889281b50b37674b10846c8d GIT binary patch literal 2056 zcmXAq2Xs|M6h-&{$xlK;LIRP{NhpROAP}UA#0UhiAW|Yow_>4*A~pmIBBCe)_J$%V z*s&LyU9f=_73^KWUU6UY{#yU7nYnZCx#ygj2Ya7AuxIa&L?*SU2gyw8(iYNsQ%NDD zmr~mqQcY;d{*cjyE*u1zd8EjItd8_%Fw|D3^I~>W0}ASP}qbb+Q2>otlJyvFJT!sLeX%B zl7t3Rm_iyf+|CYvH0nYlN}+KNQQJ?G2~3RjrbT2^0L|tzKkzm0PPcd$uV-DXm)uJH zXtA1A3q(&W7?d<*^#%bibJE{ww3gu0`_Z0 zv*4%Q3Rc9seTAqxLWd5N$O!v)r!|K|$Igz*q0vb7*OrF z2nH@^r4C`xW?m0I4}FqnV*aoRjF&kMZ>p>G#}SvRyZZ-^=Oj*tBPaVWjv><+61pFC zwE3!F=ukR@--cB)(oPw^iU(K&BaV@^VNm@QU-KOty@%O4n33sLO@~qG`dD3#NjS=e z(Ft85o-qqqL_UmF^LWNJR3CjcK2P-;!-Q+q`*t*o(eij~V|9{`IJ`1q7=Nx}(f zc3n(RO!()d0{82)N&P%8;yyWMOUjfdh&QihEcxHEV2uAnHhP^dsad>SZ7vruO4SdcIVHj)Q{D0 z?rkjR9yqViIDK<|tcqiH(s%uV=je-{=|KlHpP`jpp;OLbdoxTJxz z-Coa7r6mHna#?bA? zsOtNsq`N$yH>cIa-;z={<8DnCzxZye=eQNzo;2EimWL1I=Z;*vQQteS^tQSQR%DA# zeecSu*|)M-x5?Ap1@4o-d)mv+V7NEUed@F-+$Zm=Tj>A3a9`5wE#Ura_vx<(!X4&& zuuhE+ABw(>e>mxW^?szj)r(NUHg6_X&!ldGH|>8R5=xK zo}P~S>hVmtLq9!RS6!RKa}h1xwQE`H4aYh;jAwo5DIXgmpLsTxdm9ac=W}&Q3A~V! z75R9vNcQ)Imx^SyE4&=4smm)Rs_A)uwWD~e;I#@*x4dqe!K~P~Ir4bkUym-0e