Refactor CI and OSDK tests, add OSDK publish workflow

This commit is contained in:
Zhang Junyang
2024-02-26 11:08:42 +08:00
committed by Tate, Hongliang Tian
parent eb42383532
commit 6694f08eac
40 changed files with 441 additions and 318 deletions

View File

@ -1,4 +1,4 @@
name: Integration test
name: Asterinas CI
on:
pull_request:
@ -7,7 +7,43 @@ on:
- main
jobs:
test:
aster-lint:
runs-on: ubuntu-latest
timeout-minutes: 10
container: asterinas/asterinas:0.3.0
steps:
- run: echo "Running in asterinas/asterinas:0.3.0"
- uses: actions/checkout@v3
- run: make install_osdk
- name: Check
id: check
run: make check
aster-unit-test:
runs-on: ubuntu-latest
timeout-minutes: 15
container: asterinas/asterinas:0.3.0
steps:
- run: echo "Running in asterinas/asterinas:0.3.0"
- uses: actions/checkout@v3
- run: make install_osdk
- name: Usermode Unit test
id: usermode_unit_test
run: make test
- name: Ktest Unit Test
id: ktest_unit_test
run: make update_initramfs && make ktest
# TODO: add component check.
aster-integration-test:
runs-on: ubuntu-latest
timeout-minutes: 30
container: asterinas/asterinas:0.3.1
@ -49,4 +85,3 @@ jobs:
- name: Regression Test (MicroVM)
id: regression_test_linux
run: make run AUTO_TEST=regression ENABLE_KVM=0 QEMU_MACHINE=microvm RELEASE_MODE=1

View File

@ -6,7 +6,7 @@ on:
paths:
- VERSION
branches:
- 'main'
- main
jobs:
docker:

View File

