// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.vmware.resource;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.cloud.agent.api.Command;
import com.cloud.agent.api.ScaleVmAnswer;
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
import com.cloud.hypervisor.vmware.mo.HostDatastoreBrowserMO;
import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
import com.cloud.hypervisor.vmware.util.VmwareHelper;
import com.vmware.vim25.FileInfo;
import com.vmware.vim25.HostDatastoreBrowserSearchResults;
import com.vmware.vim25.HostDatastoreBrowserSearchSpec;
import com.vmware.vim25.VirtualTPM;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsAnswer;
import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsCommand;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;

import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CheckGuestOsMappingAnswer;
import com.cloud.agent.api.CheckGuestOsMappingCommand;
import com.cloud.agent.api.GetHypervisorGuestOsNamesAnswer;
import com.cloud.agent.api.GetHypervisorGuestOsNamesCommand;
import com.cloud.agent.api.ScaleVmCommand;
import com.cloud.agent.api.routing.GetAutoScaleMetricsAnswer;
import com.cloud.agent.api.routing.GetAutoScaleMetricsCommand;
import com.cloud.agent.api.to.DataTO;
import com.cloud.agent.api.to.NfsTO;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.agent.api.to.VolumeTO;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.hypervisor.vmware.mo.DatacenterMO;
import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
import com.cloud.hypervisor.vmware.util.VmwareClient;
import com.cloud.hypervisor.vmware.util.VmwareContext;
import com.cloud.network.router.VirtualRouterAutoScale;
import com.cloud.network.router.VirtualRouterAutoScale.AutoScaleMetrics;
import com.cloud.network.router.VirtualRouterAutoScale.AutoScaleMetricsValue;
import com.cloud.network.router.VirtualRouterAutoScale.VirtualRouterAutoScaleCounter;
import com.cloud.storage.resource.VmwareStorageProcessor;
import com.cloud.storage.resource.VmwareStorageProcessor.VmwareStorageProcessorConfigurableFields;
import com.cloud.storage.resource.VmwareStorageSubsystemCommandHandler;
import com.cloud.utils.ExecutionResult;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VmDetailConstants;
import com.vmware.vim25.GuestOsDescriptor;
import com.vmware.vim25.HostCapability;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.VimPortType;
import com.vmware.vim25.VirtualDevice;
import com.vmware.vim25.VirtualDeviceConfigSpec;
import com.vmware.vim25.VirtualMachineConfigSpec;
import com.vmware.vim25.VirtualMachineVideoCard;

@RunWith(MockitoJUnitRunner.class)
public class VmwareResourceTest {

    private static final String VOLUME_PATH = "XXXXXXXXXXXX";

    @Spy
    @InjectMocks
    VmwareResource vmwareResource = new VmwareResource();

    @Mock
    VmwareStorageProcessor storageProcessor;
    @Mock
    VmwareStorageSubsystemCommandHandler storageHandler;

    @Spy
    @InjectMocks
    VmwareResource _resource = new VmwareResource() {

        @Override
        public ScaleVmAnswer execute(ScaleVmCommand cmd) {
            return super.execute(cmd);
        }

        @Override
        public VmwareHypervisorHost getHyperHost(VmwareContext context, Command cmd) {
            return hyperHost;
        }

    };

    @Mock
    VmwareContext context;
    @Mock
    ScaleVmCommand cmd;
    @Mock
    VirtualMachineTO vmSpec;
    @Mock
    VmwareHypervisorHost hyperHost;
    @Mock
    VirtualMachineMO vmMo;
    @Mock
    VirtualMachineConfigSpec vmConfigSpec;
    @Mock
    VirtualMachineMO vmMo3dgpu;
    @Mock
    VirtualMachineTO vmSpec3dgpu;
    @Mock
    VirtualMachineVideoCard videoCard;
    @Mock
    VirtualDevice virtualDevice;
    @Mock
    DataTO srcDataTO;
    @Mock
    NfsTO srcDataNfsTO;
    @Mock
    VolumeTO volume;
    @Mock
    ManagedObjectReference mor;
    @Mock
    DatacenterMO datacenter;
    @Mock
    DataTO destDataTO;
    @Mock
    PrimaryDataStoreTO destDataStoreTO;
    @Mock
    HostMO host;
    @Mock
    ManagedObjectReference hostRef;
    @Mock
    ManagedObjectReference computeRef;
    @Mock
    ManagedObjectReference envRef;
    @Mock
    VmwareClient client;
    @Mock
    VimPortType vimService;
    @Mock
    HostCapability hostCapability;
    @Mock
    ManagedObjectReference _morHyperHost;

    CopyCommand storageCmd;
    EnumMap<VmwareStorageProcessorConfigurableFields, Object> params = new EnumMap<VmwareStorageProcessorConfigurableFields,Object>(VmwareStorageProcessorConfigurableFields.class);

    private static final String NFS_VERSION = "3";
    private static final String NFS_VERSION_NOT_PRESENT = null;
    private static final long VRAM_MEMORY_SIZE = 131072l;
    private static final long VIDEO_CARD_MEMORY_SIZE = 65536l;
    private static final Boolean FULL_CLONE_FLAG = true;

