Skip to content

[test runner] Print QEMU output directly instead of waiting until it finishes #333

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
- name: Run api tests
run: cargo test -p bootloader_api
- name: Run integration tests
run: cargo test
run: cargo test -- --test-threads 1

# test feature gates (only on one OS is enough)
- name: Test with only UEFI feature
Expand Down
131 changes: 71 additions & 60 deletions tests/runner/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{io::Write, path::Path, process::Command};
use std::{io::Read, path::Path, process::Command};

const QEMU_ARGS: &[&str] = &[
"-device",
Expand All @@ -9,6 +9,7 @@ const QEMU_ARGS: &[&str] = &[
"none",
"--no-reboot",
];
const SEPARATOR: &str = "\n____________________________________\n";

pub fn run_test_kernel(kernel_binary_path: &str) {
run_test_kernel_with_ramdisk(kernel_binary_path, None)
Expand All @@ -30,7 +31,7 @@ pub fn run_test_kernel_with_ramdisk(kernel_binary_path: &str, ramdisk_path: Opti

// create a TFTP folder with the kernel executable and UEFI bootloader for
// UEFI PXE booting
let tftp_path = kernel_path.with_extension(".tftp");
let tftp_path = kernel_path.with_extension("tftp");
uefi_builder.create_pxe_tftp_folder(&tftp_path).unwrap();

run_test_kernel_on_uefi(&gpt_path);
Expand All @@ -54,73 +55,83 @@ pub fn run_test_kernel_with_ramdisk(kernel_binary_path: &str, ramdisk_path: Opti

#[cfg(feature = "uefi")]
pub fn run_test_kernel_on_uefi(out_gpt_path: &Path) {
let mut run_cmd = Command::new("qemu-system-x86_64");
run_cmd
.arg("-drive")
.arg(format!("format=raw,file={}", out_gpt_path.display()));
run_cmd.args(QEMU_ARGS);
run_cmd.arg("-bios").arg(ovmf_prebuilt::ovmf_pure_efi());

let child_output = run_cmd.output().unwrap();
strip_ansi_escapes::Writer::new(std::io::stderr())
.write_all(&child_output.stderr)
.unwrap();
strip_ansi_escapes::Writer::new(std::io::stderr())
.write_all(&child_output.stdout)
.unwrap();

match child_output.status.code() {
Some(33) => {} // success
Some(35) => panic!("Test failed"), // success
other => panic!("Test failed with unexpected exit code `{:?}`", other),
}
let ovmf_pure_efi = ovmf_prebuilt::ovmf_pure_efi();
let args = [
"-bios",
ovmf_pure_efi.to_str().unwrap(),
"-drive",
&format!("format=raw,file={}", out_gpt_path.display()),
];
run_qemu(args);
}

#[cfg(feature = "bios")]
pub fn run_test_kernel_on_bios(out_mbr_path: &Path) {
let mut run_cmd = Command::new("qemu-system-x86_64");
run_cmd
.arg("-drive")
.arg(format!("format=raw,file={}", out_mbr_path.display()));
run_cmd.args(QEMU_ARGS);

let child_output = run_cmd.output().unwrap();
strip_ansi_escapes::Writer::new(std::io::stderr())
.write_all(&child_output.stderr)
.unwrap();
strip_ansi_escapes::Writer::new(std::io::stderr())
.write_all(&child_output.stdout)
.unwrap();

match child_output.status.code() {
Some(33) => {} // success
Some(35) => panic!("Test failed"), // success
other => panic!("Test failed with unexpected exit code `{:?}`", other),
}
let args = [
"-drive",
&(format!("format=raw,file={}", out_mbr_path.display())),
];
run_qemu(args);
}

#[cfg(feature = "uefi")]
pub fn run_test_kernel_on_uefi_pxe(out_tftp_path: &Path) {
let ovmf_pure_efi = ovmf_prebuilt::ovmf_pure_efi();
let args = [
"-netdev",
&format!(
"user,id=net0,net=192.168.17.0/24,tftp={},bootfile=bootloader,id=net0",
out_tftp_path.display()
),
"-device",
"virtio-net-pci,netdev=net0",
"-bios",
ovmf_pure_efi.to_str().unwrap(),
];
run_qemu(args);
}

fn run_qemu<'a, A>(args: A)
where
A: IntoIterator<Item = &'a str>,
{
use std::process::Stdio;

let mut run_cmd = Command::new("qemu-system-x86_64");
run_cmd.arg("-netdev").arg(format!(
"user,id=net0,net=192.168.17.0/24,tftp={},bootfile=bootloader,id=net0",
out_tftp_path.display()
));
run_cmd.arg("-device").arg("virtio-net-pci,netdev=net0");
run_cmd.args(args);
run_cmd.args(QEMU_ARGS);
run_cmd.arg("-bios").arg(ovmf_prebuilt::ovmf_pure_efi());

let child_output = run_cmd.output().unwrap();
strip_ansi_escapes::Writer::new(std::io::stderr())
.write_all(&child_output.stderr)
.unwrap();
strip_ansi_escapes::Writer::new(std::io::stderr())
.write_all(&child_output.stdout)
.unwrap();

match child_output.status.code() {
Some(33) => {} // success
Some(35) => panic!("Test failed"),
other => panic!("Test failed with unexpected exit code `{:?}`", other),
let run_cmd_str = format!("{run_cmd:?}");

run_cmd.stdout(Stdio::piped());
run_cmd.stderr(Stdio::piped());

let mut child = run_cmd.spawn().unwrap();

let child_stdout = child.stdout.take().unwrap();
let mut child_stderr = child.stderr.take().unwrap();

let copy_stdout = std::thread::spawn(move || {
let print_cmd = format!("\nRunning {run_cmd_str}\n\n").into_bytes();
let mut output = print_cmd.chain(child_stdout).chain(SEPARATOR.as_bytes());
std::io::copy(
&mut output,
&mut strip_ansi_escapes::Writer::new(std::io::stdout()),
)
});
let copy_stderr = std::thread::spawn(move || {
std::io::copy(
&mut child_stderr,
&mut strip_ansi_escapes::Writer::new(std::io::stderr()),
)
});

let exit_status = child.wait().unwrap();
match exit_status.code() {
Some(33) => {} // success
Some(35) => panic!("Test failed"), // success
other => panic!("Test failed with unexpected exit code `{other:?}`"),
}

copy_stdout.join().unwrap().unwrap();
copy_stderr.join().unwrap().unwrap();
}