Skip to content

Commit

Permalink
Added code for initial custom test harness (#20).
Browse files Browse the repository at this point in the history
Signed-off-by: Gerd Zellweger <mail@gerdzellweger.com>
  • Loading branch information
gz committed May 23, 2017
1 parent 3a18b5b commit 158cd8a
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 24 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ version = "0.7.7"
optional = true
features = ["core"]

#[target.x86_64-utest-qemu.dev-dependencies.test]
#path = "../utest"
#[target.x86_64-utest-qemu.dev-dependencies.utest-macros]
#path = "../utest/macros"
#[target.x86_64-utest-qemu.dev-dependencies.utest-x86-64-qemu]
Expand All @@ -57,3 +55,5 @@ features = ["core"]
memmap = "0.2.1"
kvm = { git = "https://github.com/gz/kvm.git" }
klogger = { git = "https://github.com/gz/rust-klogger.git" }
test = { path = "test_harness" }
test-macros = { path = "test_macros" }
8 changes: 8 additions & 0 deletions test_harness/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "test"
version = "0.0.1"
authors = ["Gerd Zellweger <mail@gerdzellweger.com>"]

[dependencies]
syn = "0.11.*"
quote = "0.3.*"
98 changes: 98 additions & 0 deletions test_harness/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#![feature(used, lang_items)]

pub fn test_start(ntests: usize) {
println!("KVM testing: running {} tests", ntests)
}

pub fn test_ignored(name: &str) {
println!("test {} ... ignored", name);
}

pub fn test_before_run(name: &str) {
print!("test {} ... ", name);
}

pub fn test_panic_fmt(args: std::fmt::Arguments, file: &'static str, line: u32) {
print!("\npanicked at '");
use std::io::Write;
std::io::stderr().write_fmt(args);
println!("', {}:{}", file, line);
}

pub fn test_failed(_name: &str) {
println!("FAILED");
}

pub fn test_success(_name: &str) {
println!("OK");
}

pub fn test_summary(passed: usize, failed: usize, ignored: usize) {
println!("\ntest result: {} {} passed; {} failed; {} ignored",
if failed == 0 { "OK" } else { "FAILED" },
passed,
failed,
ignored);

if failed != 0 {
std::process::exit(101);
}
}

#[no_mangle]
#[used]
pub static mut __TEST_PANICKED: bool = false;

pub fn test_main_static(tests: &[TestDescAndFn]) {
unsafe {
test_start(tests.len());

let mut failed = 0;
let mut ignored = 0;
let mut passed = 0;
for test in tests {
if test.desc.ignore {
ignored += 1;
test_ignored(test.desc.name.0);
} else {
test_before_run(test.desc.name.0);

__TEST_PANICKED = false;

test.testfn.0();

if __TEST_PANICKED == (test.desc.should_panic == ShouldPanic::Yes) {
passed += 1;
test_success(test.desc.name.0);
} else {
failed += 1;
test_failed(test.desc.name.0);
}
}

}

test_summary(passed, failed, ignored);
}
}

// required for compatibility with the `rustc --test` interface
pub struct TestDescAndFn {
pub desc: TestDesc,
pub testfn: StaticTestFn,
}

pub struct TestDesc {
pub ignore: bool,
pub name: StaticTestName,
pub should_panic: ShouldPanic,
}

pub struct StaticTestName(pub &'static str);
pub struct StaticTestFn(pub fn());

#[derive(PartialEq)]
pub enum ShouldPanic {
No,
Yes,
}
14 changes: 14 additions & 0 deletions test_macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "test-macros"
version = "0.0.1"
authors = ["Gerd Zellweger <mail@gerdzellweger.com>"]

[dependencies]
quote = "0.3.*"

[dependencies.syn]
version = "0.11.*"
features = ["full"]

[lib]
proc-macro = true
72 changes: 72 additions & 0 deletions test_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#![feature(rustc_private)]
#![feature(proc_macro)]

extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
extern crate syntax;

use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn panics_note(args: TokenStream, input: TokenStream) -> TokenStream {
let args = args.to_string();
let mut input = input.to_string();

assert!(args.starts_with("= \""),
"`#[panics_note]` requires an argument of the form `#[panics_note = \"panic note \
here\"]`");

// Get just the bare note string
let panics_note = args.trim_matches(&['=', ' ', '"'][..]);

// The input will include all docstrings regardless of where the attribute is placed,
// so we need to find the last index before the start of the item
let insert_idx = idx_after_last_docstring(&input);

// And insert our `### Panics` note there so it always appears at the end of an item's docs
input.insert_str(insert_idx, &format!("/// # Panics \n/// {}\n", panics_note));

input.parse().unwrap()
}

// `proc-macro` crates can contain any kind of private item still
fn idx_after_last_docstring(input: &str) -> usize {
// Skip docstring lines to find the start of the item proper
input.lines().skip_while(|line| line.trim_left().starts_with("///")).next()
// Find the index of the first non-docstring line in the input
// Note: assumes this exact line is unique in the input
.and_then(|line_after| input.find(line_after))
// No docstrings in the input
.unwrap_or(0)
}


fn generate_kvm_setup(ident: syn::Ident) -> quote::Tokens {
quote! {
// The generated impl
fn #ident() {
log!("blabla");
}
}
}

use std::string;

#[proc_macro_attribute]
pub fn kvmattrs(args: TokenStream, input: TokenStream) -> TokenStream {
let args = args.to_string();
let mut input = input.to_string();

let ast = syn::parse_item(&input).unwrap();
let new_fn_ident = syn::Ident::new(String::from(ast.ident.as_ref()) + "_setup");
println!("{:?}", new_fn_ident);

// Get just the bare note string
let panics_note = args.trim_matches(&['=', ' ', '"'][..]);
let new_code: TokenStream = generate_kvm_setup(new_fn_ident).parse().unwrap();

input += new_code.to_string().as_str();
input.parse().unwrap()
}
59 changes: 37 additions & 22 deletions tests/kvm/bin.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(linkage, naked_functions, asm, const_fn)]
#![feature(linkage, naked_functions, asm, const_fn, test, proc_macro)]
// Execute using: RUSTFLAGS="-C soft-float -C relocation-model=static -C code-model=kernel" RUST_BACKTRACE=1 cargo test --verbose --test kvm -- --nocapture