    final static long[] vpcStats = { 1000L, 2000L };
    final static long[] networkStats = { 3000L, 4000L };
    final static long[] lbStats = { 5L };

    private Map<String,String> specsArray = new HashMap<String,String>();


    @Before
    public void setup() throws Exception {
        storageCmd = mock(CopyCommand.class);
        doReturn(context).when(_resource).getServiceContext(null);
        when(cmd.getVirtualMachine()).thenReturn(vmSpec);

        when(storageCmd.getSrcTO()).thenReturn(srcDataTO);
        when(srcDataTO.getDataStore()).thenReturn(srcDataNfsTO);
        when(srcDataNfsTO.getNfsVersion()).thenReturn(NFS_VERSION);
        when(videoCard.getVideoRamSizeInKB()).thenReturn(VIDEO_CARD_MEMORY_SIZE);
        when(volume.getPath()).thenReturn(VOLUME_PATH);

        when(storageCmd.getDestTO()).thenReturn(destDataTO);
        when(destDataTO.getHypervisorType()).thenReturn(HypervisorType.VMware);
        when(destDataTO.getDataStore()).thenReturn(destDataStoreTO);
        when(destDataStoreTO.isFullCloneFlag()).thenReturn(FULL_CLONE_FLAG);
        when(volume.getPath()).thenReturn(VOLUME_PATH);
        when(vmSpec.getDetails()).thenReturn(specsArray);

        when(vmMo.getContext()).thenReturn(context);
        when(vmMo.getRunningHost()).thenReturn(host);
        when(host.getMor()).thenReturn(hostRef);
        when(context.getVimClient()).thenReturn(client);
        when(client.getMoRefProp(hostRef, "parent")).thenReturn(computeRef);
        when(client.getMoRefProp(computeRef, "environmentBrowser")).thenReturn(envRef);
        when(context.getService()).thenReturn(vimService);
        when(vimService.queryTargetCapabilities(envRef, hostRef)).thenReturn(hostCapability);
        when(hostCapability.isNestedHVSupported()).thenReturn(true);
    }

    //Test successful scaling up the vm
    @Test
    public void testScaleVMF1() throws Exception {
        when(_resource.getHyperHost(context, null)).thenReturn(hyperHost);
        doReturn("i-2-3-VM").when(cmd).getVmName();
        when(hyperHost.findVmOnHyperHost("i-2-3-VM")).thenReturn(vmMo);

        _resource.execute(cmd);
        verify(_resource).execute(cmd);
    }

    @Test
    public void testGenerateMacSequence() {
        final NicTO nicTo1 = new NicTO();
        nicTo1.setMac("01:23:45:67:89:AB");
        nicTo1.setDeviceId(1);

        final NicTO nicTo2 = new NicTO();
        nicTo2.setMac("02:00:65:b5:00:03");
        nicTo2.setDeviceId(0);

        //final NicTO [] nicTOs = {nicTO1, nicTO2, nicTO3};
        //final NicTO[] nics = new NicTO[]{nic};
        final NicTO[] nics = new NicTO[] {nicTo1, nicTo2};

        String macSequence = _resource.generateMacSequence(nics);
        assertEquals("02:00:65:b5:00:03|01:23:45:67:89:AB", macSequence);
    }

    @Test
    public void testReplaceNicsMacSequenceInBootArgs() {
        String bootArgs = "nic_macs=02:00:65:b5:00:03|7C02:00:4f:1b:00:15|7C1e:00:54:00:00:0f|7C02:00:35:fa:00:11|7C02:00:47:40:00:12";
        doReturn(bootArgs).when(vmSpec).getBootArgs();

        String oldMacSequence = "7C02:00:35:fa:00:11|7C02:00:47:40:00:12";
        String newMacSequence = "7C02:00:0c:1d:00:1d|7C02:00:68:0f:00:1e";

        String updatedBootArgs = _resource.replaceNicsMacSequenceInBootArgs(oldMacSequence, newMacSequence, vmSpec);

        String newBootArgs = "nic_macs=02:00:65:b5:00:03|7C02:00:4f:1b:00:15|7C1e:00:54:00:00:0f|7C02:00:0c:1d:00:1d|7C02:00:68:0f:00:1e";
        assertEquals(newBootArgs, updatedBootArgs);
    }

    @Test
    public void testConfigureVideoCardSvgaVramProvided() throws Exception {
        Map<String, String> specDetails = new HashMap<String, String>();
        specDetails.put("svga.vramSize", String.valueOf(VRAM_MEMORY_SIZE));
        when(vmSpec3dgpu.getDetails()).thenReturn(specDetails);

        _resource.configureVideoCard(vmMo3dgpu, vmSpec3dgpu, vmConfigSpec);
        verify(_resource).setNewVRamSizeVmVideoCard(vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec);
    }

    @Test
    public void testConfigureVideoCardNotSvgaVramProvided() throws Exception {
        _resource.configureVideoCard(vmMo3dgpu, vmSpec3dgpu, vmConfigSpec);
        verify(_resource, never()).setNewVRamSizeVmVideoCard(vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec);
    }

    @Test
    public void testModifyVmVideoCardVRamSizeDifferentVramSizes() {
        _resource.modifyVmVideoCardVRamSize(videoCard, vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec);
        verify(_resource).configureSpecVideoCardNewVRamSize(videoCard, VRAM_MEMORY_SIZE, vmConfigSpec);
    }

