From c1064ad3169d2e6d4c2e73a2d5318db1f88421f6 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 20 Apr 2019 18:05:13 +0200 Subject: [PATCH 01/27] Add a .cargo/config file --- .cargo/config | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .cargo/config diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 00000000..7f2ad55d --- /dev/null +++ b/.cargo/config @@ -0,0 +1,5 @@ +[build] +target = "x86_64-blog_os.json" + +[target.'cfg(target_os = "none")'] +runner = "bootimage runner" From 06d1c9ff81295b0dc13f60e06454740d70a8c881 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 20 Apr 2019 18:06:02 +0200 Subject: [PATCH 02/27] Update CI scripts to use `cargo bootimage` --- .appveyor.yml | 2 +- .travis.yml | 5 ++--- azure-pipelines.yml | 6 ++---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 7c4bd1c0..ad43a498 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -73,4 +73,4 @@ before_test: - cargo install-latest cargo-xbuild bootimage test_script: - - bootimage build + - cargo bootimage diff --git a/.travis.yml b/.travis.yml index a6f58578..ff2ad445 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,9 +30,8 @@ cache: before_script: - rustup component add rust-src llvm-tools-preview - - (test -x $HOME/.cargo/bin/cargo-install-latest || cargo install cargo-install-latest) - - cargo install-latest cargo-xbuild bootimage cargo-cache + - cargo install cargo-xbuild bootimage cargo-cache --debug -Z install-upgrade script: - - bootimage build + - cargo bootimage - cargo cache --autoclean diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5b43f03d..8a3dcda1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -52,12 +52,10 @@ steps: - script: rustup component add rust-src llvm-tools-preview displayName: 'Install Rustup Components' -- script: | - cargo install cargo-xbuild --debug - cargo install bootimage --debug +- script: cargo install cargo-xbuild bootimage --debug displayName: 'Install cargo-xbuild and bootimage' -- script: bootimage build +- script: cargo bootimage displayName: 'Build' - script: rustup component add rustfmt From 528bbd4e9fb90cf1e730cefd932112702c078c2f Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Apr 2019 14:28:43 +0200 Subject: [PATCH 03/27] Add a `cargo xbuild` check on CI --- .appveyor.yml | 1 + .travis.yml | 1 + azure-pipelines.yml | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index ad43a498..423cc6db 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -73,4 +73,5 @@ before_test: - cargo install-latest cargo-xbuild bootimage test_script: + - cargo xbuild - cargo bootimage diff --git a/.travis.yml b/.travis.yml index ff2ad445..1e6697ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,5 +33,6 @@ before_script: - cargo install cargo-xbuild bootimage cargo-cache --debug -Z install-upgrade script: + - cargo xbuild - cargo bootimage - cargo cache --autoclean diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8a3dcda1..672a9add 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -55,9 +55,12 @@ steps: - script: cargo install cargo-xbuild bootimage --debug displayName: 'Install cargo-xbuild and bootimage' -- script: cargo bootimage +- script: cargo xbuild displayName: 'Build' +- script: cargo bootimage + displayName: 'Create Bootimage' + - script: rustup component add rustfmt displayName: 'Install Rustfmt' From 63b97fe1f85ec0ceec0a192c68a589089e6364ed Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 26 Apr 2019 11:23:32 +0200 Subject: [PATCH 04/27] Update bootloader to version 0.6.0 --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b30261fd..0bd7abdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,12 +22,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "blog_os" version = "0.1.0" dependencies = [ - "bootloader 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bootloader 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bootloader" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -215,7 +215,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum bootloader 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2735a1e3ddf16e6832fff86db617778dd3cecd5804fc8c2f20f448750d4c989c" +"checksum bootloader 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8654b1ebbd38d2a8687a451ad53466d01b5edc9d75ec63d676525a6103d77151" "checksum fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c6c16d316ccdac21a4dd648e314e76facbbaf316e83ca137d0857a9c07419d0" "checksum font8x8 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b81d84c3c978af7d05d31a2198af4b9ba956d819d15d8f6d58fc150e33f8dc1f" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" diff --git a/Cargo.toml b/Cargo.toml index affeefaf..26a84cf7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Philipp Oppermann "] edition = "2018" [dependencies] -bootloader = "0.5.1" +bootloader = "0.6.0" [profile.dev] panic = "abort" From b59720f6e112f5c1079ba649d50cdcf61139ef66 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 26 Apr 2019 11:27:36 +0200 Subject: [PATCH 05/27] Update Readme for new runner --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2c264b11..66739f4b 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,13 @@ cargo install cargo-xbuild bootimage Then you can build the project by running: ``` -bootimage build +cargo xbuild +``` + +To create a bootable disk image, run: + +``` +cargo bootimage ``` This creates a bootable disk image in the `target/x86_64-blog_os/debug` directory. @@ -33,7 +39,7 @@ You can run the disk image in [QEMU] through: [QEMU]: https://www.qemu.org/ ``` -bootimage run +cargo xrun ``` Of course [QEMU] needs to be installed for this. From 23cbafab1c10d178043709d7104bdefae41f8c3a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Apr 2019 11:34:16 +0200 Subject: [PATCH 06/27] Reset code to post-03 branch --- Cargo.lock | 10 ------ Cargo.toml | 3 -- azure-pipelines.yml | 3 -- src/main.rs | 7 ++-- src/vga_buffer.rs | 83 --------------------------------------------- 5 files changed, 2 insertions(+), 104 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e409fe78..ea2cc2e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,13 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "array-init" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "array-init" version = "0.0.4" @@ -30,7 +22,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "blog_os" version = "0.1.0" dependencies = [ - "array-init 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "bootloader 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -247,7 +238,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum array-init 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c3cc8456d0ae81a8c76f59e384683a601548c38949a4bfcb65dd31ded5c75ff3" "checksum array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" diff --git a/Cargo.toml b/Cargo.toml index b466cccb..afad8301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,6 @@ spin = "0.4.9" version = "1.0" features = ["spin_no_std"] -[dev-dependencies] -array-init = "0.0.3" - [profile.dev] panic = "abort" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c6cc6936..5b43f03d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -60,9 +60,6 @@ steps: - script: bootimage build displayName: 'Build' -- script: cargo test - displayName: 'Unit Tests' - - script: rustup component add rustfmt displayName: 'Install Rustfmt' diff --git a/src/main.rs b/src/main.rs index 319b9942..18e03650 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,10 @@ -#![cfg_attr(not(test), no_std)] -#![cfg_attr(not(test), no_main)] -#![cfg_attr(test, allow(unused_imports))] +#![no_std] +#![no_main] use core::panic::PanicInfo; mod vga_buffer; -#[cfg(not(test))] #[no_mangle] pub extern "C" fn _start() -> ! { println!("Hello World{}", "!"); @@ -15,7 +13,6 @@ pub extern "C" fn _start() -> ! { } /// This function is called on panic. -#[cfg(not(test))] #[panic_handler] fn panic(info: &PanicInfo) -> ! { println!("{}", info); diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index d89b4948..94dbc4c6 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -169,86 +169,3 @@ pub fn _print(args: fmt::Arguments) { use core::fmt::Write; WRITER.lock().write_fmt(args).unwrap(); } - -#[cfg(test)] -mod test { - use super::*; - - fn construct_writer() -> Writer { - use std::boxed::Box; - - let buffer = construct_buffer(); - Writer { - column_position: 0, - color_code: ColorCode::new(Color::Blue, Color::Magenta), - buffer: Box::leak(Box::new(buffer)), - } - } - - fn construct_buffer() -> Buffer { - use array_init::array_init; - - Buffer { - chars: array_init(|_| array_init(|_| Volatile::new(empty_char()))), - } - } - - fn empty_char() -> ScreenChar { - ScreenChar { - ascii_character: b' ', - color_code: ColorCode::new(Color::Green, Color::Brown), - } - } - - #[test] - fn write_byte() { - let mut writer = construct_writer(); - writer.write_byte(b'X'); - writer.write_byte(b'Y'); - - for (i, row) in writer.buffer.chars.iter().enumerate() { - for (j, screen_char) in row.iter().enumerate() { - let screen_char = screen_char.read(); - if i == BUFFER_HEIGHT - 1 && j == 0 { - assert_eq!(screen_char.ascii_character, b'X'); - assert_eq!(screen_char.color_code, writer.color_code); - } else if i == BUFFER_HEIGHT - 1 && j == 1 { - assert_eq!(screen_char.ascii_character, b'Y'); - assert_eq!(screen_char.color_code, writer.color_code); - } else { - assert_eq!(screen_char, empty_char()); - } - } - } - } - - #[test] - fn write_formatted() { - use core::fmt::Write; - - let mut writer = construct_writer(); - writeln!(&mut writer, "a").unwrap(); - writeln!(&mut writer, "b{}", "c").unwrap(); - - for (i, row) in writer.buffer.chars.iter().enumerate() { - for (j, screen_char) in row.iter().enumerate() { - let screen_char = screen_char.read(); - if i == BUFFER_HEIGHT - 3 && j == 0 { - assert_eq!(screen_char.ascii_character, b'a'); - assert_eq!(screen_char.color_code, writer.color_code); - } else if i == BUFFER_HEIGHT - 2 && j == 0 { - assert_eq!(screen_char.ascii_character, b'b'); - assert_eq!(screen_char.color_code, writer.color_code); - } else if i == BUFFER_HEIGHT - 2 && j == 1 { - assert_eq!(screen_char.ascii_character, b'c'); - assert_eq!(screen_char.color_code, writer.color_code); - } else if i >= BUFFER_HEIGHT - 2 { - assert_eq!(screen_char.ascii_character, b' '); - assert_eq!(screen_char.color_code, writer.color_code); - } else { - assert_eq!(screen_char, empty_char()); - } - } - } - } -} From 0beb0d80f8e7667de82b0fed194bdac0128916e8 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 20 Apr 2019 18:29:43 +0200 Subject: [PATCH 07/27] Add a custom test runner --- src/main.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main.rs b/src/main.rs index 18e03650..d63dab29 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,8 @@ #![no_std] #![no_main] +#![feature(custom_test_frameworks)] +#![test_runner(crate::test_runner)] +#![reexport_test_harness_main = "test_main"] use core::panic::PanicInfo; @@ -9,9 +12,20 @@ mod vga_buffer; pub extern "C" fn _start() -> ! { println!("Hello World{}", "!"); + #[cfg(test)] + test_main(); + loop {} } +#[cfg(test)] +fn test_runner(tests: &[&dyn Fn()]) { + println!("Running {} tests", tests.len()); + for test in tests { + test(); + } +} + /// This function is called on panic. #[panic_handler] fn panic(info: &PanicInfo) -> ! { From 3aa6151729c19030ee947495f5d3d254d839f7e0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 20 Apr 2019 18:30:34 +0200 Subject: [PATCH 08/27] Add a trivial_assertion test --- src/main.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main.rs b/src/main.rs index d63dab29..f35b2fa4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,3 +32,10 @@ fn panic(info: &PanicInfo) -> ! { println!("{}", info); loop {} } + +#[test_case] +fn trivial_assertion() { + print!("trivial assertion... "); + assert_eq!(1, 1); + println!("[ok]"); +} From dbdd46da9a770470ed565bfa56edfd65925f4262 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 20 Apr 2019 18:32:58 +0200 Subject: [PATCH 09/27] Exit qemu after running tests --- Cargo.lock | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/main.rs | 15 ++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index f9be4ee3..ff21abbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "x86_64 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -41,6 +42,11 @@ dependencies = [ "xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cc" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fixedvec" version = "0.2.3" @@ -128,6 +134,16 @@ name = "rand_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "raw-cpuid" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -144,6 +160,27 @@ dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "skeptic" version = "0.5.0" @@ -224,6 +261,19 @@ dependencies = [ "ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "x86_64" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "xmas-elf" version = "0.6.2" @@ -242,6 +292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum bootloader 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8654b1ebbd38d2a8687a451ad53466d01b5edc9d75ec63d676525a6103d77151" +"checksum cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5f3fee5eeb60324c2781f1e41286bdee933850fff9b3c672587fed5ec58c83" "checksum fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c6c16d316ccdac21a4dd648e314e76facbbaf316e83ca137d0857a9c07419d0" "checksum font8x8 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b81d84c3c978af7d05d31a2198af4b9ba956d819d15d8f6d58fc150e33f8dc1f" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" @@ -255,8 +306,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" +"checksum raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30a9d219c32c9132f7be513c18be77c9881c7107d2ab5569d205a6a0f0e6dc7d" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum skeptic 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "061203a849117b0f7090baf8157aa91dac30545208fbb85166ac58b4ca33d89c" "checksum spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f" "checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" @@ -269,5 +324,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum x86_64 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f9258d7e2dd25008d69e8c9e9ee37865887a5e1e3d06a62f1cb3f6c209e6f177" +"checksum x86_64 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0a8201f52d2c7b373c7243dcdfb27c0dd5012f221ef6a126f507ee82005204" "checksum xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22678df5df766e8d1e5d609da69f0c3132d794edf6ab5e75e7abcd2270d4cf58" "checksum zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5" diff --git a/Cargo.toml b/Cargo.toml index 43d26d71..86fb1bd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" bootloader = "0.6.0" volatile = "0.2.3" spin = "0.4.9" +x86_64 = "0.5.2" [dependencies.lazy_static] version = "1.0" @@ -21,3 +22,4 @@ panic = "abort" [package.metadata.bootimage] default-target = "x86_64-blog_os.json" +test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04"] diff --git a/src/main.rs b/src/main.rs index f35b2fa4..7180308a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,7 @@ fn test_runner(tests: &[&dyn Fn()]) { for test in tests { test(); } + unsafe { exit_qemu(QemuExitCode::Success) }; } /// This function is called on panic. @@ -33,6 +34,20 @@ fn panic(info: &PanicInfo) -> ! { loop {} } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum QemuExitCode { + Success = 0x10, + Failed = 0x11, +} + +pub unsafe fn exit_qemu(exit_code: QemuExitCode) { + use x86_64::instructions::port::Port; + + let mut port = Port::new(0xf4); + port.write(exit_code as u32); +} + #[test_case] fn trivial_assertion() { print!("trivial assertion... "); From 98a0e605be80017866ab694cb083dab7387b8ee0 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 20 Apr 2019 18:34:30 +0200 Subject: [PATCH 10/27] Interpret exit code 33 as test success --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 86fb1bd3..4e17de9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,4 @@ panic = "abort" [package.metadata.bootimage] default-target = "x86_64-blog_os.json" test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04"] +test-success-exit-code = 33 # (0x10 << 1) | 1 From 155949380275ebd0f018436ea3caca3846ab0837 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 20 Apr 2019 18:40:25 +0200 Subject: [PATCH 11/27] Print test results over serial port --- Cargo.lock | 11 +++++++++++ Cargo.toml | 5 ++++- src/main.rs | 7 ++++--- src/serial.rs | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 src/serial.rs diff --git a/Cargo.lock b/Cargo.lock index ff21abbb..370d630a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,7 @@ dependencies = [ "bootloader 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "uart_16550 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "x86_64 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -209,6 +210,15 @@ dependencies = [ "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "uart_16550" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "x86_64 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-width" version = "0.1.5" @@ -316,6 +326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f" "checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +"checksum uart_16550 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b9392f60931fe3bf8f24e0a15ee4f51528770f1d64c48768ab66571334d95b0" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" "checksum ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f" diff --git a/Cargo.toml b/Cargo.toml index 4e17de9a..c5987010 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ bootloader = "0.6.0" volatile = "0.2.3" spin = "0.4.9" x86_64 = "0.5.2" +uart_16550 = "0.2.0" [dependencies.lazy_static] version = "1.0" @@ -22,5 +23,7 @@ panic = "abort" [package.metadata.bootimage] default-target = "x86_64-blog_os.json" -test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04"] +test-args = [ + "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "mon:stdio" +] test-success-exit-code = 33 # (0x10 << 1) | 1 diff --git a/src/main.rs b/src/main.rs index 7180308a..4798dec4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use core::panic::PanicInfo; +mod serial; mod vga_buffer; #[no_mangle] @@ -20,7 +21,7 @@ pub extern "C" fn _start() -> ! { #[cfg(test)] fn test_runner(tests: &[&dyn Fn()]) { - println!("Running {} tests", tests.len()); + serial_println!("Running {} tests", tests.len()); for test in tests { test(); } @@ -50,7 +51,7 @@ pub unsafe fn exit_qemu(exit_code: QemuExitCode) { #[test_case] fn trivial_assertion() { - print!("trivial assertion... "); + serial_print!("trivial assertion... "); assert_eq!(1, 1); - println!("[ok]"); + serial_println!("[ok]"); } diff --git a/src/serial.rs b/src/serial.rs new file mode 100644 index 00000000..e8807df8 --- /dev/null +++ b/src/serial.rs @@ -0,0 +1,37 @@ +use lazy_static::lazy_static; +use spin::Mutex; +use uart_16550::SerialPort; + +lazy_static! { + pub static ref SERIAL1: Mutex = { + let mut serial_port = unsafe { SerialPort::new(0x3F8) }; + serial_port.init(); + Mutex::new(serial_port) + }; +} + +#[doc(hidden)] +pub fn _print(args: ::core::fmt::Arguments) { + use core::fmt::Write; + SERIAL1 + .lock() + .write_fmt(args) + .expect("Printing to serial failed"); +} + +/// Prints to the host through the serial interface. +#[macro_export] +macro_rules! serial_print { + ($($arg:tt)*) => { + $crate::serial::_print(format_args!($($arg)*)); + }; +} + +/// Prints to the host through the serial interface, appending a newline. +#[macro_export] +macro_rules! serial_println { + () => ($crate::serial_print!("\n")); + ($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => ($crate::serial_print!( + concat!($fmt, "\n"), $($arg)*)); +} From 3ecf9a9b565040435ba4a8435d114af3ed4bdccb Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 20 Apr 2019 18:50:43 +0200 Subject: [PATCH 12/27] Exit qemu on panic --- src/main.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main.rs b/src/main.rs index 4798dec4..f8e09cbd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,12 +29,22 @@ fn test_runner(tests: &[&dyn Fn()]) { } /// This function is called on panic. +#[cfg(not(test))] #[panic_handler] fn panic(info: &PanicInfo) -> ! { println!("{}", info); loop {} } +#[cfg(test)] +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + serial_println!("[failed]\n"); + serial_println!("Error: {}\n", info); + unsafe { exit_qemu(QemuExitCode::Failed); } + loop {} +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u32)] pub enum QemuExitCode { From d4ebbc291a29210319c3a69931ec90b298ca99d4 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 20 Apr 2019 18:51:37 +0200 Subject: [PATCH 13/27] Hide QEMU in test mode --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c5987010..554e3482 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ panic = "abort" [package.metadata.bootimage] default-target = "x86_64-blog_os.json" test-args = [ - "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "mon:stdio" + "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "mon:stdio", + "-display", "none" ] test-success-exit-code = 33 # (0x10 << 1) | 1 From 4cbac20b40594d34d1c06773dbe50d8e2d7414df Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 20 Apr 2019 19:04:55 +0200 Subject: [PATCH 14/27] Add tests for the VGA buffer --- src/main.rs | 7 ------- src/vga_buffer.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index f8e09cbd..c715b7ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,10 +58,3 @@ pub unsafe fn exit_qemu(exit_code: QemuExitCode) { let mut port = Port::new(0xf4); port.write(exit_code as u32); } - -#[test_case] -fn trivial_assertion() { - serial_print!("trivial assertion... "); - assert_eq!(1, 1); - serial_println!("[ok]"); -} diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 94dbc4c6..e7d4ed5f 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -2,6 +2,7 @@ use core::fmt; use lazy_static::lazy_static; use spin::Mutex; use volatile::Volatile; +use crate::{serial_print, serial_println}; lazy_static! { /// A global `Writer` instance that can be used for printing to the VGA text buffer. @@ -169,3 +170,33 @@ pub fn _print(args: fmt::Arguments) { use core::fmt::Write; WRITER.lock().write_fmt(args).unwrap(); } + +#[test_case] +fn test_println_simple() { + serial_print!("test_println... "); + println!("test_println_simple output"); + serial_println!("[ok]"); +} + +#[test_case] +fn test_println_many() { + serial_print!("test_println_many... "); + for _ in 0..200 { + println!("test_println_many output"); + } + serial_println!("[ok]"); +} + +#[test_case] +fn test_println_output() { + serial_print!("test_println_output... "); + + let s = "Some test string that fits on a single line"; + println!("{}", s); + for (i, c) in s.chars().enumerate() { + let screen_char = WRITER.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read(); + assert_eq!(char::from(screen_char.ascii_character), c); + } + + serial_println!("[ok]"); +} From 46bb44b95d066bd73505613396042e759bc3769b Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sat, 20 Apr 2019 19:06:00 +0200 Subject: [PATCH 15/27] Run cargo fmt --- src/main.rs | 4 +++- src/vga_buffer.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index c715b7ca..e76ebb8e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,7 +41,9 @@ fn panic(info: &PanicInfo) -> ! { fn panic(info: &PanicInfo) -> ! { serial_println!("[failed]\n"); serial_println!("Error: {}\n", info); - unsafe { exit_qemu(QemuExitCode::Failed); } + unsafe { + exit_qemu(QemuExitCode::Failed); + } loop {} } diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index e7d4ed5f..d53fcbe4 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -1,8 +1,8 @@ +use crate::{serial_print, serial_println}; use core::fmt; use lazy_static::lazy_static; use spin::Mutex; use volatile::Volatile; -use crate::{serial_print, serial_println}; lazy_static! { /// A global `Writer` instance that can be used for printing to the VGA text buffer. From d0629ba5a22ac71f14e3e2f87a5b623f9e129311 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 22 Apr 2019 11:48:50 +0200 Subject: [PATCH 16/27] Import serial_println only in test mode --- src/vga_buffer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index d53fcbe4..fcf95893 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -1,9 +1,11 @@ -use crate::{serial_print, serial_println}; use core::fmt; use lazy_static::lazy_static; use spin::Mutex; use volatile::Volatile; +#[cfg(test)] +use crate::{serial_print, serial_println}; + lazy_static! { /// A global `Writer` instance that can be used for printing to the VGA text buffer. /// From 3b13211579fcd1f195f7a1acda3b71f862bc50ae Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 22 Apr 2019 11:52:22 +0200 Subject: [PATCH 17/27] Add the skeleton for an integration test --- tests/basic_boot.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/basic_boot.rs diff --git a/tests/basic_boot.rs b/tests/basic_boot.rs new file mode 100644 index 00000000..52ff9810 --- /dev/null +++ b/tests/basic_boot.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] +#![feature(custom_test_frameworks)] +#![test_runner(crate::test_runner)] +#![reexport_test_harness_main = "test_main"] + +use core::panic::PanicInfo; + +#[no_mangle] // don't mangle the name of this function +pub extern "C" fn _start() -> ! { + test_main(); + + loop {} +} + +fn test_runner(tests: &[&dyn Fn()]) { + unimplemented!(); +} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} From 48e1f72d2390d7dd9a2556ca15a8f49f4862bd9c Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 22 Apr 2019 12:06:16 +0200 Subject: [PATCH 18/27] Split off a library --- src/lib.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 36 +++-------------------------------- 2 files changed, 58 insertions(+), 33 deletions(-) create mode 100644 src/lib.rs diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..96db9bcb --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,55 @@ +#![no_std] +#![cfg_attr(test, no_main)] +#![feature(custom_test_frameworks)] +#![test_runner(crate::test_runner)] +#![reexport_test_harness_main = "test_main"] + +use core::panic::PanicInfo; + +pub mod serial; +pub mod vga_buffer; + +pub fn test_runner(tests: &[&dyn Fn()]) { + serial_println!("Running {} tests", tests.len()); + for test in tests { + test(); + } + unsafe { exit_qemu(QemuExitCode::Success) }; +} + +pub fn test_panic_handler(info: &PanicInfo) -> ! { + serial_println!("[failed]\n"); + serial_println!("Error: {}\n", info); + unsafe { + exit_qemu(QemuExitCode::Failed); + } + loop {} +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum QemuExitCode { + Success = 0x10, + Failed = 0x11, +} + +pub unsafe fn exit_qemu(exit_code: QemuExitCode) { + use x86_64::instructions::port::Port; + + let mut port = Port::new(0xf4); + port.write(exit_code as u32); +} + +/// Entry point for `cargo xtest` +#[cfg(test)] +#[no_mangle] +pub extern "C" fn _start() -> ! { + test_main(); + loop {} +} + +#[cfg(test)] +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + test_panic_handler(info) +} diff --git a/src/main.rs b/src/main.rs index e76ebb8e..6e7f82d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,12 @@ #![no_std] #![no_main] #![feature(custom_test_frameworks)] -#![test_runner(crate::test_runner)] +#![test_runner(blog_os::test_runner)] #![reexport_test_harness_main = "test_main"] +use blog_os::println; use core::panic::PanicInfo; -mod serial; -mod vga_buffer; - #[no_mangle] pub extern "C" fn _start() -> ! { println!("Hello World{}", "!"); @@ -19,15 +17,6 @@ pub extern "C" fn _start() -> ! { loop {} } -#[cfg(test)] -fn test_runner(tests: &[&dyn Fn()]) { - serial_println!("Running {} tests", tests.len()); - for test in tests { - test(); - } - unsafe { exit_qemu(QemuExitCode::Success) }; -} - /// This function is called on panic. #[cfg(not(test))] #[panic_handler] @@ -39,24 +28,5 @@ fn panic(info: &PanicInfo) -> ! { #[cfg(test)] #[panic_handler] fn panic(info: &PanicInfo) -> ! { - serial_println!("[failed]\n"); - serial_println!("Error: {}\n", info); - unsafe { - exit_qemu(QemuExitCode::Failed); - } - loop {} -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u32)] -pub enum QemuExitCode { - Success = 0x10, - Failed = 0x11, -} - -pub unsafe fn exit_qemu(exit_code: QemuExitCode) { - use x86_64::instructions::port::Port; - - let mut port = Port::new(0xf4); - port.write(exit_code as u32); + blog_os::test_panic_handler(info) } From a2d628cabe80af76a83b702a88cca20e2c52505d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 22 Apr 2019 13:20:22 +0200 Subject: [PATCH 19/27] Complete the basic_boot integration test --- tests/basic_boot.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/basic_boot.rs b/tests/basic_boot.rs index 52ff9810..d737b4da 100644 --- a/tests/basic_boot.rs +++ b/tests/basic_boot.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] #![feature(custom_test_frameworks)] -#![test_runner(crate::test_runner)] +#![test_runner(blog_os::test_runner)] #![reexport_test_harness_main = "test_main"] use core::panic::PanicInfo; @@ -13,11 +13,7 @@ pub extern "C" fn _start() -> ! { loop {} } -fn test_runner(tests: &[&dyn Fn()]) { - unimplemented!(); -} - #[panic_handler] fn panic(info: &PanicInfo) -> ! { - loop {} + blog_os::test_panic_handler(info) } From cdd91df869ba54ae759374e8dbf28188e8044ba2 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 22 Apr 2019 13:25:09 +0200 Subject: [PATCH 20/27] Run cargo xtest on CI --- .appveyor.yml | 1 + .travis.yml | 1 + azure-pipelines.yml | 3 +++ 3 files changed, 5 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index 423cc6db..e2d5851d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -75,3 +75,4 @@ before_test: test_script: - cargo xbuild - cargo bootimage + - cargo xtest diff --git a/.travis.yml b/.travis.yml index 1e6697ca..b2daef83 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,4 +35,5 @@ before_script: script: - cargo xbuild - cargo bootimage + - cargo xtest - cargo cache --autoclean diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 672a9add..9c81050f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -61,6 +61,9 @@ steps: - script: cargo bootimage displayName: 'Create Bootimage' +- script: cargo xtest + displayName: 'Test' + - script: rustup component add rustfmt displayName: 'Install Rustfmt' From c2f36e086e9efb1ef3c4161fafe54b6073fc9480 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 22 Apr 2019 14:09:20 +0200 Subject: [PATCH 21/27] Install qemu on CI --- .appveyor.yml | 11 +++++++++++ .travis.yml | 11 +++++++++++ azure-pipelines.yml | 20 ++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index e2d5851d..71c10ea7 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -55,6 +55,17 @@ install: - set PATH=%PATH%;%USERPROFILE%\.cargo\bin - rustc -vV - cargo -vV + # Install qemu + - echo %cd% + - mkdir "C:\Program Files\qemu" + - cd "C:\Program Files\qemu" + - if %target%==i686-pc-windows-msvc appveyor DownloadFile "https://qemu.weilnetz.de/w32/2018/qemu-w32-setup-20180801.exe" -FileName "qemu-setup.exe" + - if %target%==i686-pc-windows-gnu appveyor DownloadFile "https://qemu.weilnetz.de/w32/2018/qemu-w32-setup-20180801.exe" -FileName "qemu-setup.exe" + - if %target%==x86_64-pc-windows-msvc appveyor DownloadFile "https://qemu.weilnetz.de/w64/2018/qemu-w64-setup-20180801.exe" -FileName "qemu-setup.exe" + - if %target%==x86_64-pc-windows-gnu appveyor DownloadFile "https://qemu.weilnetz.de/w64/2018/qemu-w64-setup-20180801.exe" -FileName "qemu-setup.exe" + - 7z x qemu-setup.exe + - set PATH=%PATH%;C:\Program Files\qemu + - cd "C:\projects\blog-os" ## Build Script ## diff --git a/.travis.yml b/.travis.yml index b2daef83..0d1ba227 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,17 @@ cache: directories: - $HOME/.cargo +addons: + apt: + packages: + - qemu-system-x86 + homebrew: + packages: + - qemu + +install: + - if [ $TRAVIS_OS_NAME = windows ]; then choco install qemu; export PATH="/c/Program Files/qemu:$PATH"; fi + before_script: - rustup component add rust-src llvm-tools-preview - cargo install cargo-xbuild bootimage cargo-cache --debug -Z install-upgrade diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9c81050f..577b7e35 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -61,6 +61,26 @@ steps: - script: cargo bootimage displayName: 'Create Bootimage' +- script: sudo apt update && sudo apt install qemu-system-x86 + condition: eq( variables['Agent.OS'], 'Linux' ) + displayName: 'Install QEMU (Linux)' + +- script: | + export HOMEBREW_NO_AUTO_UPDATE=1 + export HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK=1 + export HOMEBREW_NO_INSTALL_CLEANUP=1 + brew install qemu + condition: eq( variables['Agent.OS'], 'Darwin' ) + displayName: 'Install QEMU (macOS)' + +- script: | + choco install qemu --limit-output --no-progress + echo ##vso[task.setvariable variable=PATH;]%PATH%;C:\Program Files\qemu + set PATH=%PATH%;C:\Program Files\qemu + qemu-system-x86_64 --version + condition: eq( variables['Agent.OS'], 'Windows_NT' ) + displayName: 'Install QEMU (Windows)' + - script: cargo xtest displayName: 'Test' From 2e5f192f35d1c7896c33849374d5d865c541db7d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 23 Apr 2019 16:50:07 +0200 Subject: [PATCH 22/27] Make exit_qemu safe --- src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 96db9bcb..83169fe7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,15 +14,13 @@ pub fn test_runner(tests: &[&dyn Fn()]) { for test in tests { test(); } - unsafe { exit_qemu(QemuExitCode::Success) }; + exit_qemu(QemuExitCode::Success); } pub fn test_panic_handler(info: &PanicInfo) -> ! { serial_println!("[failed]\n"); serial_println!("Error: {}\n", info); - unsafe { - exit_qemu(QemuExitCode::Failed); - } + exit_qemu(QemuExitCode::Failed); loop {} } @@ -33,11 +31,13 @@ pub enum QemuExitCode { Failed = 0x11, } -pub unsafe fn exit_qemu(exit_code: QemuExitCode) { +pub fn exit_qemu(exit_code: QemuExitCode) { use x86_64::instructions::port::Port; - let mut port = Port::new(0xf4); - port.write(exit_code as u32); + unsafe { + let mut port = Port::new(0xf4); + port.write(exit_code as u32); + } } /// Entry point for `cargo xtest` From a55ced6b7f22f21686e9ded2aa9fd7f1c39e31c7 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 23 Apr 2019 16:50:19 +0200 Subject: [PATCH 23/27] Add a basic test to basic_boot --- tests/basic_boot.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/basic_boot.rs b/tests/basic_boot.rs index d737b4da..eb4a25ed 100644 --- a/tests/basic_boot.rs +++ b/tests/basic_boot.rs @@ -5,6 +5,7 @@ #![reexport_test_harness_main = "test_main"] use core::panic::PanicInfo; +use blog_os::{println, serial_print, serial_println}; #[no_mangle] // don't mangle the name of this function pub extern "C" fn _start() -> ! { @@ -17,3 +18,10 @@ pub extern "C" fn _start() -> ! { fn panic(info: &PanicInfo) -> ! { blog_os::test_panic_handler(info) } + +#[test_case] +fn test_println() { + serial_print!("test_println... "); + println!("test_println output"); + serial_println!("[ok]"); +} From ccb8b79ef81b986dae51dd5e2c2976e34b374187 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Tue, 23 Apr 2019 16:50:32 +0200 Subject: [PATCH 24/27] Add a panic handler test --- Cargo.toml | 4 +++ tests/panic_handler.rs | 69 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/panic_handler.rs diff --git a/Cargo.toml b/Cargo.toml index 554e3482..d984b6ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,10 @@ version = "0.1.0" authors = ["Philipp Oppermann "] edition = "2018" +[[test]] +name = "panic_handler" +harness = false + [dependencies] bootloader = "0.6.0" volatile = "0.2.3" diff --git a/tests/panic_handler.rs b/tests/panic_handler.rs new file mode 100644 index 00000000..c15df7d3 --- /dev/null +++ b/tests/panic_handler.rs @@ -0,0 +1,69 @@ +#![no_std] +#![no_main] +#![feature(panic_info_message)] + +use core::{fmt::{self, Write}, panic::PanicInfo}; +use blog_os::{serial_print, serial_println, QemuExitCode, exit_qemu}; + +const MESSAGE: &str = "Example panic message from panic_handler test"; +const PANIC_LINE: u32 = 14; // adjust this when moving the `panic!` call + +#[no_mangle] +pub extern "C" fn _start() -> ! { + serial_print!("panic_handler... "); + panic!(MESSAGE); // must be in line `PANIC_LINE` +} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + check_location(info); + check_message(info); + + serial_println!("[ok]"); + exit_qemu(QemuExitCode::Success); + loop {} +} + +fn fail(error: &str) -> ! { + serial_println!("[failed]"); + serial_println!("{}", error); + exit_qemu(QemuExitCode::Failed); + loop {} +} + +fn check_location(info: &PanicInfo) { + let location = info.location().unwrap_or_else(|| fail("no location")); + if location.file() != file!() { + fail("file name wrong"); + } + if location.line() != PANIC_LINE { + fail("file line wrong"); + } +} + +fn check_message(info: &PanicInfo) { + let message = info.message().unwrap_or_else(|| fail("no message")); + let mut compare_message = CompareMessage { equals: false }; + write!(&mut compare_message, "{}", message).unwrap_or_else(|_| fail("write failed")); + if !compare_message.equals { + fail("message not equal to expected message"); + } +} + +/// Compares a `fmt::Arguments` instance with the `MESSAGE` string +/// +/// To use this type, write the `fmt::Arguments` instance to it using the +/// `write` macro. If a message component matches `MESSAGE`, the equals +/// field is set to true. +struct CompareMessage { + equals: bool, +} + +impl fmt::Write for CompareMessage { + fn write_str(&mut self, s: &str) -> fmt::Result { + if s == MESSAGE { + self.equals = true; + } + Ok(()) + } +} \ No newline at end of file From c8087cdd6f2c11c21a4acc1350482c1e8d505202 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Apr 2019 12:23:45 +0200 Subject: [PATCH 25/27] Update Readme for `Testing` post --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 216c9bcc..9392cf06 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# Blog OS (Unit Testing) +# Blog OS (Testing) [![Azure Pipelines CI build](https://img.shields.io/azure-devops/build/phil-opp/blog_os/1/post-04.svg?label=Build&style=flat-square)](https://dev.azure.com/phil-opp/blog_os/_build?definitionId=1) -This repository contains the source code for the [Unit Testing][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series. +This repository contains the source code for the [Testing][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series. -[post]: https://os.phil-opp.com/unit-testing/ +[post]: https://os.phil-opp.com/testing/ **Check out the [master branch](https://github.com/phil-opp/blog_os) for more information.** @@ -54,7 +54,7 @@ Where `sdX` is the device name of your USB stick. **Be careful** to choose the c ## Testing -To run the unit tests on the host system, execute `cargo test`. +To run the unit and integration tests, execute `cargo xtest`. ## License The source code is dual-licensed under MIT or the Apache License (Version 2.0). From 8374aa6ab6d9c0238d767ea37c8cf68cac7e0e06 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Apr 2019 18:10:59 +0200 Subject: [PATCH 26/27] Run cargo fmt --- tests/basic_boot.rs | 2 +- tests/panic_handler.rs | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/basic_boot.rs b/tests/basic_boot.rs index eb4a25ed..f5cf85e5 100644 --- a/tests/basic_boot.rs +++ b/tests/basic_boot.rs @@ -4,8 +4,8 @@ #![test_runner(blog_os::test_runner)] #![reexport_test_harness_main = "test_main"] -use core::panic::PanicInfo; use blog_os::{println, serial_print, serial_println}; +use core::panic::PanicInfo; #[no_mangle] // don't mangle the name of this function pub extern "C" fn _start() -> ! { diff --git a/tests/panic_handler.rs b/tests/panic_handler.rs index c15df7d3..0e1e6819 100644 --- a/tests/panic_handler.rs +++ b/tests/panic_handler.rs @@ -2,8 +2,11 @@ #![no_main] #![feature(panic_info_message)] -use core::{fmt::{self, Write}, panic::PanicInfo}; -use blog_os::{serial_print, serial_println, QemuExitCode, exit_qemu}; +use blog_os::{exit_qemu, serial_print, serial_println, QemuExitCode}; +use core::{ + fmt::{self, Write}, + panic::PanicInfo, +}; const MESSAGE: &str = "Example panic message from panic_handler test"; const PANIC_LINE: u32 = 14; // adjust this when moving the `panic!` call @@ -11,11 +14,11 @@ const PANIC_LINE: u32 = 14; // adjust this when moving the `panic!` call #[no_mangle] pub extern "C" fn _start() -> ! { serial_print!("panic_handler... "); - panic!(MESSAGE); // must be in line `PANIC_LINE` + panic!(MESSAGE); // must be in line `PANIC_LINE` } #[panic_handler] -fn panic(info: &PanicInfo) -> ! { +fn panic(info: &PanicInfo) -> ! { check_location(info); check_message(info); @@ -51,7 +54,7 @@ fn check_message(info: &PanicInfo) { } /// Compares a `fmt::Arguments` instance with the `MESSAGE` string -/// +/// /// To use this type, write the `fmt::Arguments` instance to it using the /// `write` macro. If a message component matches `MESSAGE`, the equals /// field is set to true. @@ -66,4 +69,4 @@ impl fmt::Write for CompareMessage { } Ok(()) } -} \ No newline at end of file +} From ad99afae988fba54411ad7130d28946a02188c78 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 25 Apr 2019 18:18:39 +0200 Subject: [PATCH 27/27] Fix panic_handler test by fixing line number --- tests/panic_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/panic_handler.rs b/tests/panic_handler.rs index 0e1e6819..5a55537c 100644 --- a/tests/panic_handler.rs +++ b/tests/panic_handler.rs @@ -9,7 +9,7 @@ use core::{ }; const MESSAGE: &str = "Example panic message from panic_handler test"; -const PANIC_LINE: u32 = 14; // adjust this when moving the `panic!` call +const PANIC_LINE: u32 = 17; // adjust this when moving the `panic!` call #[no_mangle] pub extern "C" fn _start() -> ! {