add tests

This commit is contained in:
Pearl Dsilva 2026-03-27 14:41:30 -04:00
parent 058007e7ec
commit 0fcf12c252
3 changed files with 742 additions and 3 deletions

View File

@ -17,11 +17,21 @@
package org.apache.cloudstack.storage.volume;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.storage.ClvmLockManager;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.dao.VMInstanceDao;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -48,6 +58,18 @@ public class VolumeServiceImplClvmTest {
@Mock
private VolumeDao volumeDao;
@Mock
private PrimaryDataStoreDao storagePoolDao;
@Mock
private HostDao _hostDao;
@Mock
private VMInstanceDao vmDao;
@Mock
private VolumeDataFactory volFactory;
@Mock
private VolumeInfo volumeInfoMock;
@ -55,7 +77,16 @@ public class VolumeServiceImplClvmTest {
private VolumeVO volumeVOMock;
@Mock
ClvmLockManager clvmLockManager;
private StoragePoolVO storagePoolVOMock;
@Mock
private HostVO hostVOMock;
@Mock
private VMInstanceVO vmInstanceVOMock;
@Mock
private ClvmLockManager clvmLockManager;
private static final Long VOLUME_ID = 1L;
private static final Long POOL_ID_1 = 100L;
@ -63,12 +94,19 @@ public class VolumeServiceImplClvmTest {
private static final Long HOST_ID_1 = 10L;
private static final Long HOST_ID_2 = 20L;
private static final String POOL_PATH_VG1 = "/vg1";
private static final String POOL_PATH_VG2 = "/vg2";
@Before
public void setup() {
when(volumeInfoMock.getId()).thenReturn(VOLUME_ID);
when(volumeInfoMock.getUuid()).thenReturn("test-volume-uuid");
// Setup volumeService dependencies
volumeService.storagePoolDao = storagePoolDao;
volumeService._hostDao = _hostDao;
volumeService.vmDao = vmDao;
volumeService.volFactory = volFactory;
volumeService._volumeDao = volumeDao;
volumeService.clvmLockManager = clvmLockManager;
}
@Test
@ -308,4 +346,157 @@ public class VolumeServiceImplClvmTest {
StoragePoolType.CLVM, StoragePoolType.CLVM,
"/cloudstack-vg-01", "/cloudstack-vg-02"));
}
@Test
public void testTransferVolumeLock_Success() {
when(volumeInfoMock.getPoolId()).thenReturn(POOL_ID_1);
when(volumeInfoMock.getId()).thenReturn(VOLUME_ID);
when(volumeInfoMock.getPath()).thenReturn("/dev/vg1/volume-1");
when(storagePoolDao.findById(POOL_ID_1)).thenReturn(storagePoolVOMock);
when(storagePoolVOMock.getName()).thenReturn("test-pool");
when(clvmLockManager.transferClvmVolumeLock(
"test-volume-uuid", VOLUME_ID, "/dev/vg1/volume-1", storagePoolVOMock, HOST_ID_1, HOST_ID_2))
.thenReturn(true);
assertTrue(volumeService.transferVolumeLock(volumeInfoMock, HOST_ID_1, HOST_ID_2));
}
@Test
public void testTransferVolumeLock_Failure() {
when(volumeInfoMock.getPoolId()).thenReturn(POOL_ID_1);
when(volumeInfoMock.getId()).thenReturn(VOLUME_ID);
when(volumeInfoMock.getPath()).thenReturn("/dev/vg1/volume-1");
when(storagePoolDao.findById(POOL_ID_1)).thenReturn(storagePoolVOMock);
when(storagePoolVOMock.getName()).thenReturn("test-pool");
when(clvmLockManager.transferClvmVolumeLock(
"test-volume-uuid", VOLUME_ID, "/dev/vg1/volume-1", storagePoolVOMock, HOST_ID_1, HOST_ID_2))
.thenReturn(false);
assertFalse(volumeService.transferVolumeLock(volumeInfoMock, HOST_ID_1, HOST_ID_2));
}
@Test
public void testTransferVolumeLock_PoolNotFound() {
when(volumeInfoMock.getPoolId()).thenReturn(POOL_ID_1);
when(storagePoolDao.findById(POOL_ID_1)).thenReturn(null);
assertFalse(volumeService.transferVolumeLock(volumeInfoMock, HOST_ID_1, HOST_ID_2));
}
@Test
public void testFindVolumeLockHost_NullVolume() {
Long result = volumeService.findVolumeLockHost(null);
assertNull(result);
}
@Test
public void testFindVolumeLockHost_ExplicitLockFound() {
when(clvmLockManager.getClvmLockHostId(VOLUME_ID, "test-volume-uuid"))
.thenReturn(HOST_ID_1);
Long result = volumeService.findVolumeLockHost(volumeInfoMock);
assertEquals(HOST_ID_1, result);
}
@Test
public void testFindVolumeLockHost_FromAttachedVM() {
when(clvmLockManager.getClvmLockHostId(VOLUME_ID, "test-volume-uuid"))
.thenReturn(null);
when(volumeInfoMock.getInstanceId()).thenReturn(100L);
when(vmDao.findById(100L)).thenReturn(vmInstanceVOMock);
when(vmInstanceVOMock.getUuid()).thenReturn("vm-uuid");
when(vmInstanceVOMock.getHostId()).thenReturn(HOST_ID_1);
Long result = volumeService.findVolumeLockHost(volumeInfoMock);
assertEquals(HOST_ID_1, result);
}
@Test
public void testFindVolumeLockHost_FallbackToClusterHost() {
when(clvmLockManager.getClvmLockHostId(VOLUME_ID, "test-volume-uuid"))
.thenReturn(null);
when(volumeInfoMock.getInstanceId()).thenReturn(null);
when(volumeInfoMock.getPoolId()).thenReturn(POOL_ID_1);
when(storagePoolDao.findById(POOL_ID_1)).thenReturn(storagePoolVOMock);
when(storagePoolVOMock.getClusterId()).thenReturn(10L);
when(hostVOMock.getId()).thenReturn(HOST_ID_1);
when(hostVOMock.getStatus()).thenReturn(com.cloud.host.Status.Up);
when(_hostDao.findByClusterId(10L)).thenReturn(java.util.Collections.singletonList(hostVOMock));
Long result = volumeService.findVolumeLockHost(volumeInfoMock);
assertEquals(HOST_ID_1, result);
}
@Test
public void testFindVolumeLockHost_NoHostFound() {
when(clvmLockManager.getClvmLockHostId(VOLUME_ID, "test-volume-uuid"))
.thenReturn(null);
when(volumeInfoMock.getInstanceId()).thenReturn(null);
when(volumeInfoMock.getPoolId()).thenReturn(POOL_ID_1);
when(storagePoolDao.findById(POOL_ID_1)).thenReturn(storagePoolVOMock);
when(storagePoolVOMock.getClusterId()).thenReturn(10L);
when(_hostDao.findByClusterId(10L)).thenReturn(java.util.Collections.emptyList());
Long result = volumeService.findVolumeLockHost(volumeInfoMock);
assertNull(result);
}
@Test
public void testPerformLockMigration_Success() {
when(volumeInfoMock.getPoolId()).thenReturn(POOL_ID_1);
when(volumeInfoMock.getId()).thenReturn(VOLUME_ID);
when(volumeInfoMock.getPath()).thenReturn("/dev/vg1/volume-1");
when(clvmLockManager.getClvmLockHostId(VOLUME_ID, "test-volume-uuid")).thenReturn(HOST_ID_1);
when(storagePoolDao.findById(POOL_ID_1)).thenReturn(storagePoolVOMock);
when(storagePoolVOMock.getName()).thenReturn("test-pool");
when(clvmLockManager.transferClvmVolumeLock(
"test-volume-uuid", VOLUME_ID, "/dev/vg1/volume-1", storagePoolVOMock, HOST_ID_1, HOST_ID_2))
.thenReturn(true);
when(volFactory.getVolume(VOLUME_ID)).thenReturn(volumeInfoMock);
VolumeInfo result = volumeService.performLockMigration(volumeInfoMock, HOST_ID_2);
assertNotNull(result);
}
@Test
public void testPerformLockMigration_SameHost() {
when(clvmLockManager.getClvmLockHostId(VOLUME_ID, "test-volume-uuid")).thenReturn(HOST_ID_1);
VolumeInfo result = volumeService.performLockMigration(volumeInfoMock, HOST_ID_1);
assertEquals(volumeInfoMock, result);
}
@Test
public void testPerformLockMigration_SourceHostNull() {
when(volumeInfoMock.getPoolId()).thenReturn(POOL_ID_1);
when(volumeInfoMock.getId()).thenReturn(VOLUME_ID);
when(clvmLockManager.getClvmLockHostId(VOLUME_ID, "test-volume-uuid")).thenReturn(null);
when(volumeInfoMock.getInstanceId()).thenReturn(null);
when(volumeInfoMock.getPoolId()).thenReturn(POOL_ID_1);
when(storagePoolDao.findById(POOL_ID_1)).thenReturn(storagePoolVOMock);
when(storagePoolVOMock.getClusterId()).thenReturn(null);
VolumeInfo result = volumeService.performLockMigration(volumeInfoMock, HOST_ID_2);
assertNotNull(result);
}
@Test(expected = com.cloud.utils.exception.CloudRuntimeException.class)
public void testPerformLockMigration_NullVolume() {
volumeService.performLockMigration(null, HOST_ID_2);
}
@Test(expected = com.cloud.utils.exception.CloudRuntimeException.class)
public void testPerformLockMigration_TransferFails() {
when(volumeInfoMock.getPoolId()).thenReturn(POOL_ID_1);
when(volumeInfoMock.getId()).thenReturn(VOLUME_ID);
when(volumeInfoMock.getPath()).thenReturn("/dev/vg1/volume-1");
when(clvmLockManager.getClvmLockHostId(VOLUME_ID, "test-volume-uuid")).thenReturn(HOST_ID_1);
when(storagePoolDao.findById(POOL_ID_1)).thenReturn(storagePoolVOMock);
when(storagePoolVOMock.getName()).thenReturn("test-pool");
when(clvmLockManager.transferClvmVolumeLock(
"test-volume-uuid", VOLUME_ID, "/dev/vg1/volume-1", storagePoolVOMock, HOST_ID_1, HOST_ID_2))
.thenReturn(false);
volumeService.performLockMigration(volumeInfoMock, HOST_ID_2);
}
}