    @Test
    public void testModifyVmVideoCardVRamSizeEqualSizes() {
        _resource.modifyVmVideoCardVRamSize(videoCard, vmMo3dgpu, VIDEO_CARD_MEMORY_SIZE, vmConfigSpec);
        verify(_resource, never()).configureSpecVideoCardNewVRamSize(videoCard, VIDEO_CARD_MEMORY_SIZE, vmConfigSpec);
    }

    @Test
    public void testSetNewVRamSizeVmVideoCardPresent() throws Exception {
        when(vmMo3dgpu.getAllDeviceList()).thenReturn(Arrays.asList(videoCard, virtualDevice));
        _resource.setNewVRamSizeVmVideoCard(vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec);
        verify(_resource).modifyVmVideoCardVRamSize(videoCard, vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec);
    }

    @Test
    public void testSetNewVRamSizeVmVideoCardNotPresent() throws Exception {
        when(vmMo3dgpu.getAllDeviceList()).thenReturn(Arrays.asList(virtualDevice));
        _resource.setNewVRamSizeVmVideoCard(vmMo3dgpu, VRAM_MEMORY_SIZE, vmConfigSpec);
        verify(_resource, never()).modifyVmVideoCardVRamSize(any(VirtualMachineVideoCard.class), eq(vmMo3dgpu), eq(VRAM_MEMORY_SIZE), eq(vmConfigSpec));
    }

    @Test
    public void testConfigureSpecVideoCardNewVRamSize() {
        when(vmConfigSpec.getDeviceChange()).thenReturn(new ArrayList<VirtualDeviceConfigSpec>());
        _resource.configureSpecVideoCardNewVRamSize(videoCard, VRAM_MEMORY_SIZE, vmConfigSpec);

        InOrder inOrder = Mockito.inOrder(videoCard, vmConfigSpec);
        inOrder.verify(videoCard).setVideoRamSizeInKB(VRAM_MEMORY_SIZE);
        inOrder.verify(videoCard).setUseAutoDetect(false);
        inOrder.verify(vmConfigSpec).getDeviceChange();
    }

    // ---------------------------------------------------------------------------------------------------

    @Test
    public void testgetNfsVersionFromNfsTONull(){
        assertFalse(_resource.getStorageNfsVersionFromNfsTO(null));
    }

    @Test
    public void testgetNfsVersionFromNfsTONfsVersionNull(){
        when(srcDataNfsTO.getNfsVersion()).thenReturn(NFS_VERSION_NOT_PRESENT);
        assertFalse(_resource.getStorageNfsVersionFromNfsTO(srcDataNfsTO));
    }

    @Test
    public void testgetNfsVersionFromNfsTONfsVersion(){
        assertTrue(_resource.getStorageNfsVersionFromNfsTO(srcDataNfsTO));
    }

    // ---------------------------------------------------------------------------------------------------

    @Test
    public void testSetCurrentNfsVersionInProcessorAndHandler(){
        params.put(VmwareStorageProcessorConfigurableFields.NFS_VERSION, NFS_VERSION);
        _resource.reconfigureProcessorByHandler(params);
        verify(storageHandler).reconfigureStorageProcessor(params);
    }

    // ---------------------------------------------------------------------------------------------------

    @Test
    public void testExamineStorageSubSystemCommandNfsVersionNotPresent(){
        when(srcDataNfsTO.getNfsVersion()).thenReturn(NFS_VERSION_NOT_PRESENT);
        _resource.examineStorageSubSystemCommandNfsVersion(storageCmd,params);
        assertTrue(params.isEmpty());
    }

    @Test
    public void testExamineStorageSubSystemCommandNfsVersion(){
        _resource.examineStorageSubSystemCommandNfsVersion(storageCmd, params);
        assertEquals(1, params.size());
        assertEquals(NFS_VERSION, params.get(VmwareStorageProcessorConfigurableFields.NFS_VERSION));
    }

    // ---------------------------------------------------------------------------------------------------

    @Test
    public void testExamineStorageSubSystemCommandFullCloneFlagForVmwareNullHypervisor(){
        when(destDataTO.getHypervisorType()).thenReturn(null);
        _resource.examineStorageSubSystemCommandFullCloneFlagForVmware(storageCmd, params);
        verify(destDataTO, never()).getDataStore();
    }

    @Test
    public void testExamineStorageSubSystemCommandFullCloneFlagForHypervisorNotVmware(){
        when(destDataTO.getHypervisorType()).thenReturn(HypervisorType.XenServer);
        _resource.examineStorageSubSystemCommandFullCloneFlagForVmware(storageCmd, params);
        verify(destDataTO, never()).getDataStore();
    }

    @Test
    public void testExamineStorageSubSystemCommandFullCloneFlagForVmware(){
        EnumMap<VmwareStorageProcessorConfigurableFields, Object> params2 = _resource.examineStorageSubSystemCommandFullCloneFlagForVmware(storageCmd, params);
        verify(destDataTO).getDataStore();
        verify(destDataStoreTO, times(2)).isFullCloneFlag();
        assertEquals(2, params2.size());
        assertEquals(FULL_CLONE_FLAG, params2.get(VmwareStorageProcessorConfigurableFields.FULL_CLONE_FLAG));
    }

