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
This commit is contained in:
John 2023-03-28 07:33:17 -05:00
parent 66bed02a5e
commit fbc0a0b2ea
9 changed files with 435 additions and 97 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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 {

View File

@ -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
mod unimplemented {
use super::*;
#[test]
#[should_panic]
fn unimplemented() {
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);
cpu.unimplemented(0xffff);
}
}
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";

View File

@ -0,0 +1,2 @@

  "$&(*,.02468:<>@BDFHJLNPRTVXZ\^`

Binary file not shown.

View File

@ -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/")

View File

@ -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.

230
tests/struct.rs Normal file
View File

@ -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()));
}
}