View File

@ -2442,7 +2442,7 @@ public class KVMStorageProcessor implements StorageProcessor {
* @return SnapshotObjectTO if fallback to full snapshot occurred, null if validation passed
* @throws LibvirtException if libvirt operations fail
*/
private SnapshotObjectTO validateClvmNgBitmapAndFallbackIfNeeded(SnapshotObjectTO snapshotObjectTO,
protected SnapshotObjectTO validateClvmNgBitmapAndFallbackIfNeeded(SnapshotObjectTO snapshotObjectTO,
KVMStoragePool primaryPool,
KVMStoragePool secondaryPool,
String secondaryPoolUrl,

View File

@ -22,6 +22,7 @@ import com.cloud.exception.InternalErrorException;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
import com.cloud.storage.Storage;
import com.cloud.storage.template.TemplateConstants;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
@ -53,6 +54,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import javax.naming.ConfigurationException;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@ -499,4 +501,550 @@ public class KVMStorageProcessorTest {
Assert.assertEquals("vda", result);
}
@Test
public void testIsBitmapUsable_ValidBitmap() {
String validJsonOutput = "{\n" +
" \"format-specific\": {\n" +
" \"data\": {\n" +
" \"bitmaps\": [\n" +
" {\n" +
" \"name\": \"checkpoint-123\",\n" +
" \"flags\": []\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
scriptMock.when(() -> Script.runSimpleBashScriptWithFullResult(Mockito.anyString(), Mockito.anyInt()))
.thenReturn(validJsonOutput);
boolean result = storageProcessorSpy.isBitmapUsable(kvmStoragePoolMock, volumeObjectToMock, "checkpoint-123");
Assert.assertTrue("Bitmap should be usable when it exists and has no 'in-use' flag", result);
}
}
@Test
public void testIsBitmapUsable_BitmapInUse() {
String inUseJsonOutput = "{\n" +
" \"format-specific\": {\n" +
" \"data\": {\n" +
" \"bitmaps\": [\n" +
" {\n" +
" \"name\": \"checkpoint-123\",\n" +
" \"flags\": [\"in-use\"]\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
scriptMock.when(() -> Script.runSimpleBashScriptWithFullResult(Mockito.anyString(), Mockito.anyInt()))
.thenReturn(inUseJsonOutput);
boolean result = storageProcessorSpy.isBitmapUsable(kvmStoragePoolMock, volumeObjectToMock, "checkpoint-123");
Assert.assertFalse("Bitmap should not be usable when marked as 'in-use'", result);
}
}
@Test
public void testIsBitmapUsable_BitmapNotFound() {
String jsonOutput = "{\n" +
" \"format-specific\": {\n" +
" \"data\": {\n" +
" \"bitmaps\": [\n" +
" {\n" +
" \"name\": \"checkpoint-456\",\n" +
" \"flags\": []\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
scriptMock.when(() -> Script.runSimpleBashScriptWithFullResult(Mockito.anyString(), Mockito.anyInt()))
.thenReturn(jsonOutput);
boolean result = storageProcessorSpy.isBitmapUsable(kvmStoragePoolMock, volumeObjectToMock, "checkpoint-123");
Assert.assertFalse("Bitmap should not be usable when not found", result);
}
}
@Test
public void testIsBitmapUsable_NoBitmaps() {
String jsonOutput = "{\n" +
" \"format-specific\": {\n" +
" \"data\": {\n" +
" \"bitmaps\": []\n" +
" }\n" +
" }\n" +
"}";
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
scriptMock.when(() -> Script.runSimpleBashScriptWithFullResult(Mockito.anyString(), Mockito.anyInt()))
.thenReturn(jsonOutput);
boolean result = storageProcessorSpy.isBitmapUsable(kvmStoragePoolMock, volumeObjectToMock, "checkpoint-123");
Assert.assertFalse("Bitmap should not be usable when no bitmaps exist", result);
}
}
@Test
public void testIsBitmapUsable_EmptyOutput() {
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
scriptMock.when(() -> Script.runSimpleBashScriptWithFullResult(Mockito.anyString(), Mockito.anyInt()))
.thenReturn("");
boolean result = storageProcessorSpy.isBitmapUsable(kvmStoragePoolMock, volumeObjectToMock, "checkpoint-123");
Assert.assertFalse("Bitmap should not be usable when qemu-img returns empty output", result);
}
}
@Test
public void testIsBitmapUsable_InvalidJson() {
String invalidJson = "{invalid json}";
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
scriptMock.when(() -> Script.runSimpleBashScriptWithFullResult(Mockito.anyString(), Mockito.anyInt()))
.thenReturn(invalidJson);
boolean result = storageProcessorSpy.isBitmapUsable(kvmStoragePoolMock, volumeObjectToMock, "checkpoint-123");
Assert.assertFalse("Bitmap should not be usable when JSON parsing fails", result);
}
}
@Test
public void testValidateClvmNgBitmapAndFallbackIfNeeded_NotClvmNg() throws LibvirtException {
Mockito.when(kvmStoragePoolMock.getType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
SnapshotObjectTO result = storageProcessorSpy.validateClvmNgBitmapAndFallbackIfNeeded(
snapshotObjectToMock, kvmStoragePoolMock, null, null, "snapshot-1",
volumeObjectToMock, connectMock, 30);
Assert.assertNull("Should return null for non-CLVM_NG storage", result);
}
@Test
public void testValidateClvmNgBitmapAndFallbackIfNeeded_NoParentSnapshot() throws LibvirtException {
Mockito.when(kvmStoragePoolMock.getType()).thenReturn(Storage.StoragePoolType.CLVM_NG);
Mockito.when(snapshotObjectToMock.getParentSnapshotPath()).thenReturn(null);
SnapshotObjectTO result = storageProcessorSpy.validateClvmNgBitmapAndFallbackIfNeeded(
snapshotObjectToMock, kvmStoragePoolMock, null, null, "snapshot-1",
volumeObjectToMock, connectMock, 30);
Assert.assertNull("Should return null when there's no parent snapshot", result);
}
@Test
public void testValidateClvmNgBitmapAndFallbackIfNeeded_EmptyParentsArray() throws LibvirtException {
Mockito.when(kvmStoragePoolMock.getType()).thenReturn(Storage.StoragePoolType.CLVM_NG);
Mockito.when(snapshotObjectToMock.getParentSnapshotPath()).thenReturn("/path/to/parent");
Mockito.when(snapshotObjectToMock.getParents()).thenReturn(new String[]{});
SnapshotObjectTO result = storageProcessorSpy.validateClvmNgBitmapAndFallbackIfNeeded(
snapshotObjectToMock, kvmStoragePoolMock, null, null, "snapshot-1",
volumeObjectToMock, connectMock, 30);
Assert.assertNull("Should return null when parents array is empty", result);
}
@Test
public void testValidateClvmNgBitmapAndFallbackIfNeeded_BitmapValidNoFallback() throws LibvirtException {
String validJsonOutput = "{\n" +
" \"format-specific\": {\n" +
" \"data\": {\n" +
" \"bitmaps\": [\n" +
" {\n" +
" \"name\": \"checkpoint-123\",\n" +
" \"flags\": []\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
Mockito.when(kvmStoragePoolMock.getType()).thenReturn(Storage.StoragePoolType.CLVM_NG);
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(snapshotObjectToMock.getParentSnapshotPath()).thenReturn("/path/to/parent");
Mockito.when(snapshotObjectToMock.getParents()).thenReturn(new String[]{"/snapshots/checkpoint-123"});
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
scriptMock.when(() -> Script.runSimpleBashScriptWithFullResult(Mockito.anyString(), Mockito.anyInt()))
.thenReturn(validJsonOutput);
SnapshotObjectTO result = storageProcessorSpy.validateClvmNgBitmapAndFallbackIfNeeded(
snapshotObjectToMock, kvmStoragePoolMock, null, null, "snapshot-1",
volumeObjectToMock, connectMock, 30);
Assert.assertNull("Should return null when bitmap is valid (no fallback needed)", result);
}
}
// ==================== CLVM/CLVM_NG Snapshot Path Parsing Tests ====================
@Test
public void testParseClvmSnapshotPath_ValidPath() {
String snapshotPath = "/dev/vg-storage/volume-uuid-123/snapshot-uuid-456";
Storage.StoragePoolType poolType = Storage.StoragePoolType.CLVM_NG;
try {
java.lang.reflect.Method method = KVMStorageProcessor.class.getDeclaredMethod(
"parseClvmSnapshotPath", String.class, Storage.StoragePoolType.class);
method.setAccessible(true);
String[] result = (String[]) method.invoke(storageProcessorSpy, snapshotPath, poolType);
Assert.assertNotNull("Should return parsed array for valid path", result);
Assert.assertEquals("Should return 4 elements", 4, result.length);
Assert.assertEquals("VG name should be vg-storage", "vg-storage", result[0]);
Assert.assertEquals("Volume UUID should be volume-uuid-123", "volume-uuid-123", result[1]);
Assert.assertEquals("Snapshot UUID should be snapshot-uuid-456", "snapshot-uuid-456", result[2]);
Assert.assertNotNull("MD5 hash should be computed", result[3]);
} catch (Exception e) {
Assert.fail("Failed to test parseClvmSnapshotPath: " + e.getMessage());
}
}
@Test
public void testParseClvmSnapshotPath_InvalidPathFormat() {
String snapshotPath = "/dev/vg-storage/invalid";
Storage.StoragePoolType poolType = Storage.StoragePoolType.CLVM;
try {
java.lang.reflect.Method method = KVMStorageProcessor.class.getDeclaredMethod(
"parseClvmSnapshotPath", String.class, Storage.StoragePoolType.class);
method.setAccessible(true);
String[] result = (String[]) method.invoke(storageProcessorSpy, snapshotPath, poolType);
Assert.assertNull("Should return null for invalid path format", result);
} catch (Exception e) {
Assert.fail("Failed to test parseClvmSnapshotPath: " + e.getMessage());
}
}
@Test
public void testDeleteClvmSnapshot_SuccessfulDeletion() {
String snapshotPath = "/dev/vg-storage/volume-uuid/snapshot-uuid";
Storage.StoragePoolType poolType = Storage.StoragePoolType.CLVM_NG;
try (MockedConstruction<Script> scriptConstruction = Mockito.mockConstruction(Script.class, (mock, context) -> {
Mockito.when(mock.execute()).thenReturn(null);
})) {
java.lang.reflect.Method method = KVMStorageProcessor.class.getDeclaredMethod(
"deleteClvmSnapshot", String.class, Storage.StoragePoolType.class, boolean.class);
method.setAccessible(true);
boolean result = (boolean) method.invoke(storageProcessorSpy, snapshotPath, poolType, true);
Assert.assertTrue("Should return true for successful deletion", result);
} catch (Exception e) {
Assert.fail("Failed to test deleteClvmSnapshot: " + e.getMessage());
}
}
@Test
public void testDeleteClvmSnapshot_SnapshotAlreadyDeleted() {
String snapshotPath = "/dev/vg-storage/volume-uuid/snapshot-uuid";
Storage.StoragePoolType poolType = Storage.StoragePoolType.CLVM;
try (MockedConstruction<Script> scriptConstruction = Mockito.mockConstruction(Script.class, (mock, context) -> {
Mockito.when(mock.execute()).thenReturn("Error: snapshot does not exist");
})) {
java.lang.reflect.Method method = KVMStorageProcessor.class.getDeclaredMethod(
"deleteClvmSnapshot", String.class, Storage.StoragePoolType.class, boolean.class);
method.setAccessible(true);
boolean result = (boolean) method.invoke(storageProcessorSpy, snapshotPath, poolType, true);
Assert.assertTrue("Should return true when snapshot already deleted", result);
} catch (Exception e) {
Assert.fail("Failed to test deleteClvmSnapshot: " + e.getMessage());
}
}
@Test
public void testDeleteClvmSnapshot_DeletionFailedWithoutCheck() {
String snapshotPath = "/dev/vg-storage/volume-uuid/snapshot-uuid";
Storage.StoragePoolType poolType = Storage.StoragePoolType.CLVM_NG;
try (MockedConstruction<Script> scriptConstruction = Mockito.mockConstruction(Script.class, (mock, context) -> {
Mockito.when(mock.execute()).thenReturn("Error: some other error");
})) {
java.lang.reflect.Method method = KVMStorageProcessor.class.getDeclaredMethod(
"deleteClvmSnapshot", String.class, Storage.StoragePoolType.class, boolean.class);
method.setAccessible(true);
boolean result = (boolean) method.invoke(storageProcessorSpy, snapshotPath, poolType, false);
Assert.assertFalse("Should return false when deletion fails", result);
} catch (Exception e) {
Assert.fail("Failed to test deleteClvmSnapshot: " + e.getMessage());
}
}
@Test
public void testDeleteClvmSnapshot_InvalidPath() {
String snapshotPath = "/invalid/path";
Storage.StoragePoolType poolType = Storage.StoragePoolType.CLVM;
try {
java.lang.reflect.Method method = KVMStorageProcessor.class.getDeclaredMethod(
"deleteClvmSnapshot", String.class, Storage.StoragePoolType.class, boolean.class);
method.setAccessible(true);
boolean result = (boolean) method.invoke(storageProcessorSpy, snapshotPath, poolType, true);
Assert.assertFalse("Should return false for invalid path", result);
} catch (Exception e) {
Assert.fail("Failed to test deleteClvmSnapshot: " + e.getMessage());
}
}
@Test
public void testComputeMd5Hash_ValidInput() {
String input = "snapshot-uuid-123";
try {
Method method = KVMStorageProcessor.class.getDeclaredMethod(
"computeMd5Hash", String.class);
method.setAccessible(true);
String result = (String) method.invoke(storageProcessorSpy, input);
Assert.assertNotNull("Should return non-null hash", result);
Assert.assertEquals("Hash should be 32 characters long (MD5)", 32, result.length());
Assert.assertTrue("Hash should contain only hex characters", result.matches("[0-9a-f]+"));
} catch (Exception e) {
Assert.fail("Failed to test computeMd5Hash: " + e.getMessage());
}
}
@Test
public void testComputeMd5Hash_EmptyInput() {
String input = "";
try {
java.lang.reflect.Method method = KVMStorageProcessor.class.getDeclaredMethod(
"computeMd5Hash", String.class);
method.setAccessible(true);
String result = (String) method.invoke(storageProcessorSpy, input);
Assert.assertNotNull("Should return non-null hash even for empty input", result);
Assert.assertEquals("Hash should be 32 characters long (MD5)", 32, result.length());
} catch (Exception e) {
Assert.fail("Failed to test computeMd5Hash: " + e.getMessage());
}
}
@Test
public void testComputeMd5Hash_ConsistentResults() {
String input = "snapshot-uuid-456";
try {
java.lang.reflect.Method method = KVMStorageProcessor.class.getDeclaredMethod(
"computeMd5Hash", String.class);
method.setAccessible(true);
String result1 = (String) method.invoke(storageProcessorSpy, input);
String result2 = (String) method.invoke(storageProcessorSpy, input);
Assert.assertEquals("Same input should produce same hash", result1, result2);
} catch (Exception e) {
Assert.fail("Failed to test computeMd5Hash: " + e.getMessage());
}
}
@Test
public void testCleanupBrokenBitmap_SuccessfulRemoval() {
String bitmapName = "checkpoint-123";
try (MockedConstruction<QemuImg> qemuImgConstruction = Mockito.mockConstruction(QemuImg.class, (mock, context) -> {
Mockito.doNothing().when(mock).bitmap(Mockito.any(), Mockito.any(), Mockito.anyString());
})) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
java.lang.reflect.Method method = KVMStorageProcessor.class.getDeclaredMethod(
"cleanupBrokenBitmap", KVMStoragePool.class, VolumeObjectTO.class, String.class);
method.setAccessible(true);
method.invoke(storageProcessorSpy, kvmStoragePoolMock, volumeObjectToMock, bitmapName);
Assert.assertTrue("Method should complete successfully", true);
} catch (Exception e) {
Assert.fail("Failed to test cleanupBrokenBitmap: " + e.getMessage());
}
}
@Test
public void testCleanupBrokenBitmap_QemuImgException() {
String bitmapName = "checkpoint-456";
try (MockedConstruction<QemuImg> qemuImgConstruction = Mockito.mockConstruction(QemuImg.class, (mock, context) -> {
Mockito.doThrow(new QemuImgException("Failed to remove bitmap"))
.when(mock).bitmap(Mockito.any(), Mockito.any(), Mockito.anyString());
})) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
java.lang.reflect.Method method = KVMStorageProcessor.class.getDeclaredMethod(
"cleanupBrokenBitmap", KVMStoragePool.class, VolumeObjectTO.class, String.class);
method.setAccessible(true);
method.invoke(storageProcessorSpy, kvmStoragePoolMock, volumeObjectToMock, bitmapName);
Assert.assertTrue("Method should handle exception gracefully", true);
} catch (Exception e) {
Assert.fail("Failed to test cleanupBrokenBitmap: " + e.getMessage());
}
}
@Test
public void testCleanupBrokenBitmap_NullVolumePath() {
String bitmapName = "checkpoint-789";
try {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn(null);
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
java.lang.reflect.Method method = KVMStorageProcessor.class.getDeclaredMethod(
"cleanupBrokenBitmap", KVMStoragePool.class, VolumeObjectTO.class, String.class);
method.setAccessible(true);
method.invoke(storageProcessorSpy, kvmStoragePoolMock, volumeObjectToMock, bitmapName);
Assert.assertTrue("Method should handle null volume path gracefully", true);
} catch (Exception e) {
Assert.fail("Failed to test cleanupBrokenBitmap: " + e.getMessage());
}
}
@Test
public void testIsBitmapUsable_MultipleFlagsWithInUse() {
String jsonOutput = "{\n" +
" \"format-specific\": {\n" +
" \"data\": {\n" +
" \"bitmaps\": [\n" +
" {\n" +
" \"name\": \"checkpoint-123\",\n" +
" \"flags\": [\"auto\", \"in-use\", \"persistent\"]\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
scriptMock.when(() -> Script.runSimpleBashScriptWithFullResult(Mockito.anyString(), Mockito.anyInt()))
.thenReturn(jsonOutput);
boolean result = storageProcessorSpy.isBitmapUsable(kvmStoragePoolMock, volumeObjectToMock, "checkpoint-123");
Assert.assertFalse("Bitmap should not be usable when 'in-use' flag is present among multiple flags", result);
}
}
@Test
public void testIsBitmapUsable_MultipleBitmapsOnlyFirstMatches() {
String jsonOutput = "{\n" +
" \"format-specific\": {\n" +
" \"data\": {\n" +
" \"bitmaps\": [\n" +
" {\n" +
" \"name\": \"checkpoint-123\",\n" +
" \"flags\": []\n" +
" },\n" +
" {\n" +
" \"name\": \"checkpoint-456\",\n" +
" \"flags\": [\"in-use\"]\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
"}";
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
scriptMock.when(() -> Script.runSimpleBashScriptWithFullResult(Mockito.anyString(), Mockito.anyInt()))
.thenReturn(jsonOutput);
boolean result = storageProcessorSpy.isBitmapUsable(kvmStoragePoolMock, volumeObjectToMock, "checkpoint-123");
Assert.assertTrue("Should match the first bitmap and return usable", result);
}
}
@Test
public void testIsBitmapUsable_NoFormatSpecificData() {
String jsonOutput = "{\n" +
" \"filename\": \"/dev/vg/volume\",\n" +
" \"format\": \"qcow2\"\n" +
"}";
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
scriptMock.when(() -> Script.runSimpleBashScriptWithFullResult(Mockito.anyString(), Mockito.anyInt()))
.thenReturn(jsonOutput);
boolean result = storageProcessorSpy.isBitmapUsable(kvmStoragePoolMock, volumeObjectToMock, "checkpoint-123");
Assert.assertFalse("Should return false when no format-specific data is present", result);
}
}
@Test
public void testIsBitmapUsable_NullOutput() {
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
Mockito.when(kvmStoragePoolMock.getLocalPathFor(Mockito.anyString()))
.thenReturn("/dev/vg/volume");
Mockito.when(volumeObjectToMock.getPath()).thenReturn("volume-path");
scriptMock.when(() -> Script.runSimpleBashScriptWithFullResult(Mockito.anyString(), Mockito.anyInt()))
.thenReturn(null);
boolean result = storageProcessorSpy.isBitmapUsable(kvmStoragePoolMock, volumeObjectToMock, "checkpoint-123");
Assert.assertFalse("Should return false when qemu-img returns null", result);
}
}
@Test
public void testIsBitmapUsable_ExceptionDuringCheck() {
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
boolean result = storageProcessorSpy.isBitmapUsable(kvmStoragePoolMock, volumeObjectToMock, "checkpoint-123");
Assert.assertFalse("Should return false when exception occurs", result);
}
}
}