diff --git a/tests/vm_tests.rs b/tests/vm_tests.rs index 1db335c..3a30a00 100644 --- a/tests/vm_tests.rs +++ b/tests/vm_tests.rs @@ -6,6 +6,62 @@ mod tests { use std::process::Command; use virt::domain::Domain; + // Test fixture struct that implements Drop for guaranteed cleanup + struct VMTestFixture { + name: String, + disk_path: String, + } + + impl VMTestFixture { + fn new(name: &str) -> Self { + let temp_dir = std::env::temp_dir(); + let disk_path = temp_dir + .join(format!("{}.qcow2", name)) + .to_string_lossy() + .to_string(); + + // Perform initial cleanup to ensure we start with a clean state + Self::cleanup_vm(name, &disk_path); + + VMTestFixture { + name: name.to_string(), + disk_path, + } + } + + // Static method to clean up VM and disk + fn cleanup_vm(name: &str, disk_path: &str) { + println!("Cleaning up VM: {}", name); + + // Force destroy the domain if it's running + let _ = Command::new("virsh").args(["destroy", name]).output(); + + // Undefine the domain with all related resources + let _ = Command::new("virsh") + .args([ + "undefine", + name, + "--managed-save", + "--snapshots-metadata", + "--nvram", + ]) + .output(); + + // Remove disk if it exists + if std::path::Path::new(disk_path).exists() { + let _ = fs::remove_file(disk_path); + println!("Removed disk: {}", disk_path); + } + } + } + + // Drop implementation ensures cleanup happens even if tests panic or fail + impl Drop for VMTestFixture { + fn drop(&mut self) { + Self::cleanup_vm(&self.name, &self.disk_path); + } + } + // Helper function to check if a VM with a given name exists fn domain_exists(name: &str) -> bool { let output = Command::new("virsh") @@ -74,36 +130,21 @@ mod tests { } // Helper function to create a test VM for later destruction tests - fn create_test_vm(name: &str) -> Result<()> { + fn create_test_vm(name: &str, fixture: &VMTestFixture) -> Result<()> { // Skip if domain already exists if domain_exists(name) { return Ok(()); } - let temp_dir = std::env::temp_dir(); - let disk_path = temp_dir.join(format!("{}.qcow2", name)); - // Create a minimal VM for testing - let mut vm = VirtualMachine::new( - name.to_string(), - 1, - 512, - 1, - disk_path.to_string_lossy().to_string(), - ); + let mut vm = VirtualMachine::new(name.to_string(), 1, 512, 1, fixture.disk_path.clone()); vm.connect(None)?; // Create disk if it doesn't exist - if !disk_path.exists() { + if !std::path::Path::new(&fixture.disk_path).exists() { Command::new("qemu-img") - .args([ - "create", - "-f", - "qcow2", - disk_path.to_string_lossy().as_ref(), - "1G", - ]) + .args(["create", "-f", "qcow2", &fixture.disk_path, "1G"]) .output() .expect("Failed to create test disk image"); } @@ -116,29 +157,6 @@ mod tests { Ok(()) } - // Helper to clean up any leftover test resources - fn cleanup_test_resources(name: &str) { - if domain_exists(name) { - let _ = Command::new("virsh").args(["destroy", name]).output(); - - let _ = Command::new("virsh") - .args([ - "undefine", - name, - "--managed-save", - "--snapshots-metadata", - "--nvram", - ]) - .output(); - } - - let temp_dir = std::env::temp_dir(); - let disk_path = temp_dir.join(format!("{}.qcow2", name)); - if disk_path.exists() { - let _ = fs::remove_file(disk_path); - } - } - #[test] fn test_create_new_vm_instance() { let vm = VirtualMachine::new( @@ -189,12 +207,15 @@ mod tests { #[test] #[ignore] fn test_connect_to_libvirt() -> Result<()> { + // Create fixture first to ensure cleanup + let fixture = VMTestFixture::new("test-connect-vm"); + let mut vm = VirtualMachine::new( "test-connect-vm".to_string(), 1, 512, 1, - "/tmp/test-connect-vm.qcow2".to_string(), + fixture.disk_path.clone(), ); vm.connect(None)?; @@ -209,33 +230,19 @@ mod tests { #[ignore] fn test_create_and_destroy_vm() -> Result<()> { let test_name = "test-create-destroy-vm"; - let temp_dir = std::env::temp_dir(); - let disk_path = temp_dir.join(format!("{}.qcow2", test_name)); - - // Clean up any previous test resources - cleanup_test_resources(test_name); + // Create fixture first to ensure cleanup happens automatically + let fixture = VMTestFixture::new(test_name); // Create a new VM - let mut vm = VirtualMachine::new( - test_name.to_string(), - 1, - 512, - 1, - disk_path.to_string_lossy().to_string(), - ); + let mut vm = + VirtualMachine::new(test_name.to_string(), 1, 512, 1, fixture.disk_path.clone()); vm.connect(None)?; // Create disk if it doesn't exist - if !disk_path.exists() { + if !std::path::Path::new(&fixture.disk_path).exists() { Command::new("qemu-img") - .args([ - "create", - "-f", - "qcow2", - disk_path.to_string_lossy().as_ref(), - "1G", - ]) + .args(["create", "-f", "qcow2", &fixture.disk_path, "1G"]) .output() .expect("Failed to create test disk image"); } @@ -256,10 +263,10 @@ mod tests { assert!(!domain_exists(test_name)); // Disk should still exist since we used remove_disk=false - assert!(disk_path.exists()); + assert!(std::path::Path::new(&fixture.disk_path).exists()); - // Clean up disk - let _ = fs::remove_file(disk_path); + // Note: We don't need manual cleanup here, + // the fixture's Drop implementation will handle it Ok(()) } @@ -269,9 +276,11 @@ mod tests { #[ignore] fn test_destroy_static_method() -> Result<()> { let test_name = "test-static-destroy-vm"; + // Create fixture first to ensure cleanup happens automatically + let fixture = VMTestFixture::new(test_name); // Create a test VM first - create_test_vm(test_name)?; + create_test_vm(test_name, &fixture)?; // Verify it exists assert!(domain_exists(test_name)); @@ -283,9 +292,7 @@ mod tests { assert!(!domain_exists(test_name)); // Disk should be gone since we used remove_disk=true - let temp_dir = std::env::temp_dir(); - let disk_path = temp_dir.join(format!("{}.qcow2", test_name)); - assert!(!disk_path.exists()); + assert!(!std::path::Path::new(&fixture.disk_path).exists()); Ok(()) } @@ -293,7 +300,10 @@ mod tests { // Test destroying a non-existent VM (should return error) #[test] fn test_destroy_nonexistent_vm() { - let result = VirtualMachine::destroy("definitely-nonexistent-vm", None, false); + // Still create a fixture to ensure any leftovers are cleaned up + let fixture = VMTestFixture::new("definitely-nonexistent-vm"); + + let result = VirtualMachine::destroy(&fixture.name, None, false); assert!(result.is_err()); } }