    @Test
    public void testExamineStorageSubSystemCommandFullCloneFlagForVmwareNull(){
        when(destDataStoreTO.isFullCloneFlag()).thenReturn(null);
        _resource.examineStorageSubSystemCommandFullCloneFlagForVmware(storageCmd, params);
        verify(destDataTO).getDataStore();
        verify(destDataStoreTO).isFullCloneFlag();
        assertTrue(params.isEmpty());
    }

    // ---------------------------------------------------------------------------------------------------

    @SuppressWarnings("unchecked")
    @Test
    public void checkStorageProcessorAndHandlerNfsVersionAttributeVersionNotSet(){
        _resource.checkStorageProcessorAndHandlerNfsVersionAttribute(storageCmd);
        verify(_resource).examineStorageSubSystemCommandNfsVersion(eq(storageCmd), any(EnumMap.class));
        verify(_resource).examineStorageSubSystemCommandFullCloneFlagForVmware(eq(storageCmd), any(EnumMap.class));
        verify(_resource).reconfigureProcessorByHandler(any(EnumMap.class));
        assertEquals(NFS_VERSION, _resource.storageNfsVersion);
    }

    @SuppressWarnings("unchecked")
    @Test
    public void checkStorageProcessorAndHandlerNfsVersionAttributeVersionSet(){
        _resource.storageNfsVersion = NFS_VERSION;
        _resource.checkStorageProcessorAndHandlerNfsVersionAttribute(storageCmd);
        verify(_resource, never()).examineStorageSubSystemCommandNfsVersion(eq(storageCmd), any(EnumMap.class));
    }

    @Test(expected=CloudRuntimeException.class)
    public void testFindVmOnDatacenterNullHyperHostReference() throws Exception {
        try (MockedConstruction<DatacenterMO> ignored = mockConstruction(DatacenterMO.class)) {
            _resource.findVmOnDatacenter(context, hyperHost, volume);
        }
    }

    @Test
    public void testFindVmOnDatacenter() throws Exception {
        when(hyperHost.getHyperHostDatacenter()).thenReturn(mor);
        try (MockedConstruction<DatacenterMO> ignored = mockConstruction(DatacenterMO.class, (mock, context) -> {
            when(mock.findVm(VOLUME_PATH)).thenReturn(vmMo);
            when(mock.getMor()).thenReturn(mor);
        })) {
            VirtualMachineMO result = _resource.findVmOnDatacenter(context, hyperHost, volume);
            assertEquals(vmMo, result);
        }
    }

    @Test
    public void testConfigNestedHVSupportFlagTrue() throws Exception{
        specsArray.put(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG, "true");
        _resource.configNestedHVSupport(vmMo, vmSpec, vmConfigSpec);
        verify(vmMo).getRunningHost();
        verify(host).getMor();
        verify(context, times(2)).getVimClient();
        verify(client).getMoRefProp(hostRef, "parent");
        verify(client).getMoRefProp(computeRef, "environmentBrowser");
        verify(context).getService();
        verify(vimService).queryTargetCapabilities(envRef, hostRef);
        verify(hostCapability).isNestedHVSupported();

        verify(vmConfigSpec).setNestedHVEnabled(true);
    }

    @Test
    public void testConfigNestedHVSupportFlagFalse() throws Exception{
        specsArray.put(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG, "false");
        _resource.configNestedHVSupport(vmMo, vmSpec, vmConfigSpec);
        verify(vmMo, never()).getRunningHost();
    }

