mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 14:27:49 +00:00
Update testing posts
This commit is contained in:
@@ -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:
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![no_std] // don't link the Rust standard library
|
||||
#![cfg_attr(not(test), no_main)] // disable all Rust-level entry points
|
||||
#![cfg_attr(test, allow(dead_code, unused_macros, unused_imports))]
|
||||
#![cfg_attr(test, allow(unused_imports))]
|
||||
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ lazy_static! {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn print(args: ::core::fmt::Arguments) {
|
||||
pub fn _print(args: ::core::fmt::Arguments) {
|
||||
use core::fmt::Write;
|
||||
use x86_64::instructions::interrupts;
|
||||
|
||||
@@ -26,7 +26,7 @@ pub fn print(args: ::core::fmt::Arguments) {
|
||||
#[macro_export]
|
||||
macro_rules! serial_print {
|
||||
($($arg:tt)*) => {
|
||||
$crate::serial::print(format_args!($($arg)*));
|
||||
$crate::serial::_print(format_args!($($arg)*));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user