content: /*
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.cloud.hypervisor.kvm.resource.wrapper;
  
  import com.cloud.agent.api.Answer;
  import com.cloud.agent.api.ScaleVmCommand;
  import com.cloud.agent.api.to.VirtualMachineTO;
  import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
  import com.cloud.template.VirtualMachineTemplate;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.cloud.vm.VirtualMachine;
  import junit.framework.TestCase;
  import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
  import org.junit.After;
  import org.junit.Before;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.libvirt.Connect;
  import org.libvirt.Domain;
  import org.libvirt.LibvirtException;
  import org.mockito.Mock;
  import org.mockito.MockedStatic;
  import org.mockito.Mockito;
  import org.mockito.Spy;
  import org.mockito.junit.MockitoJUnitRunner;
  
  @RunWith(MockitoJUnitRunner.class)
  public class LibvirtScaleVmCommandWrapperTest extends TestCase {
  
      @Spy
      LibvirtScaleVmCommandWrapper libvirtScaleVmCommandWrapperSpy = Mockito.spy(LibvirtScaleVmCommandWrapper.class);
  
      @Mock
      LibvirtComputingResource libvirtComputingResourceMock;
  
      @Mock
      ScaleVmCommand scaleVmCommandMock;
  
      @Mock
      LibvirtUtilitiesHelper libvirtUtilitiesHelperMock;
  
      @Mock
      Domain domainMock;
  
      @Mock
      Connect connectMock;
  
      @Mock
      LibvirtException libvirtException;
  
      @Mock
      Exception exception;
  
      LibvirtRequestWrapper wrapper;
      VirtualMachineTO vmTo;
  
      String scalingDetails;
  
      MockedStatic<LibvirtComputingResource> libvirtComputingResourceMocked;
  
      @Before
      public void init() {
          wrapper = LibvirtRequestWrapper.getInstance();
          assertNotNull(wrapper);
  
          vmTo = new VirtualMachineTO(1, "Test 1", VirtualMachine.Type.User, 2, 1000, 67108864, 67108864, VirtualMachineTemplate.BootloaderType.External, "Other Linux (64x)", true, true, "test123");
  
          long memory = ByteScaleUtils.bytesToKibibytes(vmTo.getMaxRam());
          int vcpus = vmTo.getCpus();
          int cpuShares = vcpus * vmTo.getSpeed();
          scalingDetails = String.format("%s memory to [%s KiB], CPU cores to [%s] and cpu_shares to [%s]", vmTo.toString(), memory, vcpus, cpuShares);
  
          libvirtComputingResourceMocked = Mockito.mockStatic(LibvirtComputingResource.class);
      }
  
      @Override
      @After
      public void tearDown() throws Exception {
          libvirtComputingResourceMocked.close();
      }
  
      @Test
      public void validateScaleVcpusRunningVcpusLessThanNewVcpusSetNewVcpu() throws LibvirtException{
          long runningVcpus = 1;
          int newVcpus = 2;
  
          Mockito.when(LibvirtComputingResource.countDomainRunningVcpus(Mockito.any())).thenReturn(runningVcpus);
          Mockito.doNothing().when(domainMock).setVcpus(Mockito.anyInt());
  
          libvirtScaleVmCommandWrapperSpy.scaleVcpus(domainMock, newVcpus, scalingDetails);
  
          Mockito.verify(domainMock).setVcpus(Mockito.anyInt());
      }
  
      @Test
      public void validateScaleVcpusRunningVcpusEqualThanNewVcpusDoNothing() throws LibvirtException{
          long runningVcpus = 2;
          int newVcpus = 2;
  
          Mockito.when(LibvirtComputingResource.countDomainRunningVcpus(Mockito.any())).thenReturn(runningVcpus);
  
          libvirtScaleVmCommandWrapperSpy.scaleVcpus(domainMock, newVcpus, scalingDetails);
  
          Mockito.verify(domainMock, Mockito.never()).setVcpus(Mockito.anyInt());
      }
  
      @Test
      public void validateScaleVcpusRunningVcpusHigherThanNewVcpusDoNothing() throws LibvirtException{
          long runningVcpus = 2;
          int newVcpus = 1;
  
          Mockito.when(LibvirtComputingResource.countDomainRunningVcpus(Mockito.any())).thenReturn(runningVcpus);
  
          libvirtScaleVmCommandWrapperSpy.scaleVcpus(domainMock, newVcpus, scalingDetails);
  
          Mockito.verify(domainMock, Mockito.never()).setVcpus(Mockito.anyInt());
      }
  
      @Test (expected = LibvirtException.class)
      public void validateScaleVcpusSetVcpusThrowLibvirtException() throws LibvirtException{
          long runningVcpus = 1;
          int newVcpus = 2;
  
          Mockito.when(LibvirtComputingResource.countDomainRunningVcpus(Mockito.any())).thenReturn(runningVcpus);
          Mockito.doThrow(LibvirtException.class).when(domainMock).setVcpus(Mockito.anyInt());
  
          libvirtScaleVmCommandWrapperSpy.scaleVcpus(domainMock, newVcpus, scalingDetails);
  
          Mockito.verify(domainMock, Mockito.never()).setVcpus(Mockito.anyInt());
      }
  
      @Test
      public void validateScaleMemoryMemoryLessThanZeroDoNothing() throws LibvirtException {
          long currentMemory = 1l;
          long newMemory = 0l;
  
          Mockito.when(LibvirtComputingResource.getDomainMemory(Mockito.any())).thenReturn(currentMemory);
  
          libvirtScaleVmCommandWrapperSpy.scaleMemory(domainMock, newMemory, scalingDetails);
  
          Mockito.verify(domainMock, Mockito.never()).getXMLDesc(Mockito.anyInt());
          Mockito.verify(domainMock, Mockito.never()).attachDevice(Mockito.anyString());
      }
  
      @Test
      public void validateScaleMemoryMemoryEqualToZeroDoNothing() throws LibvirtException {
          long currentMemory = 1l;
          long newMemory = 1l;
  
          Mockito.when(LibvirtComputingResource.getDomainMemory(Mockito.any())).thenReturn(currentMemory);
  
          libvirtScaleVmCommandWrapperSpy.scaleMemory(domainMock, newMemory, scalingDetails);
  
          Mockito.verify(domainMock, Mockito.never()).getXMLDesc(Mockito.anyInt());
          Mockito.verify(domainMock, Mockito.never()).attachDevice(Mockito.anyString());
      }
  
      @Test (expected = CloudRuntimeException.class)
      public void validateScaleMemoryDomainXmlDoesNotContainsMaxMemory() throws LibvirtException {
          long currentMemory = 1l;
          long newMemory = 2l;
  
          Mockito.when(LibvirtComputingResource.getDomainMemory(Mockito.any())).thenReturn(currentMemory);
          Mockito.doReturn("").when(domainMock).getXMLDesc(Mockito.anyInt());
  
          libvirtScaleVmCommandWrapperSpy.scaleMemory(domainMock, newMemory, scalingDetails);
  
          Mockito.verify(domainMock).getXMLDesc(Mockito.anyInt());
          Mockito.verify(domainMock, Mockito.never()).attachDevice(Mockito.anyString());
      }
  
      @Test (expected = LibvirtException.class)
      public void validateScaleMemoryAttachDeviceThrowsLibvirtException() throws LibvirtException {
          long currentMemory = 1l;
          long newMemory = 2l;
  
          Mockito.when(LibvirtComputingResource.getDomainMemory(Mockito.any())).thenReturn(currentMemory);
          Mockito.doReturn("<maxMemory slots='16' unit='KiB'>").when(domainMock).getXMLDesc(Mockito.anyInt());
          Mockito.doThrow(LibvirtException.class).when(domainMock).attachDevice(Mockito.anyString());
  
          libvirtScaleVmCommandWrapperSpy.scaleMemory(domainMock, newMemory, scalingDetails);
  
          Mockito.verify(domainMock).getXMLDesc(Mockito.anyInt());
          Mockito.verify(domainMock).attachDevice(Mockito.anyString());
      }
  
      @Test
      public void validateScaleMemory() throws LibvirtException {
          long currentMemory = 1l;
          long newMemory = 2l;
  
          Mockito.when(LibvirtComputingResource.getDomainMemory(Mockito.any())).thenReturn(currentMemory);
          Mockito.doReturn("<maxMemory slots='16' unit='KiB'>").when(domainMock).getXMLDesc(Mockito.anyInt());
          Mockito.doNothing().when(domainMock).attachDevice(Mockito.anyString());
  
          libvirtScaleVmCommandWrapperSpy.scaleMemory(domainMock, newMemory, scalingDetails);
  
          Mockito.verify(domainMock).getXMLDesc(Mockito.anyInt());
          Mockito.verify(domainMock).attachDevice(Mockito.anyString());
      }
  
      @Test
      public void validateExecuteHandleLibvirtException() throws LibvirtException {
          String errorMessage = "";
          int shares = vmTo.getCpus() * vmTo.getSpeed();
  
          Mockito.doReturn(vmTo).when(scaleVmCommandMock).getVirtualMachine();
          Mockito.doReturn(libvirtUtilitiesHelperMock).when(libvirtComputingResourceMock).getLibvirtUtilitiesHelper();
          Mockito.doReturn(shares).when(libvirtComputingResourceMock).calculateCpuShares(vmTo);
          Mockito.doThrow(libvirtException).when(libvirtUtilitiesHelperMock).getConnectionByVmName(Mockito.anyString());
          Mockito.doReturn(errorMessage).when(libvirtException).getMessage();
  
          Answer answer = libvirtScaleVmCommandWrapperSpy.execute(scaleVmCommandMock, libvirtComputingResourceMock);
  
          String details = String.format("Unable to scale %s due to [%s].", scalingDetails, errorMessage);
          assertFalse(answer.getResult());
          assertEquals(details, answer.getDetails());
      }
  
      @Test
      public void validateExecuteSuccessfully() throws LibvirtException {
          int shares = vmTo.getCpus() * vmTo.getSpeed();
  
          Mockito.doReturn(vmTo).when(scaleVmCommandMock).getVirtualMachine();
          Mockito.doReturn(libvirtUtilitiesHelperMock).when(libvirtComputingResourceMock).getLibvirtUtilitiesHelper();
          Mockito.doReturn(connectMock).when(libvirtUtilitiesHelperMock).getConnectionByVmName(Mockito.anyString());
          Mockito.doReturn(shares).when(libvirtComputingResourceMock).calculateCpuShares(vmTo);
          Mockito.doReturn(domainMock).when(connectMock).domainLookupByName(Mockito.anyString());
          Mockito.doNothing().when(libvirtScaleVmCommandWrapperSpy).scaleMemory(Mockito.any(), Mockito.anyLong(), Mockito.anyString());
          Mockito.doNothing().when(libvirtScaleVmCommandWrapperSpy).scaleVcpus(Mockito.any(), Mockito.anyInt(), Mockito.anyString());
  
          Answer answer = libvirtScaleVmCommandWrapperSpy.execute(scaleVmCommandMock, libvirtComputingResourceMock);
  
          String details = String.format("Successfully scaled %s.", scalingDetails);
          assertTrue(answer.getResult());
          assertEquals(details, answer.getDetails());
      }
  
      @Test(expected = Exception.class)
      public void validateExecuteThrowAnyOtherException() {
          Mockito.doThrow(Exception.class).when(libvirtComputingResourceMock).getLibvirtUtilitiesHelper();
  
          libvirtScaleVmCommandWrapperSpy.execute(scaleVmCommandMock, libvirtComputingResourceMock);
      }
  
      @Test
      public void updateCpuSharesTestOldSharesLessThanNewSharesUpdateShares() throws LibvirtException {
          int oldShares = 2000;
          int newShares = 3000;
  
          Mockito.when(LibvirtComputingResource.getCpuShares(Mockito.any())).thenReturn(oldShares);
          libvirtScaleVmCommandWrapperSpy.updateCpuShares(domainMock, newShares);
  
          libvirtComputingResourceMocked.verify(() -> LibvirtComputingResource.setCpuShares(domainMock, newShares),
                  Mockito.times(1));
          ;
      }
  
      @Test
      public void updateCpuSharesTestOldSharesHigherThanNewSharesDoNothing() throws LibvirtException {
          int oldShares = 3000;
          int newShares = 2000;
  
          Mockito.when(LibvirtComputingResource.getCpuShares(Mockito.any())).thenReturn(oldShares);
          libvirtScaleVmCommandWrapperSpy.updateCpuShares(domainMock, newShares);
  
  
          libvirtComputingResourceMocked.verify(() -> LibvirtComputingResource.setCpuShares(domainMock, newShares),
                  Mockito.times(0));
      }
  
      @Test
      public void updateCpuSharesTestOldSharesEqualsNewSharesDoNothing() throws LibvirtException {
          int oldShares = 2000;
          int newShares = 2000;
  
          Mockito.when(LibvirtComputingResource.getCpuShares(Mockito.any())).thenReturn(oldShares);
          libvirtScaleVmCommandWrapperSpy.updateCpuShares(domainMock, newShares);
  
          libvirtComputingResourceMocked.verify(() -> LibvirtComputingResource.setCpuShares(domainMock, newShares),
                  Mockito.times(0));
      }
  }
  