    @Test
    public void testGetAutoScaleMetricsCommandForVpc() {
        List<AutoScaleMetrics> metrics = new ArrayList<>();
        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LB_AVERAGE_CONNECTIONS, 1L, 2L, 3L, 4));
        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NETWORK_RECEIVED_AVERAGE_MBPS, 1L, 2L, 3L, 4));
        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NETWORK_TRANSMIT_AVERAGE_MBPS, 1L, 2L, 3L, 4));

        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, metrics);

        doReturn(vpcStats).when(vmwareResource).getVPCNetworkStats(anyString(), anyString(), anyString(), anyString());
        doReturn(lbStats).when(vmwareResource).getNetworkLbStats(Mockito.nullable(String.class), Mockito.nullable(String.class), Mockito.nullable(Integer.class));

        Answer answer = vmwareResource.executeRequest(getAutoScaleMetricsCommand);
        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);

        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();

        assertEquals(3, values.size());
        for (AutoScaleMetricsValue value : values) {
            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.LB_AVERAGE_CONNECTIONS)) {
                assertEquals(Double.valueOf(lbStats[0]), value.getValue());
            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NETWORK_TRANSMIT_AVERAGE_MBPS)) {
                assertEquals(Double.valueOf(Double.valueOf(vpcStats[0]) / VirtualRouterAutoScale.MBITS_TO_BYTES), value.getValue());
            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScaleCounter.NETWORK_RECEIVED_AVERAGE_MBPS)) {
                assertEquals(Double.valueOf(Double.valueOf(vpcStats[1]) / VirtualRouterAutoScale.MBITS_TO_BYTES), value.getValue());
            }
        }
    }

    @Test
    public void testGetAutoScaleMetricsCommandForNetwork() {
        List<AutoScaleMetrics> metrics = new ArrayList<>();
        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.LB_AVERAGE_CONNECTIONS, 1L, 2L, 3L, 4));
        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NETWORK_RECEIVED_AVERAGE_MBPS, 1L, 2L, 3L, 4));
        metrics.add(new AutoScaleMetrics(VirtualRouterAutoScaleCounter.NETWORK_TRANSMIT_AVERAGE_MBPS, 1L, 2L, 3L, 4));

        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", false, "10.10.10.10", 8080, metrics);

        doReturn(networkStats).when(vmwareResource).getNetworkStats(anyString(), anyString());
        doReturn(lbStats).when(vmwareResource).getNetworkLbStats(Mockito.nullable(String.class), Mockito.nullable(String.class), Mockito.nullable(Integer.class));

        Answer answer = vmwareResource.executeRequest(getAutoScaleMetricsCommand);
        assertTrue(answer instanceof GetAutoScaleMetricsAnswer);

        GetAutoScaleMetricsAnswer getAutoScaleMetricsAnswer = (GetAutoScaleMetricsAnswer) answer;
        List<AutoScaleMetricsValue> values = getAutoScaleMetricsAnswer.getValues();

        assertEquals(3, values.size());
        for (AutoScaleMetricsValue value : values) {
            if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.LB_AVERAGE_CONNECTIONS)) {
                assertEquals(Double.valueOf(lbStats[0]), value.getValue());
            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NETWORK_TRANSMIT_AVERAGE_MBPS)) {
                assertEquals(Double.valueOf(Double.valueOf(networkStats[0]) / VirtualRouterAutoScale.MBITS_TO_BYTES), value.getValue());
            } else if (value.getMetrics().getCounter().equals(VirtualRouterAutoScale.VirtualRouterAutoScaleCounter.NETWORK_RECEIVED_AVERAGE_MBPS)) {
                assertEquals(Double.valueOf(Double.valueOf(networkStats[1]) / VirtualRouterAutoScale.MBITS_TO_BYTES), value.getValue());
            }
        }
    }

    @Test
    public void testGetStatsForVpcStats() {
        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, null);

        String args = "-l " + getAutoScaleMetricsCommand.getPublicIP() + " -g";
        ExecutionResult executionResult = new ExecutionResult(true, vpcStats[0] + ":" + vpcStats[1]);
        doReturn(executionResult).when(vmwareResource).executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "vpc_netusage.sh", args);

        long[] stats = vmwareResource.getVPCNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP(), "get", "");
        assertEquals(2, stats.length);
        assertEquals(vpcStats[0], stats[0]);
        assertEquals(vpcStats[1], stats[1]);
    }

    @Test
    public void testGetStatsForNetworkStats() {
        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", false, "10.10.10.10", 8080, null);

        String args = "-g -l " + getAutoScaleMetricsCommand.getPublicIP();
        ExecutionResult executionResult = new ExecutionResult(true, networkStats[0] + ":" + networkStats[1]);
        doReturn(executionResult).when(vmwareResource).executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "netusage.sh", args);

        long[] stats = vmwareResource.getNetworkStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP());
        assertEquals(2, stats.length);
        assertEquals(networkStats[0], stats[0]);
        assertEquals(networkStats[1], stats[1]);
    }

    @Test
    public void testGetStatsForLbStats() {
        GetAutoScaleMetricsCommand getAutoScaleMetricsCommand = new GetAutoScaleMetricsCommand("192.168.10.1", true, "10.10.10.10", 8080, null);

        String args = getAutoScaleMetricsCommand.getPublicIP() + " " + getAutoScaleMetricsCommand.getPort();
        ExecutionResult executionResult = new ExecutionResult(true, String.valueOf(lbStats[0]));
        doReturn(executionResult).when(vmwareResource).executeInVR(getAutoScaleMetricsCommand.getPrivateIP(), "get_haproxy_stats.sh", args);

        long[] stats = vmwareResource.getNetworkLbStats(getAutoScaleMetricsCommand.getPrivateIP(), getAutoScaleMetricsCommand.getPublicIP(), getAutoScaleMetricsCommand.getPort());

        assertEquals(1, stats.length);
        assertEquals(lbStats[0], stats[0]);
    }

    @Test
    public void testCheckGuestOsMappingCommandFailure() throws Exception {
        CheckGuestOsMappingCommand cmd = mock(CheckGuestOsMappingCommand.class);
        when(cmd.getGuestOsName()).thenReturn("CentOS 7.2");
        when(cmd.getGuestOsHypervisorMappingName()).thenReturn("centosWrongName");
        when(_resource.getHyperHost(context, null)).thenReturn(hyperHost);
        when(hyperHost.getGuestOsDescriptor("centosWrongName")).thenReturn(null);

        CheckGuestOsMappingAnswer answer = _resource.execute(cmd);

        assertFalse(answer.getResult());
    }

    @Test
    public void testCheckGuestOsMappingCommandSuccess() throws Exception {
        CheckGuestOsMappingCommand cmd = mock(CheckGuestOsMappingCommand.class);
        when(cmd.getGuestOsName()).thenReturn("CentOS 7.2");
        when(cmd.getGuestOsHypervisorMappingName()).thenReturn("centos64Guest");
        when(_resource.getHyperHost(context, null)).thenReturn(hyperHost);
        GuestOsDescriptor guestOsDescriptor = mock(GuestOsDescriptor.class);
        when(hyperHost.getGuestOsDescriptor("centos64Guest")).thenReturn(guestOsDescriptor);
        when(guestOsDescriptor.getFullName()).thenReturn("centos64Guest");

        CheckGuestOsMappingAnswer answer = _resource.execute(cmd);

        assertTrue(answer.getResult());
    }

    @Test
    public void testCheckGuestOsMappingCommandException() {
        CheckGuestOsMappingCommand cmd = mock(CheckGuestOsMappingCommand.class);
        when(cmd.getGuestOsName()).thenReturn("CentOS 7.2");
        when(cmd.getGuestOsHypervisorMappingName()).thenReturn("centos64Guest");
        when(_resource.getHyperHost(context, null)).thenReturn(null);

        CheckGuestOsMappingAnswer answer = _resource.execute(cmd);

        assertFalse(answer.getResult());
    }

    @Test
    public void testGetHypervisorGuestOsNamesCommandFailure() throws Exception {
        GetHypervisorGuestOsNamesCommand cmd = mock(GetHypervisorGuestOsNamesCommand.class);
        when(cmd.getKeyword()).thenReturn("CentOS");
        when(_resource.getHyperHost(context, null)).thenReturn(hyperHost);
        when(hyperHost.getGuestOsDescriptors()).thenReturn(null);

        GetHypervisorGuestOsNamesAnswer answer = _resource.execute(cmd);

        assertFalse(answer.getResult());
    }

    @Test
    public void testGetHypervisorGuestOsNamesCommandSuccessWithKeyword() throws Exception {
        GetHypervisorGuestOsNamesCommand cmd = mock(GetHypervisorGuestOsNamesCommand.class);
        when(cmd.getKeyword()).thenReturn("CentOS");
        when(_resource.getHyperHost(context, null)).thenReturn(hyperHost);
        GuestOsDescriptor guestOsDescriptor = mock(GuestOsDescriptor.class);
        when(guestOsDescriptor.getFullName()).thenReturn("centos64Guest");
        when(guestOsDescriptor.getId()).thenReturn("centos64Guest");
        List<GuestOsDescriptor> guestOsDescriptors = new ArrayList<>();
        guestOsDescriptors.add(guestOsDescriptor);
        when(hyperHost.getGuestOsDescriptors()).thenReturn(guestOsDescriptors);

        GetHypervisorGuestOsNamesAnswer answer = _resource.execute(cmd);

        assertTrue(answer.getResult());
        assertEquals("centos64Guest", answer.getHypervisorGuestOsNames().get(0).first());
    }

    @Test
    public void testGetHypervisorGuestOsNamesCommandSuccessWithoutKeyword() throws Exception {
        GetHypervisorGuestOsNamesCommand cmd = mock(GetHypervisorGuestOsNamesCommand.class);
        when(_resource.getHyperHost(context, null)).thenReturn(hyperHost);
        GuestOsDescriptor guestOsDescriptor = mock(GuestOsDescriptor.class);
        when(guestOsDescriptor.getFullName()).thenReturn("centos64Guest");
        when(guestOsDescriptor.getId()).thenReturn("centos64Guest");
        List<GuestOsDescriptor> guestOsDescriptors = new ArrayList<>();
        guestOsDescriptors.add(guestOsDescriptor);
        when(hyperHost.getGuestOsDescriptors()).thenReturn(guestOsDescriptors);

        GetHypervisorGuestOsNamesAnswer answer = _resource.execute(cmd);

        assertTrue(answer.getResult());
        assertEquals("centos64Guest", answer.getHypervisorGuestOsNames().get(0).first());
    }

    @Test
    public void testGetHypervisorGuestOsNamesCommandException() {
        GetHypervisorGuestOsNamesCommand cmd = mock(GetHypervisorGuestOsNamesCommand.class);
        when(cmd.getKeyword()).thenReturn("CentOS");
        when(_resource.getHyperHost(context, null)).thenReturn(null);

        GetHypervisorGuestOsNamesAnswer answer = _resource.execute(cmd);

        assertFalse(answer.getResult());
    }

    @Test
    public void testExecuteWithValidPath() throws Exception {
        // Setup
        ListDataStoreObjectsCommand cmd = new ListDataStoreObjectsCommand(destDataStoreTO, "valid/path", 0, 10);
        HostDatastoreBrowserMO browserMo = mock(HostDatastoreBrowserMO.class);
        HostDatastoreBrowserSearchResults results = mock(HostDatastoreBrowserSearchResults.class);
        FileInfo fileInfo = mock(FileInfo.class);
        List<FileInfo> fileInfoList = new ArrayList<>();
        fileInfoList.add(fileInfo);
        Date date = new Date();

        doReturn(context).when(vmwareResource).getServiceContext(any());
        doReturn(hyperHost).when(vmwareResource).getHyperHost(context);
        when(browserMo.searchDatastore(anyString(), any(HostDatastoreBrowserSearchSpec.class))).thenReturn(results);
        when(results.getFile()).thenReturn(fileInfoList);
        when(fileInfo.getPath()).thenReturn("file.txt");
        when(fileInfo.getFileSize()).thenReturn(1L);
        when(fileInfo.getModification()).thenReturn(VmwareHelper.getXMLGregorianCalendar(date, 0));

        // Execute
        ListDataStoreObjectsAnswer answer;
        try (MockedStatic<HypervisorHostHelper> ignored = mockStatic(HypervisorHostHelper.class);
             MockedConstruction<DatastoreMO> ignored2 = mockConstruction(DatastoreMO.class, (mock, context1) -> {
                 when(mock.getName()).thenReturn("datastore");
                 when(mock.getHostDatastoreBrowserMO()).thenReturn(browserMo);
             })
        ) {
            when(HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(any(), any())).thenReturn(mor);

            answer = vmwareResource.execute(cmd);

        }
        // Verify
        assertNotNull(answer);
        assertTrue(answer.getResult());
        assertEquals(1, answer.getCount());
        assertEquals(Collections.singletonList("file.txt"), answer.getNames());
        assertEquals(Collections.singletonList("valid/path/file.txt"), answer.getPaths());
        assertEquals(Collections.singletonList("[datastore] valid/path/file.txt"), answer.getAbsPaths());
        assertEquals(Collections.singletonList(false), answer.getIsDirs());
        assertEquals(Collections.singletonList(1L), answer.getSizes());
        assertEquals(Collections.singletonList(date.getTime()), answer.getLastModified());
    }

    @Test
    public void testExecuteWithInvalidPath() throws Exception {
        // Setup
        ListDataStoreObjectsCommand cmd = new ListDataStoreObjectsCommand(destDataStoreTO, "invalid/path", 0, 10);
        HostDatastoreBrowserMO browserMo = mock(HostDatastoreBrowserMO.class);
        doReturn(context).when(vmwareResource).getServiceContext();
        doReturn(hyperHost).when(vmwareResource).getHyperHost(context);
        when(browserMo.searchDatastore(anyString(), any(HostDatastoreBrowserSearchSpec.class))).thenThrow(new Exception("was not found"));

        ListDataStoreObjectsAnswer answer;
        try (MockedStatic<HypervisorHostHelper> ignored = mockStatic(HypervisorHostHelper.class);
             MockedConstruction<DatastoreMO> ignored2 = mockConstruction(DatastoreMO.class, (mock, context1) -> {
                 when(mock.getName()).thenReturn("datastore");
                 when(mock.fileExists(anyString())).thenReturn(false);
                 when(mock.getHostDatastoreBrowserMO()).thenReturn(browserMo);
             })
        ) {
            when(HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(any(), any())).thenReturn(mor);

            // Execute
            answer = vmwareResource.execute(cmd);
        }

        // Verify
        assertNotNull(answer);
        assertTrue(answer.getResult());
        assertEquals(0, answer.getCount());
        assertEquals(Collections.emptyList(), answer.getNames());
        assertEquals(Collections.emptyList(), answer.getPaths());
        assertEquals(Collections.emptyList(), answer.getAbsPaths());
        assertEquals(Collections.emptyList(), answer.getIsDirs());
        assertEquals(Collections.emptyList(), answer.getSizes());
        assertEquals(Collections.emptyList(), answer.getLastModified());
    }

    @Test
    public void testExecuteWithRootPath() throws Exception {
        // Setup
        ListDataStoreObjectsCommand cmd = new ListDataStoreObjectsCommand(destDataStoreTO, "/", 0, 10);
        HostDatastoreBrowserMO browserMo = mock(HostDatastoreBrowserMO.class);
        HostDatastoreBrowserSearchResults results = mock(HostDatastoreBrowserSearchResults.class);
        FileInfo fileInfo = mock(FileInfo.class);
        List<FileInfo> fileInfoList = new ArrayList<>();
        fileInfoList.add(fileInfo);
        Date date = new Date();

        doReturn(context).when(vmwareResource).getServiceContext();
        doReturn(hyperHost).when(vmwareResource).getHyperHost(context);
        when(browserMo.searchDatastore(anyString(), any(HostDatastoreBrowserSearchSpec.class))).thenReturn(results);
        when(results.getFile()).thenReturn(fileInfoList);
        when(fileInfo.getPath()).thenReturn("file.txt");
        when(fileInfo.getFileSize()).thenReturn(1L);
        when(fileInfo.getModification()).thenReturn(VmwareHelper.getXMLGregorianCalendar(date, 0));

        // Execute
        ListDataStoreObjectsAnswer answer;
        try (MockedStatic<HypervisorHostHelper> ignored = mockStatic(HypervisorHostHelper.class);
             MockedConstruction<DatastoreMO> ignored2 = mockConstruction(DatastoreMO.class, (mock, context1) -> {
                 when(mock.getName()).thenReturn("datastore");
                 when(mock.getHostDatastoreBrowserMO()).thenReturn(browserMo);
             })
        ) {
            when(HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(any(), any())).thenReturn(mor);

            // Execute
            answer = vmwareResource.execute(cmd);
        }

        // Verify
        assertNotNull(answer);
        assertTrue(answer.getResult());
        assertEquals(1, answer.getCount());
        assertEquals(Collections.singletonList("file.txt"), answer.getNames());
        assertEquals(Collections.singletonList("/file.txt"), answer.getPaths());
        assertEquals(Collections.singletonList("[datastore] /file.txt"), answer.getAbsPaths());
        assertEquals(Collections.singletonList(false), answer.getIsDirs());
        assertEquals(Collections.singletonList(1L), answer.getSizes());
        assertEquals(Collections.singletonList(date.getTime()), answer.getLastModified());
    }

    @Test
    public void testExecuteWithEmptyPath() throws Exception {
        // Setup
        ListDataStoreObjectsCommand cmd = new ListDataStoreObjectsCommand(destDataStoreTO, "", 0, 10);
        HostDatastoreBrowserMO browserMo = mock(HostDatastoreBrowserMO.class);
        HostDatastoreBrowserSearchResults results = mock(HostDatastoreBrowserSearchResults.class);
        FileInfo fileInfo = mock(FileInfo.class);
        List<FileInfo> fileInfoList = new ArrayList<>();
        fileInfoList.add(fileInfo);
        Date date = new Date();

        doReturn(context).when(vmwareResource).getServiceContext();
        doReturn(hyperHost).when(vmwareResource).getHyperHost(context);
        when(browserMo.searchDatastore(anyString(), any(HostDatastoreBrowserSearchSpec.class))).thenReturn(results);
        when(results.getFile()).thenReturn(fileInfoList);
        when(fileInfo.getPath()).thenReturn("file.txt");
        when(fileInfo.getFileSize()).thenReturn(1L);
        when(fileInfo.getModification()).thenReturn(VmwareHelper.getXMLGregorianCalendar(date, 0));

        ListDataStoreObjectsAnswer answer;
        try (MockedStatic<HypervisorHostHelper> ignored = mockStatic(HypervisorHostHelper.class);
             MockedConstruction<DatastoreMO> ignored2 = mockConstruction(DatastoreMO.class, (mock, context1) -> {
                 when(mock.getName()).thenReturn("datastore");
                 when(mock.getHostDatastoreBrowserMO()).thenReturn(browserMo);
             })
        ) {
            when(HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(any(), any())).thenReturn(mor);

            // Execute
            answer = vmwareResource.execute(cmd);

        }

        // Verify
        assertNotNull(answer);
        assertTrue(answer.getResult());
        assertEquals(1, answer.getCount());
        assertEquals(Collections.singletonList("file.txt"), answer.getNames());
        assertEquals(Collections.singletonList("/file.txt"), answer.getPaths());
        assertEquals(Collections.singletonList("[datastore] /file.txt"), answer.getAbsPaths());
        assertEquals(Collections.singletonList(false), answer.getIsDirs());
        assertEquals(Collections.singletonList(1L), answer.getSizes());
        assertEquals(Collections.singletonList(date.getTime()), answer.getLastModified());
    }

    @Test
    public void testAddVirtualTPMDevice() throws Exception {
        VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class);
        VirtualMachineTO vmSpec = Mockito.mock(VirtualMachineTO.class);
        VirtualMachineConfigSpec vmConfigSpec = Mockito.mock(VirtualMachineConfigSpec.class);
        Map<String, String> details = new HashMap<>();
        details.put(ApiConstants.BootType.UEFI.toString(), "SECURE");
        details.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, "true");
        when(vmSpec.getDetails()).thenReturn(details);
        when(vmMo.getAllDeviceList()).thenReturn(new ArrayList<>());
        List<VirtualDeviceConfigSpec> deviceChanges = Mockito.mock(List.class);
        when(vmConfigSpec.getDeviceChange()).thenReturn(deviceChanges);

        vmwareResource.configureVirtualTPM(vmMo, vmSpec, vmConfigSpec);
        Mockito.verify(vmwareResource, Mockito.times(1)).addVirtualTPMDevice(vmConfigSpec);
        Mockito.verify(deviceChanges, Mockito.times(1)).add(any(VirtualDeviceConfigSpec.class));
    }

    @Test
    public void testRemoveVirtualTPMDevice() throws Exception {
        VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class);
        VirtualMachineTO vmSpec = Mockito.mock(VirtualMachineTO.class);
        VirtualMachineConfigSpec vmConfigSpec = Mockito.mock(VirtualMachineConfigSpec.class);
        Map<String, String> details = new HashMap<>();
        details.put(ApiConstants.BootType.UEFI.toString(), "SECURE");
        details.put(VmDetailConstants.VIRTUAL_TPM_ENABLED, "false");
        when(vmSpec.getDetails()).thenReturn(details);
        VirtualTPM tpm = new VirtualTPM();
        when(vmMo.getAllDeviceList()).thenReturn(List.of(tpm));
        List<VirtualDeviceConfigSpec> deviceChanges = Mockito.mock(List.class);
        when(vmConfigSpec.getDeviceChange()).thenReturn(deviceChanges);

        vmwareResource.configureVirtualTPM(vmMo, vmSpec, vmConfigSpec);
        Mockito.verify(vmwareResource, Mockito.times(1)).removeVirtualTPMDevice(vmConfigSpec, tpm);
        Mockito.verify(deviceChanges, Mockito.times(1)).add(any(VirtualDeviceConfigSpec.class));
    }
}