extern crate kvm;
Expand All @@ -9,6 +9,10 @@ extern crate core;
#[macro_use]
extern crate klogger;

extern crate test_macros;
use test_macros::panics_note;
use test_macros::kvmattrs;

use kvm::{Capability, Exit, IoDirection, Segment, System, Vcpu, VirtualMachine};
use memmap::{Mmap, Protection};
use std::fs::File;
Expand All @@ -18,9 +22,13 @@ use x86::shared::control_regs::*;
use x86::shared::paging::*;
use x86::bits64::paging::*;

unsafe fn use_the_port() {
#[kvmattrs(identity_map)]
fn use_the_port() {
use_the_port_setup();
log!("1");
asm!("inb $0, %al" :: "i"(0x01) :: "volatile");
unsafe {
asm!("inb $0, %al" :: "i"(0x01) :: "volatile");
}
}

#[test]
Expand Down Expand Up @@ -177,35 +185,42 @@ fn io_example() {

// Actually run the VCPU

let mut ios_completes = 50;
println!("size of run {:?}", std::mem::size_of::<kvm::Run>());
let mut vm_is_done = false;
let mut new_regs = kvm::Regs::default();
while ios_completes > 0 {
while !vm_is_done {
{
let (run, mut regs) = unsafe { vcpu.run_regs() }.unwrap();
if run.exit_reason == Exit::Io {
let io = unsafe { *run.io() };
match io.direction {

IoDirection::In => {
if io.port == 0x3fd {
regs.rax = 0x20; // Mark serial line as ready to write
} else {
println!("IO on unknown port: {}", io.port);
match run.exit_reason {
Exit::Io => {
let io = unsafe { *run.io() };
match io.direction {
IoDirection::In => {
if io.port == 0x3fd {
regs.rax = 0x20; // Mark serial line as ready to write
} else {
println!("IO on unknown port: {}", io.port);
}
}
}
IoDirection::Out => {
if io.port == 0x3f8 {
println!("got char {:#?}", regs.rax as u8 as char);
IoDirection::Out => {
if io.port == 0x3f8 {
println!("got char {:#?}", regs.rax as u8 as char);
}
}
//println!("IOOut dont know what to do");
}
}
new_regs = regs;
Exit::Shutdown => {
println!("Shutting down");
vm_is_done = true;
}
_ => {
println!("Unknown exit reason: {:?}", run.exit_reason);
}
}

new_regs = regs;
}
vcpu.set_regs(&new_regs).unwrap();

ios_completes = ios_completes - 1;
}

// Ensure that the exit reason we get back indicates that the I/O
Expand Down

0 comments on commit 158cd8a

Please sign in to comment.