@ -2,6 +2,8 @@ name: OSDK CI
on:
pull_request:
paths:
- osdk/**
push:
branches:
- main
@ -16,9 +18,9 @@ jobs:
- uses: actions/checkout@v3
- name: Format check
id: fmt_check
run: cd osdk && cargo check && cargo clippy
- name: Lint
id: lint
run: cd osdk && cargo clippy -- -D warnings
- name: Unit test
id: unit_test

23
.github/workflows/osdk_publish.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: OSDK Publish
on:
workflow_dispatch:
push:
paths:
- osdk/Cargo.toml
branches:
- main
jobs:
osdk-publish:
runs-on: ubuntu-latest
timeout-minutes: 10
container: asterinas/asterinas:0.3.0
steps:
- uses: actions/checkout@v3
- uses: katyo/publish-crates@v2
with:
path: './osdk'
args: --no-verify
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}
ignore-unpublished-changes: true

View File

@ -3,6 +3,8 @@
A secure, fast, and general-purpose OS kernel written in Rust and compatible with Linux
</p>
[![Asterinas CI](https://github.com/asterinas/asterinas/actions/workflows/aster_ci.yml/badge.svg)](https://github.com/asterinas/asterinas/actions/workflows/aster_ci.yml)
English | [中文版](README_CN.md)
## Introducing Asterinas

View File

@ -3,6 +3,8 @@
一个安全、快速、通用的操作系统内核使用Rust编写并与Linux兼容
</p>
[![Asterinas CI](https://github.com/asterinas/asterinas/actions/workflows/aster_ci.yml/badge.svg)](https://github.com/asterinas/asterinas/actions/workflows/aster_ci.yml)
[English](README.md) | 中文版
## 初见星绽

3
osdk/.gitignore vendored
View File

@ -8,6 +8,3 @@ target/
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# Integration test generated files
workspace_*

6
osdk/Cargo.lock generated
View File

@ -88,9 +88,9 @@ checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "bstr"
version = "1.9.0"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
dependencies = [
"memchr",
"regex-automata",
@ -119,7 +119,7 @@ dependencies = [
[[package]]
name = "cargo-osdk"
version = "0.1.5"
version = "0.2.0"
dependencies = [
"assert_cmd",
"clap",

View File

@ -1,6 +1,6 @@
[package]
name = "cargo-osdk"
version = "0.1.5"
version = "0.2.0"
edition = "2021"
description = "Accelerate OS development with Asterinas OSDK"
license = "MPL-2.0"

View File

@ -1,6 +1,7 @@
# Accelerate OS development with Asterinas OSDK
[![Crates.io](https://img.shields.io/crates/v/cargo-osdk.svg)](https://crates.io/crates/cargo-osdk)
[![OSDK CI](https://github.com/asterinas/asterinas/actions/workflows/osdk_ci.yml/badge.svg)](https://github.com/asterinas/asterinas/actions/workflows/osdk_ci.yml)
### What is it?

View File

@ -1,3 +1,2 @@
[toolchain]
channel = "nightly-2023-12-01"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
channel = "stable"

View File

@ -0,0 +1,4 @@
[package]
name = "#NAME#"
version = "#VERSION#"
edition = "2021"

View File

@ -4,9 +4,7 @@
//! It will depend on the kernel crate.
//!
use std::{fs, path::Path, process, process::Command, str::FromStr};
use crate::{error::Errno, error_msg};
use std::{fs, path::Path, str::FromStr};
pub fn new_base_crate(
base_crate_path: impl AsRef<Path>,
@ -17,17 +15,32 @@ pub fn new_base_crate(
std::fs::remove_dir_all(&base_crate_path).unwrap();
}
let mut cmd = Command::new("cargo");
cmd.arg("new").arg("--bin").arg(base_crate_path.as_ref());
cmd.arg("--vcs").arg("none");
let dep_crate_version = {
let cargo_toml = dep_crate_path.as_ref().join("Cargo.toml");
let cargo_toml = fs::read_to_string(cargo_toml).unwrap();
let cargo_toml: toml::Value = toml::from_str(&cargo_toml).unwrap();
let dep_version = cargo_toml
.get("package")
.unwrap()
.as_table()
.unwrap()
.get("version")
.unwrap()
.as_str()
.unwrap();
dep_version.to_string()
};
if !cmd.status().unwrap().success() {
error_msg!(
"Failed to create base crate at: {:#?}",
base_crate_path.as_ref()
);
process::exit(Errno::CreateBaseCrate as _);
}
// Create the directory
fs::create_dir_all(&base_crate_path).unwrap();
// Create the src directory
fs::create_dir_all(base_crate_path.as_ref().join("src")).unwrap();
// Write Cargo.toml
let cargo_toml = include_str!("Cargo.toml.template");
let cargo_toml = cargo_toml.replace("#NAME#", &(dep_crate_name.to_string() + "-osdk-bin"));
let cargo_toml = cargo_toml.replace("#VERSION#", &dep_crate_version);
fs::write(base_crate_path.as_ref().join("Cargo.toml"), cargo_toml).unwrap();
// Set the current directory to the target osdk directory
let original_dir = std::env::current_dir().unwrap();
@ -44,7 +57,7 @@ pub fn new_base_crate(
// Overrite the main.rs file
let main_rs = include_str!("main.rs.template");
// Replace all occurence of `#TARGET_NAME#` with the `dep_crate_name`
let main_rs = main_rs.replace("#TARGET_NAME#", &dep_crate_name.replace("-", "_"));
let main_rs = main_rs.replace("#TARGET_NAME#", &dep_crate_name.replace('-', "_"));
fs::write("src/main.rs", main_rs).unwrap();
// Add dependencies to the Cargo.toml
@ -54,7 +67,7 @@ pub fn new_base_crate(
copy_manifest_configurations(base_crate_path);
// Get back to the original directory
std::env::set_current_dir(&original_dir).unwrap();
std::env::set_current_dir(original_dir).unwrap();
}
fn add_manifest_dependency(crate_name: &str, crate_path: impl AsRef<Path>) {
@ -65,6 +78,14 @@ fn add_manifest_dependency(crate_name: &str, crate_path: impl AsRef<Path>) {
toml::from_str(&content).unwrap()
};
// Check if "dependencies" key exists, create it if it doesn't
if !manifest.contains_key("dependencies") {
manifest.insert(
"dependencies".to_string(),
toml::Value::Table(toml::Table::new()),
);
}
let dependencies = manifest.get_mut("dependencies").unwrap();
let dep = toml::Table::from_str(&format!(

View File

@ -44,7 +44,7 @@ impl Bundle {
#[allow(dead_code)]
pub fn load(path: impl AsRef<Path>) -> Self {
let manifest_file_path = path.as_ref().join("bundle.toml");
let manifest_file_content = std::fs::read_to_string(&manifest_file_path).unwrap();
let manifest_file_content = std::fs::read_to_string(manifest_file_path).unwrap();
let manifest: BundleManifest = toml::from_str(&manifest_file_content).unwrap();
// TODO: check integrity of the loaded bundle.
Self {
@ -142,7 +142,7 @@ impl Bundle {
}
let file_name = vm_image.path.file_name().unwrap();
let copied_path = self.path.join(file_name);
std::fs::copy(&vm_image.path, &copied_path).unwrap();
std::fs::copy(&vm_image.path, copied_path).unwrap();
self.manifest.vm_image = Some(AsterVmImage {
path: file_name.into(),
typ: vm_image.typ.clone(),
@ -158,13 +158,13 @@ impl Bundle {
}
let file_name = aster_bin.path.file_name().unwrap();
let copied_path = self.path.join(file_name);
std::fs::copy(&aster_bin.path, &copied_path).unwrap();
std::fs::copy(&aster_bin.path, copied_path).unwrap();
self.manifest.aster_bin = Some(AsterBin {
path: file_name.into(),
typ: aster_bin.typ.clone(),
version: aster_bin.version.clone(),
sha256sum: aster_bin.sha256sum.clone(),
stripped: aster_bin.stripped.clone(),
stripped: aster_bin.stripped,
});
self.write_manifest_content();
}
@ -172,7 +172,7 @@ impl Bundle {
fn write_manifest_content(&self) {
let manifest_file_content = toml::to_string(&self.manifest).unwrap();
let manifest_file_path = self.path.join("bundle.toml");
std::fs::write(&manifest_file_path, manifest_file_content).unwrap();
std::fs::write(manifest_file_path, manifest_file_content).unwrap();
}
}

View File

@ -115,7 +115,7 @@ pub struct TestArgs {
pub struct CargoArgs {
#[arg(
long,
help = "The Cargo build profile (built-in candidates are 'debug', 'release' and 'dev')",
help = "The Cargo build profile (built-in candidates are 'debug', 'release', 'dev' and 'bench')",
default_value = "dev"
)]
pub profile: String,

View File

@ -49,7 +49,7 @@ pub fn make_install_bzimage(
setup_install_dir.join("bin").join("linux-bzimage-setup")
};
// Make the `bzImage`-compatible kernel image and place it in the boot directory.
let install_path = install_dir.as_ref().join(&target_name);
let install_path = install_dir.as_ref().join(target_name);
info!("Building bzImage");
println!("install_path: {:?}", install_path);
make_bzimage(&install_path, image_type, &aster_elf.path, &setup_bin);

View File

@ -25,7 +25,7 @@ pub fn create_bootdev_image(
// Clear or make the iso dir.
if iso_root.exists() {
fs::remove_dir_all(&iso_root).unwrap();
fs::remove_dir_all(iso_root).unwrap();
}
fs::create_dir_all(iso_root.join("boot").join("grub")).unwrap();
@ -41,12 +41,12 @@ pub fn create_bootdev_image(
// Make the kernel image and place it in the boot directory.
match protocol {
BootProtocol::LinuxLegacy32 | BootProtocol::LinuxEfiHandover64 => {
make_install_bzimage(&iso_root.join("boot"), &target_dir, aster_bin, protocol);
make_install_bzimage(iso_root.join("boot"), &target_dir, aster_bin, protocol);
}
BootProtocol::Multiboot | BootProtocol::Multiboot2 => {
// Copy the kernel image to the boot directory.
let target_path = iso_root.join("boot").join(&target_name);
fs::copy(&aster_bin.path, &target_path).unwrap();
fs::copy(&aster_bin.path, target_path).unwrap();
}
};
@ -63,7 +63,7 @@ pub fn create_bootdev_image(
protocol,
);
let grub_cfg_path = iso_root.join("boot").join("grub").join("grub.cfg");
fs::write(&grub_cfg_path, grub_cfg).unwrap();
fs::write(grub_cfg_path, grub_cfg).unwrap();
// Make the boot device CDROM image using `grub-mkrescue`.
let iso_path = &target_dir.as_ref().join(target_name.to_string() + ".iso");
@ -109,11 +109,11 @@ fn generate_grub_cfg(
let grub_cfg = grub_cfg.replace("#KERNEL_COMMAND_LINE#", kcmdline);
// Replace the grub commands according to the protocol selected.
let aster_bin_path_on_device = PathBuf::from("/boot")
.join(&target_name)
.join(target_name)
.into_os_string()
.into_string()
.unwrap();
let grub_cfg = match protocol {
match protocol {
BootProtocol::Multiboot => grub_cfg
.replace("#GRUB_CMD_KERNEL#", "multiboot")
.replace("#KERNEL#", &aster_bin_path_on_device)
@ -147,9 +147,7 @@ fn generate_grub_cfg(
"".to_owned()
},
),
};
grub_cfg
}
}
fn get_grub_mkrescue_version(grub_mkrescue: &PathBuf) -> String {

View File

@ -29,9 +29,9 @@ pub fn execute_build_command(config: &BuildConfig) {
std::fs::create_dir_all(&osdk_target_directory).unwrap();
}
let target_info = get_current_crate_info();
let bundle_path = osdk_target_directory.join(&target_info.name);
let bundle_path = osdk_target_directory.join(target_info.name);
let _bundle = create_base_and_build(&bundle_path, &osdk_target_directory, &config);
let _bundle = create_base_and_build(bundle_path, &osdk_target_directory, config);
}
pub fn create_base_and_build(
@ -43,12 +43,12 @@ pub fn create_base_and_build(
new_base_crate(
&base_crate_path,
&get_current_crate_info().name,
&get_current_crate_info().path,
get_current_crate_info().path,
);
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(&base_crate_path).unwrap();
let bundle = do_build(&bundle_path, &osdk_target_directory, &config);
std::env::set_current_dir(&original_dir).unwrap();
let bundle = do_build(&bundle_path, &osdk_target_directory, config);
std::env::set_current_dir(original_dir).unwrap();
bundle
}
@ -92,7 +92,7 @@ pub fn do_build(
&osdk_target_directory,
&aster_elf,
config.manifest.initramfs.as_ref(),
&config,
config,
);
bundle.add_vm_image(&bootdev_image);
}
@ -105,6 +105,7 @@ fn build_kernel_elf(args: &CargoArgs) -> AsterBin {
let target_json_path = PathBuf::from_str("x86_64-custom.json").unwrap();
let mut command = cargo();
command.env_remove("RUSTUP_TOOLCHAIN");
command.arg("build").arg("--target").arg(&target_json_path);
command.args(COMMON_CARGO_ARGS);
command.arg("--profile=".to_string() + &args.profile);
@ -114,8 +115,8 @@ fn build_kernel_elf(args: &CargoArgs) -> AsterBin {
process::exit(Errno::ExecuteCommand as _);
}
let aster_bin_path = PathBuf::from(target_directory)
.join(target_json_path.file_stem().unwrap().to_str().unwrap());
let aster_bin_path =
target_directory.join(target_json_path.file_stem().unwrap().to_str().unwrap());
let aster_bin_path = if args.profile == "dev" {
aster_bin_path.join("debug")
} else {

View File

@ -77,7 +77,7 @@ fn create_osdk_manifest(cargo_metadata: &serde_json::Value) {
// The apt OVMF repo installs to `/usr/share/OVMF`
fs::write(
osdk_manifest_path,
r#"
r#"\
[boot]
ovmf = "/usr/share/OVMF"
[qemu]

View File

@ -18,10 +18,10 @@ pub fn execute_run_command(config: &RunConfig) {
// TODO: Check if the bundle is already built and compatible with the run configuration.
let bundle = create_base_and_build(
&default_bundle_directory,
default_bundle_directory,
&osdk_target_directory,
&required_build_config,
);
bundle.run(&config);
bundle.run(config);
}

View File

@ -19,7 +19,7 @@ pub fn execute_test_command(config: &TestConfig) {
let ktest_test_whitelist = match &config.test_name {
Some(name) => format!(r#"Some(&["{}"])"#, name),
None => format!(r#"None"#),
None => r#"None"#.to_string(),
};
let mut ktest_crate_whitelist = vec![current_crate.name];
@ -54,12 +54,12 @@ pub static KTEST_CRATE_WHITELIST: Option<&[&str]> = Some(&{:#?});
// Add `--cfg ktest` to RUSTFLAGS
std::env::set_var("RUSTFLAGS", "--cfg ktest");
let bundle = do_build(
&default_bundle_directory,
default_bundle_directory,
&osdk_target_directory,
&required_build_config,
);
std::env::remove_var("RUSTFLAGS");
std::env::set_current_dir(&original_dir).unwrap();
std::env::set_current_dir(original_dir).unwrap();
let required_run_config = RunConfig {
manifest: required_build_config.manifest.clone(),

View File

@ -158,3 +158,16 @@ fn check_cfg(cfg: &str) {
lazy_static::lazy_static! {
pub static ref SELECT_REGEX: Regex = Regex::new(r#"cfg\(select="(?P<select>\w+)"\)"#).unwrap();
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn extract_selection() {
let text = "cfg(select=\"abc123_\")";
let captures = SELECT_REGEX.captures(text).unwrap();
let selection = captures.name("select").unwrap().as_str();
assert_eq!(selection, "abc123_");
}
}

View File

@ -9,6 +9,9 @@ pub mod boot;
pub mod manifest;
pub mod qemu;
#[cfg(test)]
mod test;
use std::{fs, path::PathBuf, process};
use indexmap::{IndexMap, IndexSet};
@ -336,39 +339,3 @@ fn split_kcmd_args(kcmd_args: &mut Vec<String>) -> Vec<String> {
init_args.remove(0);
init_args
}
#[test]
fn split_kcmd_args_test() {
let mut kcmd_args = ["init=/bin/sh", "--", "sh", "-l"]
.iter()
.map(ToString::to_string)
.collect();
let init_args = split_kcmd_args(&mut kcmd_args);
let expected_kcmd_args: Vec<_> = ["init=/bin/sh"].iter().map(ToString::to_string).collect();
assert_eq!(kcmd_args, expected_kcmd_args);
let expecetd_init_args: Vec<_> = ["sh", "-l"].iter().map(ToString::to_string).collect();
assert_eq!(init_args, expecetd_init_args);
let mut kcmd_args = ["init=/bin/sh", "--"]
.iter()
.map(ToString::to_string)
.collect();
let init_args = split_kcmd_args(&mut kcmd_args);
let expected_kcmd_args: Vec<_> = ["init=/bin/sh"].iter().map(ToString::to_string).collect();
assert_eq!(kcmd_args, expected_kcmd_args);
let expecetd_init_args: Vec<String> = Vec::new();
assert_eq!(init_args, expecetd_init_args);
let mut kcmd_args = ["init=/bin/sh", "shell=/bin/sh"]
.iter()
.map(ToString::to_string)
.collect();
let init_args = split_kcmd_args(&mut kcmd_args);
let expected_kcmd_args: Vec<_> = ["init=/bin/sh", "shell=/bin/sh"]
.iter()
.map(ToString::to_string)
.collect();
assert_eq!(kcmd_args, expected_kcmd_args);
let expecetd_init_args: Vec<String> = Vec::new();
assert_eq!(init_args, expecetd_init_args);
}

View File

@ -0,0 +1,152 @@
// SPDX-License-Identifier: MPL-2.0
use super::*;
#[test]
fn split_kcmd_args_test() {
let mut kcmd_args = ["init=/bin/sh", "--", "sh", "-l"]
.iter()
.map(ToString::to_string)
.collect();
let init_args = split_kcmd_args(&mut kcmd_args);
let expected_kcmd_args: Vec<_> = ["init=/bin/sh"].iter().map(ToString::to_string).collect();
assert_eq!(kcmd_args, expected_kcmd_args);
let expecetd_init_args: Vec<_> = ["sh", "-l"].iter().map(ToString::to_string).collect();
assert_eq!(init_args, expecetd_init_args);
let mut kcmd_args = ["init=/bin/sh", "--"]
.iter()
.map(ToString::to_string)
.collect();
let init_args = split_kcmd_args(&mut kcmd_args);
let expected_kcmd_args: Vec<_> = ["init=/bin/sh"].iter().map(ToString::to_string).collect();
assert_eq!(kcmd_args, expected_kcmd_args);
let expecetd_init_args: Vec<String> = Vec::new();
assert_eq!(init_args, expecetd_init_args);
let mut kcmd_args = ["init=/bin/sh", "shell=/bin/sh"]
.iter()
.map(ToString::to_string)
.collect();
let init_args = split_kcmd_args(&mut kcmd_args);
let expected_kcmd_args: Vec<_> = ["init=/bin/sh", "shell=/bin/sh"]
.iter()
.map(ToString::to_string)
.collect();
assert_eq!(kcmd_args, expected_kcmd_args);
let expecetd_init_args: Vec<String> = Vec::new();
assert_eq!(init_args, expecetd_init_args);
}
#[test]
fn get_key_test() {
let string1 = "init=/bin/init";
let key = get_key(string1, "=").unwrap();
assert_eq!(key.as_str(), "init");
let string2 = "-m 2G";
let key = get_key(string2, " ").unwrap();
assert_eq!(key.as_str(), "-m");
let string3 = "-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off";
let key = get_key(string3, " ").unwrap();
assert_eq!(key.as_str(), "-device");
let string4 = "-device";
assert!(get_key(string4, " ").is_none());
}
#[test]
fn apply_kv_array_test() {
let qemu_args = &[
"-enable-kvm",
"-m 2G",
"-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off",
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off",
];
let args = &["-m 100G", "-device ioh3420,id=pcie.0,chassis=1"];
let expected = &[
"-enable-kvm",
"-m 100G",
"-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off",
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off",
"-device ioh3420,id=pcie.0,chassis=1",
];
let mut array = qemu_args.iter().map(ToString::to_string).collect();
let args = args.iter().map(ToString::to_string).collect();
apply_kv_array(&mut array, &args, " ", &["-device"]);
let expected: Vec<_> = expected.iter().map(ToString::to_string).collect();
assert_eq!(expected, array);
}
#[test]
fn deserialize_osdk_manifest() {
let content = include_str!("OSDK.toml.empty");
let osdk_manifest: TomlManifest = toml::from_str(content).unwrap();
assert!(osdk_manifest == TomlManifest::default());
let content = include_str!("OSDK.toml.full");
let osdk_manifest: TomlManifest = toml::from_str(content).unwrap();
assert!(osdk_manifest.boot.grub_mkrescue.unwrap() == PathBuf::from("/usr/bin/grub-mkrescue"));
}
#[test]
fn serialize_osdk_manifest() {
let manifest = TomlManifest::default();
let contents = toml::to_string(&manifest).unwrap();
fs::write("OSDK.toml", contents).unwrap();
fs::remove_file("OSDK.toml").unwrap();
}
#[test]
fn deserialize_conditional_osdk_manifest() {
let content = include_str!("OSDK.toml.conditional");
let manifest: TomlManifest = toml::from_str(content).unwrap();
println!("manifest = {:?}", manifest);
}
#[test]
fn conditional_manifest() {
let toml_manifest: TomlManifest = {
let content = include_str!("OSDK.toml.conditional");
toml::from_str(content).unwrap()
};
assert!(toml_manifest.qemu.cfg.is_some());
assert!(toml_manifest
.qemu
.cfg
.as_ref()
.unwrap()
.contains_key(&String::from("cfg(select=\"intel_tdx\")")));
assert!(toml_manifest
.qemu
.cfg
.as_ref()
.unwrap()
.contains_key(&String::from("cfg(select=\"iommu\")")));
// Default selection
let selection: Option<&str> = None;
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), selection);
assert!(manifest.qemu.args.contains(&String::from(
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off"
)));
// Iommu
let selection: Option<&str> = Some("iommu");
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), selection);
assert!(manifest
.qemu
.args
.contains(&String::from("-device ioh3420,id=pcie.0,chassis=1")));
// Tdx
let selection: Option<&str> = Some("intel_tdx");
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), selection);
assert!(manifest.qemu.args.is_empty());
}

View File

@ -10,7 +10,6 @@ pub enum Errno {
ExecuteCommand = 5,
BuildCrate = 6,
RunBundle = 7,
CreateBaseCrate = 8,
}
/// Print error message to console

View File

@ -14,8 +14,6 @@ mod cli;
mod commands;
mod config_manager;
mod error;
#[cfg(test)]
mod test;
mod utils;
mod vm_image;

View File

@ -1,153 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
use std::{
fs,
path::{Path, PathBuf},
};
use crate::{
cli::CargoArgs,
config_manager::{
get_feature_strings,
manifest::{OsdkManifest, TomlManifest, SELECT_REGEX},
},
test::utils::{assert_success, cargo_osdk, create_workspace},
};
#[test]
fn deserialize_osdk_manifest() {
let content = include_str!("OSDK.toml.empty");
let osdk_manifest: TomlManifest = toml::from_str(content).unwrap();
assert!(osdk_manifest == TomlManifest::default());
let content = include_str!("OSDK.toml.full");
let osdk_manifest: TomlManifest = toml::from_str(content).unwrap();
assert!(osdk_manifest.boot.grub_mkrescue.unwrap() == PathBuf::from("/usr/bin/grub-mkrescue"));
}
#[test]
fn serialize_osdk_manifest() {
let manifest = TomlManifest::default();
let contents = toml::to_string(&manifest).unwrap();
fs::write("OSDK.toml", contents).unwrap();
fs::remove_file("OSDK.toml").unwrap();
}
#[test]
fn deserialize_conditional_osdk_manifest() {
let content = include_str!("OSDK.toml.conditional");
let manifest: TomlManifest = toml::from_str(content).unwrap();
println!("manifest = {:?}", manifest);
}
#[test]
fn load_manifest() {
let workspace = "workspace_foo";
let kernel_name: &str = "foo_os";
create_workspace(workspace, &[kernel_name]);
create_osdk_kernel(kernel_name, workspace);
let cargo_args = CargoArgs::default();
cargo_osdk_build(PathBuf::from(workspace).join(kernel_name), &cargo_args);
fs::remove_dir_all(workspace).unwrap();
}
#[test]
fn load_manifest_conditional() {
let workspace = "workspace_bar";
let kernel_name: &str = "bar_os";
create_workspace(workspace, &[kernel_name]);
create_osdk_kernel_with_features(kernel_name, &["intel_tdx", "iommu"], workspace);
let contents = include_str!("OSDK.toml.conditional");
let path = PathBuf::from(workspace).join("OSDK.toml");
fs::write(path, contents).unwrap();
let cargo_args = CargoArgs {
profile: "release".to_string(),
features: vec![String::from("iommu")],
};
cargo_osdk_build(PathBuf::from(workspace).join(kernel_name), &cargo_args);
fs::remove_dir_all(workspace).unwrap();
}
fn create_osdk_kernel(name: &str, current_dir: &str) {
let output = cargo_osdk(&["new", "--kernel", name])
.current_dir(current_dir)
.output()
.unwrap();
assert_success(&output);
}
fn create_osdk_kernel_with_features(name: &str, features: &[&str], current_dir: &str) {
create_osdk_kernel(name, current_dir);
let manifest_path = PathBuf::from(current_dir).join(name).join("Cargo.toml");
let contents = fs::read_to_string(&manifest_path).unwrap();
let mut manifest: toml::Table = toml::from_str(&contents).unwrap();
let mut features_table = toml::Table::new();
for feature in features {
features_table.insert(feature.to_string(), toml::Value::Array(Vec::new()));
}
manifest.insert("features".to_string(), toml::Value::Table(features_table));
fs::write(&manifest_path, manifest.to_string()).unwrap();
}
fn cargo_osdk_build<P: AsRef<Path>>(current_dir: P, cargo_args: &CargoArgs) {
let args = get_feature_strings(cargo_args);
let mut command = cargo_osdk(&["build"]);
command.args(args);
command.current_dir(current_dir);
let output = command.output().unwrap();
assert_success(&output);
}
#[test]
fn conditional_manifest() {
let toml_manifest: TomlManifest = {
let content = include_str!("OSDK.toml.conditional");
toml::from_str(content).unwrap()
};
assert!(toml_manifest.qemu.cfg.is_some());
assert!(toml_manifest
.qemu
.cfg
.as_ref()
.unwrap()
.contains_key(&String::from("cfg(select=\"intel_tdx\")")));
assert!(toml_manifest
.qemu
.cfg
.as_ref()
.unwrap()
.contains_key(&String::from("cfg(select=\"iommu\")")));
// Default selection
let selection: Option<&str> = None;
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), selection);
assert!(manifest.qemu.args.contains(&String::from(
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off"
)));
// Iommu
let selection: Option<&str> = Some("iommu");
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), selection);
assert!(manifest
.qemu
.args
.contains(&String::from("-device ioh3420,id=pcie.0,chassis=1")));
// Tdx
let selection: Option<&str> = Some("intel_tdx");
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), selection);
assert!(manifest.qemu.args.is_empty());
}
#[test]
fn extract_selection() {
let text = "cfg(select=\"abc123_\")";
let captures = SELECT_REGEX.captures(text).unwrap();
let selection = captures.name("select").unwrap().as_str();
assert_eq!(selection, "abc123_");
}

View File

@ -1,49 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
use crate::config_manager::{apply_kv_array, get_key};
mod manifest;
#[test]
fn get_key_test() {
let string1 = "init=/bin/init";
let key = get_key(string1, "=").unwrap();
assert_eq!(key.as_str(), "init");
let string2 = "-m 2G";
let key = get_key(string2, " ").unwrap();
assert_eq!(key.as_str(), "-m");
let string3 = "-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off";
let key = get_key(string3, " ").unwrap();
assert_eq!(key.as_str(), "-device");
let string4 = "-device";
assert!(get_key(string4, " ").is_none());
}
#[test]
fn apply_kv_array_test() {
let qemu_args = &[
"-enable-kvm",
"-m 2G",
"-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off",
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off",
];
let args = &["-m 100G", "-device ioh3420,id=pcie.0,chassis=1"];
let expected = &[
"-enable-kvm",
"-m 100G",
"-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off",
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off",
"-device ioh3420,id=pcie.0,chassis=1",
];
let mut array = qemu_args.iter().map(ToString::to_string).collect();
let args = args.iter().map(ToString::to_string).collect();
apply_kv_array(&mut array, &args, " ", &["-device"]);
let expected: Vec<_> = expected.iter().map(ToString::to_string).collect();
assert_eq!(expected, array);
}

View File

@ -1,8 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
//! The crate unit test module
mod cli;
mod commands;
mod config_manager;
mod utils;

View File

@ -83,13 +83,13 @@ pub fn get_current_crate_info() -> CrateInfo {
let default_member = default_members[0]
.as_str()
.unwrap()
.split(" ")
.split(' ')
.collect::<Vec<&str>>();
let name = default_member[0].to_string();
let version = default_member[1].to_string();
let path = default_member[2]
.trim_start_matches("(path+file://")
.trim_end_matches(")")
.trim_end_matches(')')
.to_string();
CrateInfo {
name,

10
osdk/tests/bin.rs Normal file
View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: MPL-2.0
//! This module contains tests that invokes the `osdk` binary and checks the output.
//! Please be sure the the `osdk` binary is built and available in the `target/debug`
//! directory before running these tests.
mod cli;
mod commands;
mod integration;
mod util;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use super::utils::*;
use crate::util::*;
#[test]
fn cli_help_message() {

View File

@ -1,15 +1,18 @@
// SPDX-License-Identifier: MPL-2.0
use std::fs::remove_dir_all;
use std::{fs::remove_dir_all, path::Path};
use crate::test::utils::*;
use crate::util::*;
const KERNEL_NAME: &str = "myos";
const LIB_NAME: &str = "my_module";
#[test]
fn create_kernel_in_workspace() {
const WORKSPACE_NAME: &str = "kernel_workspace";
const WORKSPACE_NAME: &str = "/tmp/kernel_workspace";
if Path::new(WORKSPACE_NAME).exists() {
remove_dir_all(WORKSPACE_NAME).unwrap();
}
create_workspace(WORKSPACE_NAME, &[KERNEL_NAME]);
let mut cargo_osdk = cargo_osdk(["new", "--kernel", KERNEL_NAME]);
cargo_osdk.current_dir(WORKSPACE_NAME);
@ -20,7 +23,10 @@ fn create_kernel_in_workspace() {
#[test]
fn create_lib_in_workspace() {
const WORKSPACE_NAME: &str = "lib_workspace";
const WORKSPACE_NAME: &str = "/tmp/lib_workspace";
if Path::new(WORKSPACE_NAME).exists() {
remove_dir_all(WORKSPACE_NAME).unwrap();
}
create_workspace(WORKSPACE_NAME, &[LIB_NAME]);
let mut cargo_osdk = cargo_osdk(["new", LIB_NAME]);
cargo_osdk.current_dir(WORKSPACE_NAME);
@ -31,7 +37,11 @@ fn create_lib_in_workspace() {
#[test]
fn create_two_crates_in_workspace() {
const WORKSPACE_NAME: &str = "my_workspace";
const WORKSPACE_NAME: &str = "/tmp/my_workspace";
if Path::new(WORKSPACE_NAME).exists() {
remove_dir_all(WORKSPACE_NAME).unwrap();
}
create_workspace(WORKSPACE_NAME, &[LIB_NAME]);
// Create lib crate
let mut command = cargo_osdk(["new", LIB_NAME]);

View File

@ -0,0 +1,16 @@
[qemu]
args = [
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off",
"-device virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off",
"-device virtio-serial-pci,disable-legacy=on,disable-modern=off"
]
[qemu.'cfg(select="intel_tdx")']
[qemu.'cfg(select="iommu")']
args = [
"-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on",
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on",
"-device intel-iommu,intremap=on,device-iotlb=on",
"-device ioh3420,id=pcie.0,chassis=1",
]

View File

@ -0,0 +1,76 @@
// SPDX-License-Identifier: MPL-2.0
use std::{
fs,
path::{Path, PathBuf},
};
use crate::util::{assert_success, cargo_osdk, create_workspace};
#[test]
fn build_with_default_manifest() {
let workspace = "/tmp/workspace_foo";
if Path::new(workspace).exists() {
fs::remove_dir_all(workspace).unwrap();
}
let kernel_name: &str = "foo_os";
create_workspace(workspace, &[kernel_name]);
create_osdk_kernel(kernel_name, workspace);
cargo_osdk_build(PathBuf::from(workspace).join(kernel_name), &[]);
fs::remove_dir_all(workspace).unwrap();
}
#[test]
fn build_with_conditional_manifest() {
let workspace = "/tmp/workspace_bar";
if Path::new(workspace).exists() {
fs::remove_dir_all(workspace).unwrap();
}
let kernel_name: &str = "bar_os";
create_workspace(workspace, &[kernel_name]);
create_osdk_kernel_with_features(kernel_name, &["intel_tdx", "iommu"], workspace);
let contents = include_str!("OSDK.toml.conditional");
let path = PathBuf::from(workspace).join("OSDK.toml");
fs::write(path, contents).unwrap();
cargo_osdk_build(
PathBuf::from(workspace).join(kernel_name),
&["--profile", "release", "--features", "iommu"],
);
fs::remove_dir_all(workspace).unwrap();
}
fn create_osdk_kernel(name: &str, current_dir: &str) {
let output = cargo_osdk(&["new", "--kernel", name])
.current_dir(current_dir)
.output()
.unwrap();
assert_success(&output);
}
fn create_osdk_kernel_with_features(name: &str, features: &[&str], current_dir: &str) {
create_osdk_kernel(name, current_dir);
let manifest_path = PathBuf::from(current_dir).join(name).join("Cargo.toml");
let contents = fs::read_to_string(&manifest_path).unwrap();
let mut manifest: toml::Table = toml::from_str(&contents).unwrap();
let mut features_table = toml::Table::new();
for feature in features {
features_table.insert(feature.to_string(), toml::Value::Array(Vec::new()));
}
manifest.insert("features".to_string(), toml::Value::Table(features_table));
fs::write(&manifest_path, manifest.to_string()).unwrap();
}
fn cargo_osdk_build<P: AsRef<Path>>(current_dir: P, args: &[&str]) {
let mut command = cargo_osdk(&["build"]);
command.args(args);
command.current_dir(current_dir);
let output = command.output().unwrap();
assert_success(&output);
}

View File

@ -33,6 +33,7 @@ pub fn assert_stdout_contains_msg(output: &Output, msg: &str) {
}
pub fn create_workspace(workspace_name: &str, members: &[&str]) {
// Create Cargo.toml
let mut table = toml::Table::new();
let workspace_table = {
let mut table = toml::Table::new();
@ -58,8 +59,14 @@ pub fn create_workspace(workspace_name: &str, members: &[&str]) {
let content = table.to_string();
fs::write(manefest_path, content).unwrap();
// Create OSDK.toml
let osdk_manifest_path = PathBuf::from(workspace_name).join("OSDK.toml");
fs::write(osdk_manifest_path, "").unwrap();
// Create rust-toolchain.toml which is synced with the Asterinas' toolchain
let rust_toolchain_path = PathBuf::from(workspace_name).join("rust-toolchain.toml");
let content = include_str!("../../../rust-toolchain.toml");
fs::write(rust_toolchain_path, content).unwrap();
}
pub fn add_member_to_workspace(workspace: impl AsRef<Path>, new_member: &str) {