From fbc0a0b2ea7b5c9a7c4561c82c90b642df335eeb Mon Sep 17 00:00:00 2001 From: John Breaux Date: Tue, 28 Mar 2023 07:33:17 -0500 Subject: [PATCH] tests: Coverage and cleanup/speedup - Improved test coverage to >80% of lines, functions - When doctests are included. - Wrote new unit tests: - Explicit tests for invalid instructions in the ranges {`5xyn`, `8xyn`, `9xyn`, `Fxbb`} - `rand` Tests for 1052671 cycles, to ensure randomly generated number is < ANDed byte - `Ex9E` (sek), `ExA1`(snek) will press only the expected key, then every key except the expected key, for every address - `Fx0A` (waitk) asserts based on the waveform of a keypress. After all, an A press is an A press. - Improved test performance by printing slightly less - Removed nightly requirement - (now optional, with feature = "unstable") - Amended justfile to test with `cargo nextest` (nice) - Changed release builds to optlevel 3 --- Cargo.toml | 7 +- src/cpu.rs | 25 +++ src/cpu/disassemble.rs | 5 +- src/cpu/tests.rs | 260 +++++++++++++++++++++----------- src/cpu/tests/roms/jumptest.ch8 | 2 + src/cpu/tests/roms/looptest.ch8 | Bin 0 -> 96 bytes src/io.rs | 1 + src/lib.rs | 2 +- tests/struct.rs | 230 ++++++++++++++++++++++++++++ 9 files changed, 435 insertions(+), 97 deletions(-) create mode 100644 src/cpu/tests/roms/jumptest.ch8 create mode 100644 src/cpu/tests/roms/looptest.ch8 create mode 100644 tests/struct.rs diff --git a/Cargo.toml b/Cargo.toml index 7fb2923..12826cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,20 +6,21 @@ authors = ["John Breaux"] license = "MIT" [features] -default = [] +default = ["unstable"] +unstable = [] rhexdump = ["dep:rhexdump"] serde = ["dep:serde"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [profile.release] -opt-level = 'z' +opt-level = 3 debug = false rpath = false lto = true debug-assertions = false codegen-units = 1 panic = 'unwind' -incremental = true +incremental = false overflow-checks = false diff --git a/src/cpu.rs b/src/cpu.rs index 0b14fa1..f346835 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -74,10 +74,35 @@ pub struct ControlFlags { impl ControlFlags { /// Toggles debug mode + /// + /// # Examples + /// ```rust + ///# use chirp::prelude::*; + ///# fn main() -> Result<()> { + /// let mut cpu = CPU::default(); + /// assert_eq!(true, cpu.flags.debug); + /// cpu.flags.debug(); + /// assert_eq!(false, cpu.flags.debug); + ///# Ok(()) + ///# } + /// ``` pub fn debug(&mut self) { self.debug = !self.debug } + /// Toggles pause + /// + /// # Examples + /// ```rust + ///# use chirp::prelude::*; + ///# fn main() -> Result<()> { + /// let mut cpu = CPU::default(); + /// assert_eq!(false, cpu.flags.pause); + /// cpu.flags.pause(); + /// assert_eq!(true, cpu.flags.pause); + ///# Ok(()) + ///# } + /// ``` pub fn pause(&mut self) { self.pause = !self.pause } diff --git a/src/cpu/disassemble.rs b/src/cpu/disassemble.rs index 871e5bf..22dce52 100644 --- a/src/cpu/disassemble.rs +++ b/src/cpu/disassemble.rs @@ -309,8 +309,9 @@ impl Disassemble { } /// `Dxyn`: Draws n-byte sprite to the screen at coordinates (vX, vY) pub fn draw(&self, x: Reg, y: Reg, n: Nib) -> String { - #[rustfmt::skip] - format!("draw #{n:x}, v{x:X}, v{y:X}").style(self.normal).to_string() + format!("draw #{n:x}, v{x:X}, v{y:X}") + .style(self.normal) + .to_string() } /// `Ex9E`: Skip next instruction if key == #X pub fn skip_if_key_equals_x(&self, x: Reg) -> String { diff --git a/src/cpu/tests.rs b/src/cpu/tests.rs index a37db83..8eea761 100644 --- a/src/cpu/tests.rs +++ b/src/cpu/tests.rs @@ -4,6 +4,13 @@ //! Tests for cpu.rs //! //! These run instructions, and ensure their output is consistent with previous builds +//! +//! General test format: +//! 1. Prepare to do the thing +//! 2. Do the thing +//! 3. Compare the result to the expected result +//! +//! Some of these tests run >16M times, which is very silly use super::*; pub(self) use crate::{ @@ -25,8 +32,8 @@ fn setup_environment() -> (CPU, Bus) { bus! { // Load the charset into ROM Charset [0x0050..0x00A0] = include_bytes!("../mem/charset.bin"), - // Load the ROM file into RAM - Program [0x0200..0x1000] = include_bytes!("../../chip-8/BC_test.ch8"), + // Load the ROM file into RAM (dummy binary which contains nothing but `jmp pc+2`) + Program [0x0200..0x1000] = include_bytes!("tests/roms/jumptest.ch8"), // Create a screen Screen [0x0F00..0x1000] = include_bytes!("../../chip-8/IBM Logo.ch8"), }, @@ -38,15 +45,37 @@ fn print_screen(bytes: &[u8]) { } /// Unused instructions -/// -/// TODO: Exhaustively test unused instructions -#[test] -#[should_panic] -fn unimplemented() { - let (mut cpu, mut bus) = setup_environment(); - bus.write(0x200u16, 0xffffu16); // 0xffff is not an instruction - cpu.tick(&mut bus); - cpu.unimplemented(0xffff); +mod unimplemented { + use super::*; + #[test] + #[should_panic] + fn ins_5xyn() { + let (mut cpu, mut bus) = setup_environment(); + bus.write(0x200u16, 0x500fu16); // 0x500f is not an instruction + cpu.tick(&mut bus); + } + #[test] + #[should_panic] + fn ins_8xyn() { + let (mut cpu, mut bus) = setup_environment(); + bus.write(0x200u16, 0x800fu16); // 0x800f is not an instruction + cpu.tick(&mut bus); + } + #[test] + #[should_panic] + fn ins_9xyn() { + let (mut cpu, mut bus) = setup_environment(); + bus.write(0x200u16, 0x900fu16); // 0x800f is not an instruction + cpu.tick(&mut bus); + } + // Fxbb + #[test] + #[should_panic] + fn ins_fxbb() { + let (mut cpu, mut bus) = setup_environment(); + bus.write(0x200u16, 0xffffu16); // 0xffff is not an instruction + cpu.tick(&mut bus); + } } mod sys { @@ -133,12 +162,12 @@ mod cf { for x in 0..=0xf { // set the PC to a random address cpu.pc = addr; - // set the register under test to a + cpu.v[x] = a; - // do the thing + cpu.skip_equals_immediate(x, b); - // validate the result - assert_eq!(cpu.pc, addr.wrapping_add(if dbg!(a == b) { 2 } else { 0 })); + + assert_eq!(cpu.pc, addr.wrapping_add(if a == b { 2 } else { 0 })); } } } @@ -152,11 +181,11 @@ mod cf { for x in 0..=0xf { // set the PC to a random address cpu.pc = addr; - // set the register under test to a + cpu.v[x] = a; - // do the thing + cpu.skip_not_equals_immediate(x, b); - // validate the result + assert_eq!(cpu.pc, addr.wrapping_add(if a != b { 2 } else { 0 })); } } @@ -175,11 +204,11 @@ mod cf { } // set the PC to a random address cpu.pc = addr; - // set the registers under test to a, b + (cpu.v[x], cpu.v[y]) = (a, b); - // do the thing + cpu.skip_equals(x, y); - // validate the result + assert_eq!(cpu.pc, addr.wrapping_add(if a == b { 2 } else { 0 })); } } @@ -198,11 +227,11 @@ mod cf { } // set the PC to a random address cpu.pc = addr; - // set the registers under test to a, b + (cpu.v[x], cpu.v[y]) = (a, b); - // do the thing + cpu.skip_not_equals(x, y); - // validate the result + assert_eq!(cpu.pc, addr.wrapping_add(if a != b { 2 } else { 0 })); } } @@ -218,9 +247,9 @@ mod cf { for v0 in 0..=0xff { // set v[0] = v0 cpu.v[0] = v0; - // jump indexed + cpu.jump_indexed(addr); - // Validate register set + assert_eq!(cpu.pc, addr.wrapping_add(v0.into())); } } @@ -248,11 +277,11 @@ mod math { for test_register in 0x0..=0xf { let mut sum = 0u8; for test_byte in 0x0..=0xff { - // Wrapping-add to the running total (Chip-8 allows unsigned overflow) + // Note: Chip-8 allows unsigned overflow sum = sum.wrapping_add(test_byte); - // Perform add #byte, vReg + cpu.add_immediate(test_register, test_byte); - //Verify the running total in the register matches + assert_eq!(cpu.v[test_register], sum); } } @@ -273,7 +302,9 @@ mod math { cpu.v[y] = test_value; // zero X cpu.v[x] = 0; + cpu.load(x, y); + // verify results assert_eq!(cpu.v[x], test_value); assert_eq!(cpu.v[y], test_value); @@ -292,13 +323,11 @@ mod math { let expected_result = a | b; for reg in 0..=0xff { let (x, y) = (reg & 0xf, reg >> 4); - // set the registers under test to a, b + (cpu.v[x], cpu.v[y]) = (a, b); - // do the thing cpu.or(x, y); - // validate the result assert_eq!(cpu.v[x], if x == y { b } else { expected_result }); } } @@ -315,13 +344,11 @@ mod math { let expected_result = a & b; for reg in 0..=0xff { let (x, y) = (reg & 0xf, reg >> 4); - // set the registers under test to a, b + (cpu.v[x], cpu.v[y]) = (a, b); - // do the thing cpu.and(x, y); - // validate the result assert_eq!(cpu.v[x], if x == y { b } else { expected_result }); } } @@ -338,13 +365,11 @@ mod math { let expected_result = a ^ b; for reg in 0..=0xff { let (x, y) = (reg & 0xf, reg >> 4); - // set the registers under test to a, b + (cpu.v[x], cpu.v[y]) = (a, b); - // do the thing cpu.xor(x, y); - // validate the result assert_eq!(cpu.v[x], if x == y { 0 } else { expected_result }); } } @@ -362,13 +387,11 @@ mod math { // calculate the expected result // If x == y, a is discarded let (expected, carry) = if x == y { b } else { a }.overflowing_add(b); - // set the registers under test to a, b + (cpu.v[x], cpu.v[y]) = (a, b); - // do the thing cpu.add(x, y); - // validate the result // if the destination is vF, the result was discarded, and only the carry was kept if x != 0xf { assert_eq!(cpu.v[x], expected); @@ -388,13 +411,11 @@ mod math { let (x, y) = (reg & 0xf, reg >> 4); // calculate the expected result let (expected, carry) = if x == y { b } else { a }.overflowing_sub(b); - // set the registers under test to a, b + (cpu.v[x], cpu.v[y]) = (a, b); - // do the thing cpu.sub(x, y); - // validate the result // if the destination is vF, the result was discarded, and only the carry was kept if x != 0xf { assert_eq!(cpu.v[x], expected); @@ -414,10 +435,9 @@ mod math { for x in 0..=0xf { // set the register under test to `word` cpu.v[x] = word; - // do the thing + cpu.shift_right(x, x); - // validate the result // if the destination is vF, the result was discarded, and only the carry was kept if x != 0xf { assert_eq!(cpu.v[x], word >> 1); @@ -438,13 +458,10 @@ mod math { let (x, y) = (reg & 0xf, reg >> 4); // calculate the expected result let (expected, carry) = if x == y { a } else { b }.overflowing_sub(a); - // set the registers under test to a, b (cpu.v[x], cpu.v[y]) = (a, b); - // do the thing cpu.backwards_sub(x, y); - // validate the result // if the destination is vF, the result was discarded, and only the carry was kept if x != 0xf { assert_eq!(cpu.v[x], expected); @@ -464,10 +481,9 @@ mod math { for x in 0..=0xf { // set the register under test to `word` cpu.v[x] = word; - // do the thing + cpu.shift_left(x, x); - // validate the result // if the destination is vF, the result was discarded, and only the carry was kept if x != 0xf { assert_eq!(cpu.v[x], word << 1); @@ -523,15 +539,21 @@ mod io { use super::*; /// Cxbb: Stores a random number & the provided byte into vX - //#[test] - #[allow(dead_code)] + #[test] fn rand() { - todo!() + let (mut cpu, _) = setup_environment(); + for xb in 0..0x100fff { + let (x, b) = ((xb >> 8) % 16, xb as u8); + cpu.v[x] = 0; + cpu.rand(x, b); + // We don't know what the number will be, + // but we do know it'll be <= b + assert!(cpu.v[x] <= b); + } } mod display { use super::*; - #[derive(Debug)] struct ScreenTest { program: &'static [u8], screen: &'static [u8], @@ -542,7 +564,7 @@ mod io { const SCREEN_TESTS: [ScreenTest; 4] = [ // Passing BC_test // # Quirks: - // - Requires + // - Requires shift and dma_inc to act like Super-CHIP ScreenTest { program: include_bytes!("../../chip-8/BC_test.ch8"), screen: include_bytes!("tests/screens/BC_test.ch8/197.bin"), @@ -551,21 +573,21 @@ mod io { bin_ops: true, shift: false, draw_wait: true, - dma_inc: false, stupid_jumps: false, }, }, // The IBM Logo + // # Quirks + // Originally timed without quirks ScreenTest { program: include_bytes!("../../chip-8/IBM Logo.ch8"), screen: include_bytes!("tests/screens/IBM Logo.ch8/20.bin"), - steps: 20, + steps: 56, quirks: Quirks { bin_ops: true, shift: true, - draw_wait: false, - + draw_wait: true, dma_inc: true, stupid_jumps: false, }, @@ -623,26 +645,94 @@ mod io { } mod cf { - //use super::*; + use super::*; /// Ex9E: Skip next instruction if key == #X - //#[test] - #[allow(dead_code)] + #[test] fn skip_key_equals() { - todo!() + let (mut cpu, _) = setup_environment(); + for ka in 0..=0x7fef { + let (key, addr) = ((ka & 0xf) as u8, ka >> 8); + // positive test (no keys except key pressed) + cpu.keys = [false; 16]; // unset all keys + cpu.keys[key as usize] = true; // except the one we care about + for x in 0..=0xf { + cpu.pc = addr; + cpu.v[x] = key; + cpu.skip_key_equals(x); + assert_eq!(cpu.pc, addr.wrapping_add(2)); + cpu.v[x] = 0xff; + } + // negative test (all keys except key pressed) + cpu.keys = [true; 16]; // set all keys + cpu.keys[key as usize] = false; // except the one we care about + for x in 0..=0xf { + cpu.pc = addr; + cpu.v[x] = key; + cpu.skip_key_equals(x); + assert_eq!(cpu.pc, addr); + cpu.v[x] = 0xff; + } + } } /// ExaE: Skip next instruction if key != #X - //#[test] - #[allow(dead_code)] + #[test] fn skip_key_not_equals() { - todo!() + let (mut cpu, _) = setup_environment(); + for ka in 0..=0x7fcf { + let (key, addr) = ((ka & 0xf) as u8, ka >> 8); + // positive test (no keys except key pressed) + cpu.keys = [false; 16]; // unset all keys + cpu.keys[key as usize] = true; // except the one we care about + for x in 0..=0xf { + cpu.pc = addr; + cpu.v[x] = key; + cpu.skip_key_not_equals(x); + assert_eq!(cpu.pc, addr); + cpu.v[x] = 0xff; + } + // negative test (all keys except key pressed) + cpu.keys = [true; 16]; // set all keys + cpu.keys[key as usize] = false; // except the one we care about + for x in 0..=0xf { + cpu.pc = addr; + cpu.v[x] = key; + cpu.skip_key_not_equals(x); + assert_eq!(cpu.pc, addr.wrapping_add(2)); + cpu.v[x] = 0xff; + } + } } /// Fx0A: Wait for key, then vX = K - //#[test] - #[allow(dead_code)] + /// + /// The write happens on key *release* + #[test] fn wait_for_key() { - todo!() + let (mut cpu, _) = setup_environment(); + for key in 0..0xf { + for x in 0..0xf { + cpu.v[x] = 0xff; + cpu.wait_for_key(x); + assert_eq!(true, cpu.flags.keypause); + assert_eq!(0xff, cpu.v[x]); + // There are three parts to a button press + // When the button is pressed + cpu.press(key); + assert_eq!(true, cpu.flags.keypause); + assert_eq!(0xff, cpu.v[x]); + // When the button is held + cpu.wait_for_key(x); + assert_eq!(true, cpu.flags.keypause); + assert_eq!(0xff, cpu.v[x]); + // And when the button is released! + cpu.release(key); + assert_eq!(false, cpu.flags.keypause); + assert_eq!(Some(key), cpu.flags.lastkey); + cpu.wait_for_key(x); + assert_eq!(key as u8, cpu.v[x]); + } + } } } @@ -654,9 +744,9 @@ mod io { for x in 0..=0xf { // set the register under test to `word` cpu.delay = word as f64; - // do the thing + cpu.load_delay_timer(x); - // validate the result + assert_eq!(cpu.v[x], word); } } @@ -670,9 +760,9 @@ mod io { for x in 0..=0xf { // set the register under test to `word` cpu.v[x] = word; - // do the thing + cpu.store_delay_timer(x); - // validate the result + assert_eq!(cpu.delay, word as f64); } } @@ -680,15 +770,15 @@ mod io { /// Fx18: Load vX into ST #[test] - fn load_sound_timer() { + fn store_sound_timer() { let (mut cpu, _) = setup_environment(); for word in 0..=0xff { for x in 0..=0xf { // set the register under test to `word` cpu.v[x] = word; - // do the thing + cpu.store_sound_timer(x); - // validate the result + assert_eq!(cpu.sound, word as f64); } } @@ -724,7 +814,6 @@ mod io { /// Fx29: Load sprite for character vX into I #[test] - #[allow(dead_code)] fn load_sprite() { let (mut cpu, bus) = setup_environment(); for test in TESTS { @@ -735,15 +824,7 @@ mod io { cpu.load_sprite(reg); let addr = cpu.i as usize; - assert_eq!( - bus.get(addr..addr.wrapping_add(5)).unwrap(), - test.output, - "\nTesting load_sprite({:x}) -> {:04x} {{\n\tout:{:02x?}\n\texp:{:02x?}\n}}", - test.input, - cpu.i, - bus.get(addr..addr.wrapping_add(5)).unwrap(), - test.output, - ); + assert_eq!(bus.get(addr..addr.wrapping_add(5)).unwrap(), test.output,); } } } @@ -775,7 +856,6 @@ mod io { /// Fx33: BCD convert X into I`[0..3]` #[test] - #[allow(dead_code)] fn bcd_convert() { for test in BCD_TESTS { let (mut cpu, mut bus) = setup_environment(); @@ -785,7 +865,7 @@ mod io { cpu.v[5] = test.input; cpu.bcd_convert(5, &mut bus); - // validate the results + assert_eq!(bus.get(addr..addr.saturating_add(3)), Some(test.output)) } } @@ -794,7 +874,6 @@ mod io { /// Fx55: DMA Stor from I to registers 0..X // TODO: Test with dma_inc quirk set #[test] - #[allow(dead_code)] fn dma_store() { let (mut cpu, mut bus) = setup_environment(); const DATA: &[u8] = b"ABCDEFGHIJKLMNOP"; @@ -817,7 +896,6 @@ mod io { /// Fx65: DMA Load from I to registers 0..X // TODO: Test with dma_inc quirk set #[test] - #[allow(dead_code)] fn dma_load() { let (mut cpu, mut bus) = setup_environment(); const DATA: &[u8] = b"ABCDEFGHIJKLMNOP"; diff --git a/src/cpu/tests/roms/jumptest.ch8 b/src/cpu/tests/roms/jumptest.ch8 new file mode 100644 index 0000000..d35a115 --- /dev/null +++ b/src/cpu/tests/roms/jumptest.ch8 @@ -0,0 +1,2 @@ + +  "$&(*,.02468:<>@BDFHJLNPRTVXZ\^` \ No newline at end of file diff --git a/src/cpu/tests/roms/looptest.ch8 b/src/cpu/tests/roms/looptest.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..dfe30497da896e9ba48dc0c4a309ddc7041cd4be GIT binary patch literal 96 zcmV~$0S3YV006*bCX?i3GD(t2lF4K;NivhkB$;IYe@1r(h616H$k@abGqJhE!qUpx hMrvzk?;vxOJ1Lx%F0O9wDz%5kQ|smJqx1FC{{g(;3^4!z literal 0 HcmV?d00001 diff --git a/src/io.rs b/src/io.rs index dbf6344..c1c17c7 100644 --- a/src/io.rs +++ b/src/io.rs @@ -266,6 +266,7 @@ pub fn identify_key(key: Key) -> usize { } } +#[cfg_attr(feature = "unstable", no_coverage)] pub fn debug_dump_screen(ch8: &Chip8, rom: &Path) -> Result<()> { let path = PathBuf::new() .join("src/cpu/tests/screens/") diff --git a/src/lib.rs b/src/lib.rs index 39f802c..aee6302 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ // (c) 2023 John A. Breaux // This code is licensed under MIT license (see LICENSE.txt for details) -#![feature(stmt_expr_attributes)] +#![cfg_attr(feature = "unstable", feature(no_coverage))] //! This crate implements a Chip-8 interpreter as if it were a real CPU architecture, //! to the best of my current knowledge. As it's the first emulator project I've //! embarked on, and I'm fairly new to Rust, it's going to be rough around the edges. diff --git a/tests/struct.rs b/tests/struct.rs new file mode 100644 index 0000000..58f5970 --- /dev/null +++ b/tests/struct.rs @@ -0,0 +1,230 @@ +//! Testing methods on Chirp's structs +use chirp::prelude::*; +use std::{collections::hash_map::DefaultHasher, hash::Hash}; + +#[test] +fn chip8() { + let ch8 = Chip8::default(); // Default + let ch82 = ch8.clone(); // Clone + assert_eq!(ch8, ch82); // PartialEq + println!("{ch8:?}"); // Debug +} + +#[test] +fn error() { + let error = chirp::error::Error::FunkyMath { + word: 0x1234, + explanation: "This shit was funky".to_owned(), + }; + println!("{error} {error:?}"); +} + +mod ui_builder { + use super::*; + #[test] + fn ui_builder() -> Result<()> { + let builder = UIBuilder::new(32, 64).rom("dummy.ch8").build()?; + println!("{builder:?}"); + Ok(()) + } + #[test] + fn default() { + let ui_builder = UIBuilder::default(); + println!("{ui_builder:?}"); + } + #[test] + fn clone_debug() { + let ui_builder_clone = UIBuilder::default().clone(); + println!("{ui_builder_clone:?}"); + } +} +mod ui { + use super::*; + fn new_chip8() -> Chip8 { + Chip8 { + cpu: CPU::default(), + bus: bus! {}, + } + } + #[test] + fn frame() -> Result<()> { + let mut ui = UIBuilder::new(32, 64).build()?; + let mut ch8 = new_chip8(); + ui.frame(&mut ch8).unwrap(); + Ok(()) + } + #[test] + fn keys() -> Result<()> { + let mut ui = UIBuilder::new(32, 64).build()?; + let mut ch8 = new_chip8(); + let ch8 = &mut ch8; + ui.frame(ch8).unwrap(); + ui.keys(ch8); + Ok(()) + } + #[test] + fn debug() -> Result<()> { + println!("{:?}", UIBuilder::new(32, 64).build()?); + Ok(()) + } +} + +mod framebuffer_format { + use super::*; + #[test] + fn default() { + let _fbf = FrameBufferFormat::default(); + } + #[test] + fn clone() { + let fbf = FrameBufferFormat { + fg: 0x12345678, + bg: 0x90abcdef, + }; + let fbf2 = fbf.clone(); + assert_eq!(fbf, fbf2); + } + #[test] + fn debug() { + println!("{:?}", FrameBufferFormat::default()); + } + #[test] + fn eq() { + assert_eq!(FrameBufferFormat::default(), FrameBufferFormat::default()); + assert_ne!( + FrameBufferFormat { + fg: 0xff00ff, + bg: 0x00ff00 + }, + FrameBufferFormat { + fg: 0x00ff00, + bg: 0xff00ff + }, + ); + } + #[test] + fn ord() { + assert!( + FrameBufferFormat::default() + == FrameBufferFormat { + fg: 0xffffff, + bg: 0xffffff, + } + .min(FrameBufferFormat::default()) + ); + } + #[test] + fn hash() { + println!( + "{:?}", + FrameBufferFormat::default().hash(&mut DefaultHasher::new()) + ); + } +} + +mod framebuffer { + use super::*; + // [derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] + #[test] + fn new() { + assert_eq!(FrameBuffer::new(64, 32), FrameBuffer::default()); + } + #[test] + fn clone() { + let fb1 = FrameBuffer::default(); + let fb2 = fb1.clone(); + assert_eq!(fb1, fb2); + } + + #[test] + fn debug() { + println!("{:?}", FrameBuffer::default()); + } + + #[test] + fn eq() { + assert_eq!(FrameBuffer::new(64, 32), FrameBuffer::default()); + } + + #[test] + fn ord() { + assert!(FrameBuffer::new(21, 12) == FrameBuffer::new(21, 12).min(FrameBuffer::new(34, 46))); + } + + #[test] + fn hash() { + println!( + "{:?}", + FrameBuffer::default().hash(&mut DefaultHasher::new()) + ); + } +} + +mod quirks { + use super::*; + use chirp::cpu::Quirks; + + #[test] + fn from_true() { + let quirks_true = Quirks::from(true); + assert_eq!( + quirks_true, + Quirks { + bin_ops: true, + shift: true, + draw_wait: true, + dma_inc: true, + stupid_jumps: false, + } + ) + } + + #[test] + fn from_false() { + let quirks_true = Quirks::from(false); + assert_eq!( + quirks_true, + Quirks { + bin_ops: false, + shift: false, + draw_wait: false, + dma_inc: false, + stupid_jumps: false, + } + ) + } + + #[test] + fn clone() { + let q1 = Quirks { + bin_ops: false, + shift: true, + draw_wait: false, + dma_inc: true, + stupid_jumps: false, + }; + let q2 = q1.clone(); + assert_eq!(q1, q2); + } + + #[test] + fn debug() { + println!("{:?}", Quirks::from(true)); + } + + #[test] + fn eq() { + assert_ne!(Quirks::from(false), Quirks::from(true)); + } + + #[test] + fn ord() { + assert!(Quirks::from(false) < Quirks::from(true)); + assert!(Quirks::from(true) == Quirks::from(false).max(Quirks::from(true))); + } + + #[test] + fn hash() { + println!("{:?}", Quirks::from(true).hash(&mut DefaultHasher::new())); + } +}