Update testing posts

This commit is contained in:
Philipp Oppermann
2018-11-18 15:16:19 +01:00
parent bf413d3baa
commit f5aea8f015
4 changed files with 24 additions and 49 deletions

View File

@@ -111,13 +111,13 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
The test framework seems to work as intended. We don't have any tests yet, but we already get a test result summary.
### Silencing the Warnings
We get a few warnings about unused items, because we no longer compile our `_start` function. To silence such unused code warnings, we can add the following to the top of our `main.rs`:
We get a few warnings about unused imports, because we no longer compile our `_start` function. To silence such unused code warnings, we can add the following to the top of our `main.rs`:
```
#![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))]
#![cfg_attr(test, allow(unused_imports))]
```
Like before, the `cfg_attr` attribute sets the passed attribute if the passed condition holds. Here, we set the `allow(…)` attribute when compiling in test mode. We use the `allow` attribute to disable warnings for the `dead_code`, `unused_macro`, and `unused_import` _lints_.
Like before, the `cfg_attr` attribute sets the passed attribute if the passed condition holds. Here, we set the `allow(…)` attribute when compiling in test mode. We use the `allow` attribute to disable warnings for the `unused_import` _lint_.
Lints are classes of warnings, for example `dead_code` for unused code or `missing-docs` for missing documentation. Lints can be set to four different states:
@@ -131,15 +131,13 @@ Some lints are `allow` by default (such as `missing-docs`), others are `warn` by
[clippy]: https://github.com/rust-lang-nursery/rust-clippy
### Including the Standard Library
Unit tests run on the host machine, so it's possible to use the complete standard library inside them. To link the standard library in test mode, we can add the following to our `main.rs`:
Unit tests run on the host machine, so it's possible to use the complete standard library inside them. To link the standard library in test mode, we can make the `#![no_std]` attribute conditional through `cfg_attr` too:
```rust
#[cfg(test)]
extern crate std;
```diff
-#![no_std]
+#![cfg_attr(not(test), no_std)]
```
Rust knows where to find the `std` crate, so no modification to the `Cargo.toml` is required.
## Testing the VGA Module
Now that we have set up the test framework, we can add a first unit test for our `vga_buffer` module:

View File

@@ -103,12 +103,13 @@ Like with the [VGA text buffer][vga lazy-static], we use `lazy_static` and a spi
To make the serial port easily usable, we add `serial_print!` and `serial_println!` macros:
```rust
pub fn print(args: ::core::fmt::Arguments) {
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)*));
@@ -116,10 +117,11 @@ macro_rules! serial_print {
}
/// Prints to the host through the serial interface, appending a newline.
#[macro_export]
macro_rules! serial_println {
() => (serial_print!("\n"));
($fmt:expr) => (serial_print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (serial_print!(concat!($fmt, "\n"), $($arg)*));
() => ($crate::serial_print!("\n"));
($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => ($crate::serial_print!(concat!($fmt, "\n"), $($arg)*));
}
```
@@ -132,7 +134,8 @@ Now we can print to the serial interface in our `main.rs`:
```rust
// in src/main.rs
#[macro_use]
use crate::serial_println;
mod serial;
#[cfg(not(test))]
@@ -145,7 +148,7 @@ pub extern "C" fn _start() -> ! {
}
```
Note that we need to add the `#[macro_use]` attribute to the `mod serial` declaration, because otherwise the `serial_println` macro is not imported.
Note that the `serial_println` macro lives directly under the root namespace because we used the `#[macro_export]` attribute, so importing it through `use crate::serial::serial_println` will not work.
### QEMU Arguments
@@ -371,7 +374,7 @@ Cargo supports hybrid projects that are both a library and a binary. We only nee
```rust
// src/lib.rs
#![no_std] // don't link the Rust standard library
#![cfg_attr(not(test), no_std)] // don't link the Rust standard library
extern crate bootloader;
extern crate spin;
@@ -382,8 +385,6 @@ extern crate x86_64;
#[cfg(test)]
extern crate array_init;
#[cfg(test)]
extern crate std;
// NEW: We need to add `pub` here to make them accessible from the outside
pub mod vga_buffer;
@@ -405,10 +406,10 @@ pub unsafe fn exit_qemu() {
#![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))]
// NEW: Add the library as dependency (same crate name as executable)
#[macro_use]
extern crate blog_os;
use core::panic::PanicInfo;
use blog_os::println;
/// This function is the entry point, since the linker looks for a function
/// named `_start` by default.
@@ -429,29 +430,7 @@ fn panic(info: &PanicInfo) -> ! {
}
```
So we move everything except `_start` and `panic` to `lib.rs`, make the `vga_buffer` and `serial` modules public, and add an `extern crate` definition to our `main.rs`.
This doesn't compile yet, because Rust's macros are not exported over crate boundaries by default. To export our printing macros, we need to add the `#[macro_export]` attribute to them:
```rust
// in src/vga_buffer.rs
#[macro_export]
macro_rules! print {}
#[macro_export]
macro_rules! println {}
// in src/serial.rs
#[macro_export]
macro_rules! serial_print {}
#[macro_export]
macro_rules! serial_println {}
```
Now everything should work exactly as before, including `bootimage run` and `cargo test`.
So we move everything except `_start` and `panic` to `lib.rs`, make the `vga_buffer` and `serial` modules public, and add an `extern crate` definition to our `main.rs`. Everything should work exactly as before, including `bootimage run` and `cargo test`.
### Test Basic Boot
@@ -465,11 +444,10 @@ We are finally able to create our first integration test executable. We start si
#![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))]
// add the library as dependency (same crate name as executable)
#[macro_use]
extern crate blog_os;
use core::panic::PanicInfo;
use blog_os::exit_qemu;
use blog_os::{exit_qemu, serial_println};
/// This function is the entry point, since the linker looks for a function
/// named `_start` by default.
@@ -529,11 +507,10 @@ To test that our panic handler is really invoked on a panic, we create a `test-p
#![cfg_attr(not(test), no_main)]
#![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))]
#[macro_use]
extern crate blog_os;
use core::panic::PanicInfo;
use blog_os::exit_qemu;
use blog_os::{exit_qemu, serial_println};
#[cfg(not(test))]
#[no_mangle]