use super::*;
use crate::traits::{Vcpu, VirtualMachine};
use crate::types::{CpuArch, VirtioDeviceConfig};
#[test]
fn test_vm_creation() {
if !arcbox_vz::is_supported() {
println!("Virtualization not supported, skipping");
return;
}
let config = VmConfig {
vcpu_count: 2,
memory_size: 512 * 1024 * 1024,
arch: CpuArch::native(),
..Default::default()
};
let vm = DarwinVm::new(config).unwrap();
assert_eq!(vm.state(), VmState::Created);
assert!(!vm.is_running());
}
#[test]
fn test_vcpu_creation() {
if !arcbox_vz::is_supported() {
println!("Virtualization not supported, skipping");
return;
}
let config = VmConfig {
vcpu_count: 4,
memory_size: 512 * 1024 * 1024,
..Default::default()
};
let mut vm = DarwinVm::new(config).unwrap();
let vcpu0 = vm.create_vcpu(0);
assert!(vcpu0.is_ok());
assert_eq!(vcpu0.unwrap().id(), 0);
let vcpu1 = vm.create_vcpu(1);
assert!(vcpu1.is_ok());
let vcpu0_again = vm.create_vcpu(0);
assert!(vcpu0_again.is_err());
let vcpu99 = vm.create_vcpu(99);
assert!(vcpu99.is_err());
}
#[test]
fn test_vm_lifecycle() {
if !arcbox_vz::is_supported() {
println!("Virtualization not supported, skipping");
return;
}
let config = VmConfig {
vcpu_count: 1,
memory_size: 256 * 1024 * 1024,
..Default::default()
};
let mut vm = DarwinVm::new(config).unwrap();
assert_eq!(vm.state(), VmState::Created);
match vm.start() {
Ok(()) => {
assert_eq!(vm.state(), VmState::Running);
assert!(vm.is_running());
vm.pause().unwrap();
assert_eq!(vm.state(), VmState::Paused);
vm.resume().unwrap();
assert_eq!(vm.state(), VmState::Running);
vm.stop().unwrap();
assert_eq!(vm.state(), VmState::Stopped);
assert!(!vm.is_running());
}
Err(e) => {
println!("VM start failed (expected without entitlement): {}", e);
let state = vm.state();
assert!(
state == VmState::Starting || state == VmState::Error,
"Unexpected state after failed start: {:?}",
state
);
}
}
}
#[test]
fn test_invalid_state_transitions() {
if !arcbox_vz::is_supported() {
println!("Virtualization not supported, skipping");
return;
}
let config = VmConfig {
memory_size: 128 * 1024 * 1024, ..Default::default()
};
let mut vm = DarwinVm::new(config).unwrap();
assert!(vm.pause().is_err());
assert!(vm.resume().is_err());
assert!(vm.stop().is_err());
}
#[test]
fn test_balloon_device_configuration() {
if !arcbox_vz::is_supported() {
println!("Virtualization not supported, skipping");
return;
}
let config = VmConfig {
vcpu_count: 1,
memory_size: 512 * 1024 * 1024,
..Default::default()
};
let mut vm = DarwinVm::new(config).unwrap();
assert!(!vm.has_balloon_device());
let balloon_config = VirtioDeviceConfig::balloon();
vm.add_virtio_device(balloon_config).unwrap();
assert!(vm.has_balloon_device());
assert_eq!(vm.configured_memory_size(), 512 * 1024 * 1024);
assert_eq!(vm.get_balloon_target_memory(), 0);
}
#[test]
fn test_balloon_device_pending() {
if !arcbox_vz::is_supported() {
println!("Virtualization not supported, skipping");
return;
}
println!("Balloon device test skipped: pending arcbox-vz support");
}
fn read_serial_from_pipe(bytes: &[u8], chunk: usize) -> String {
assert!(bytes.len() <= 512, "payload must fit a minimum-size pipe");
let mut fds = [0 as libc::c_int; 2];
unsafe {
assert_eq!(libc::pipe(fds.as_mut_ptr()), 0);
let written = libc::write(fds[1], bytes.as_ptr().cast(), bytes.len());
assert_eq!(usize::try_from(written), Ok(bytes.len()), "pipe write");
let output = DarwinVm::read_serial_fd_chunked(fds[0], chunk);
libc::close(fds[0]);
libc::close(fds[1]);
output
}
}
#[test]
fn serial_read_keeps_a_multibyte_char_split_across_read_chunks() {
let mut bytes = vec![b'a'; 255];
bytes.extend_from_slice("中\n".as_bytes());
let output = read_serial_from_pipe(&bytes, 256);
assert_eq!(output.len(), bytes.len(), "every byte reaches the caller");
assert!(output.starts_with(&"a".repeat(255)));
assert!(output.ends_with("中\n"));
}
#[test]
fn serial_read_spans_several_read_chunks() {
let bytes = vec![b'x'; 500];
let output = read_serial_from_pipe(&bytes, 64);
assert_eq!(output.len(), 500);
}