/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2013 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.ow2.jonas.addon.test;

import junit.framework.Assert;
import org.ow2.jonas.addon.deploy.api.deployable.IAddonDeployable;
import org.ow2.jonas.addon.deploy.api.deployable.IAddonMetadata;
import org.ow2.jonas.addon.deploy.api.deployer.IAddonDeployer;
import org.ow2.jonas.addon.deploy.api.util.IAddonLogEntry;
import org.ow2.jonas.addon.deploy.impl.deployable.SortableDeployable;
import org.ow2.jonas.addon.deploy.impl.deployer.AddonDeployerImpl;
import org.ow2.jonas.addon.deploy.impl.util.AddonUtil;
import org.ow2.util.ee.deploy.api.deployable.IDeployable;
import org.ow2.util.ee.deploy.api.deployer.DeployerException;
import org.ow2.util.ee.deploy.api.report.IDeploymentReport;
import org.ow2.util.ee.deploy.impl.deployer.DeployerManager;
import org.ow2.util.ee.deploy.impl.report.DeploymentReport;
import org.ow2.util.file.FileUtils;
import org.testng.annotations.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyList;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * Deployment system test case
 * @author Jeremy Cazaux
 */
public class DeploymentSystemTestCase extends BaseTestCase {

    public static final File ADDON = new File(ADDON_DIR, "deployment-system");
    public static final File ADDON_RENAMED = new File(ADDON_DIR, "deployment-system-2");
    public static final String ADDON_NAME = "deployment-system-test";
    public static final String ADDON_NEW_NAME = "new-name";
    public static final String ADDON_AUTOSTART_NEW_VALUE = "false";
    public static final String SAMPLE_CONF_FILENAME = "sample.conf";

    public static final File AUTOSTART_ADDON = new File(ADDON_DIR, "autostart");
    public static final String AUTOSTART_ADDON_NAME = "autostart-test";

    @Test
    public void testThatJOnASConfigurationHasBeenRetrievedWhenAnAddonHasBeenRemovedWhenTheServerWasNotRunning() throws Exception {
        //deploy a addon
        this.deployer.install(getDeployable(ADDON), false);
        this.deployer.start(ADDON_NAME);

        //check the deployer log size
        Assert.assertEquals(this.deployerLog.getEntries().size(), 1);

        //check the configuration directory and the configuration files
        File addonConfDir = new File(AddonUtil.getAddonDirectoryPath(ADDON_NAME));
        Assert.assertTrue(addonConfDir.exists());
        Assert.assertTrue(addonConfDir.isDirectory());
        File confFile = new File(addonConfDir, SAMPLE_CONF_FILENAME);
        Assert.assertTrue(confFile.exists());
        Assert.assertFalse(confFile.isDirectory());
        File metadataFile = new File(addonConfDir, IAddonDeployable.METADATA_FILENAME);
        Assert.assertTrue(metadataFile.exists());
        Assert.assertFalse(metadataFile.isDirectory());

        //check the list of deployed addons
        Assert.assertEquals(this.deployer.getAddons().size(), 1);

        //rename the original deployable (file has been removed from the deployment system)
        ADDON.renameTo(ADDON_RENAMED);

        //restart the addon system
        this.deployer = getAddonDeployerInstance();

        //check the deployer log size
        Assert.assertEquals(this.deployerLog.getEntries().size(), 0);

        //check the configuration directory
        Assert.assertFalse(addonConfDir.exists());

        //check the list of deployed addons
        Assert.assertTrue(this.deployer.getAddons().isEmpty());
    }

    @Test(dependsOnMethods = "testThatJOnASConfigurationHasBeenRetrievedWhenAnAddonHasBeenRemovedWhenTheServerWasNotRunning")
    public void testThatAnUnpackedDeployableWhichHasBeenRemovedWillBeUnpackAgain() throws Exception {
        //deploy the addon
        IAddonDeployable addon = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ADDON_RENAMED), false));
        this.deployer.start(ADDON_NAME);

        //removal of the unpacked deployable
        File unpackedDeployable = this.deployerLog.getEntry(ADDON_RENAMED.getAbsoluteFile()).getCopy();
        Assert.assertTrue(unpackedDeployable.exists());
        FileUtils.delete(addon.getShortName());

        //restart the addon deployer
        this.deployer = getAddonDeployerInstance();
        addon = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ADDON_RENAMED.getAbsoluteFile()), false));
        this.deployer.start(ADDON_NAME);

        //check that the deployable has been unpacked again
        unpackedDeployable = this.deployerLog.getEntry(ADDON_RENAMED.getAbsoluteFile()).getCopy();
        Assert.assertTrue(unpackedDeployable.exists());
    }

    @Test(dependsOnMethods = "testThatAnUnpackedDeployableWhichHasBeenRemovedWillBeUnpackAgain")
    public void testThatAnAddonWhichHasBeenUpdateWhenTheServerWasNotRunningWillBeRedeployed() throws Exception {
        //deploy the addon
        IAddonDeployable addon = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ADDON_RENAMED), false));
        this.deployer.start(ADDON_NAME);

        //check some metadata properties
        Assert.assertEquals(addon.getMetadata().getName(), ADDON_NAME);
        Assert.assertTrue(addon.getMetadata().getAutostart());

        //update the metadata file
        File metadataFile = new File(ADDON_RENAMED, IAddonDeployable.JONAS_ADDON_METADATA);

        //update the XML document
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.parse(metadataFile);
        Element rootElement = document.getDocumentElement();
        Node node = rootElement.getElementsByTagName(IAddonMetadata.NAME_ELEMENT).item(0);
        Assert.assertEquals(node.getNodeName(), IAddonMetadata.NAME_ELEMENT);
        node.getFirstChild().setNodeValue(ADDON_NEW_NAME);
        node = rootElement.getElementsByTagName(IAddonMetadata.AUTOSTART_ELEMENT).item(0);
        Assert.assertEquals(node.getNodeName(), IAddonMetadata.AUTOSTART_ELEMENT);
        node.getFirstChild().setNodeValue(ADDON_AUTOSTART_NEW_VALUE);

        //write the new content into xml file
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        DOMSource source = new DOMSource(document);
        StreamResult result = new StreamResult(metadataFile);
        transformer.transform(source, result);

        //restart the addon deployer
        this.deployer = getAddonDeployerInstance();
        addon = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ADDON_RENAMED), false));

        //start the addon with the new name
        this.deployer.start(addon.getMetadata().getName());

        //check addon metadata in memory
        Assert.assertEquals(ADDON_NEW_NAME, addon.getMetadata().getName());
        Assert.assertEquals(new Boolean(ADDON_AUTOSTART_NEW_VALUE), addon.getMetadata().getAutostart());

        //check addon metadata conf file
        metadataFile = new File(new File(AddonUtil.getAddonDirectoryPath(ADDON_NEW_NAME)), IAddonDeployable.METADATA_FILENAME);
        Assert.assertTrue(metadataFile.exists());
        IAddonMetadata metadata = AddonUtil.getAddonMetadata(metadataFile, null);
        Assert.assertEquals(ADDON_NEW_NAME, metadata.getName());
        Assert.assertEquals(new Boolean(ADDON_AUTOSTART_NEW_VALUE), metadata.getAutostart());

        //check that the old configuration directory doesn't exist anymore
        Assert.assertFalse(new File(AddonUtil.getAddonDirectoryPath(ADDON_NAME)).exists());

        //check log entry
        Assert.assertEquals(1, this.deployerLog.getEntries().size());
        IAddonLogEntry logEntry = IAddonLogEntry.class.cast(this.deployerLog.getEntries().get(0));
        Assert.assertEquals(ADDON_NEW_NAME, logEntry.getName());
    }

    @Test(expectedExceptions = DeployerException.class)
    public void testThatTheStateOfAnAddonWithAFailureOnTheStartOperationIsEqualsToResolved() throws Exception {
        //install an addon
        IAddonDeployable addon = IAddonDeployable.class.cast(this.deployer.install(getDeployable(BasicTestCase.SAMPLE_ADDON), false));

        //add a mock on a embedded addon
        IAddonDeployable embeddedAddon = mock(IAddonDeployable.class);
        addon.addDeployable(new SortableDeployable(embeddedAddon, AddonUtil.ADDON_DEPLOYABLE_PRIORITY));

        //build the list of deployment report
        DeploymentReport deploymentReport = new DeploymentReport();
        deploymentReport.setDeployable(embeddedAddon);
        deploymentReport.setDeploymentOk(false);
        List<IDeploymentReport> deploymentReports = new ArrayList<IDeploymentReport>();
        deploymentReports.add(deploymentReport);

        //the addon has one deployable which cannot be deployed.
        when(this.deployerManager.deploy(anyList())).thenReturn(deploymentReports);

        try {
            //start the addon
            this.deployer.start(BasicTestCase.SAMPLE_NAME);
        } finally {
            //Check that the state of the addon is not ACTIVE but RESOLVE
            Assert.assertEquals(IAddonDeployable.RESOLVED, addon.getState());
            IAddonLogEntry logEntry = IAddonLogEntry.class.cast(this.deployerLog.getEntry(BasicTestCase.SAMPLE_ADDON.getAbsoluteFile()));
            Assert.assertEquals(IAddonDeployable.RESOLVED, logEntry.getState());
        }
    }

    @Test(expectedExceptions = DeployerException.class)
    public void testThatTheStateOfAnAddonWithAFailureOnTheStopOperationIsEqualsToResolved() throws Exception {
        //install an addon
        IAddonDeployable addon = IAddonDeployable.class.cast(this.deployer.install(getDeployable(BasicTestCase.SAMPLE_ADDON), false));

        //start the addon
        this.deployer.start(BasicTestCase.SAMPLE_NAME);

        //add a mock on a embedded addon
        IAddonDeployable embeddedAddon = mock(IAddonDeployable.class);
        addon.addDeployable(new SortableDeployable(embeddedAddon, AddonUtil.ADDON_DEPLOYABLE_PRIORITY));

        //build the list of deployment report
        DeploymentReport deploymentReport = new DeploymentReport();
        deploymentReport.setDeployable(embeddedAddon);
        deploymentReport.setDeploymentOk(false);
        List<IDeploymentReport> deploymentReports = new ArrayList<IDeploymentReport>();
        deploymentReports.add(deploymentReport);

        //the addon has onne deployable which cannot be undeployed.
        when(this.deployerManager.undeploy(anyList())).thenReturn(deploymentReports);

        try {
            //stop the addon
            this.deployer.stop(BasicTestCase.SAMPLE_NAME);
        } finally {
            //Check that the state of the addon is not ACTIVE but RESOLVE
            Assert.assertEquals(IAddonDeployable.RESOLVED, addon.getState());
            IAddonLogEntry logEntry = IAddonLogEntry.class.cast(this.deployerLog.getEntry(BasicTestCase.SAMPLE_ADDON.getAbsoluteFile()));
            Assert.assertEquals(IAddonDeployable.RESOLVED, logEntry.getState());
        }
    }

    @Test(expectedExceptions =  DeployerException.class)
    public void testThatTheStateOfAnAddonWithAFailureOnTheUndeployOperationIsEqualsToUninstalled() throws Exception {
        //install an addon
        IAddonDeployable addon = IAddonDeployable.class.cast(this.deployer.install(getDeployable(BasicTestCase.SAMPLE_ADDON), false));

        //start the addon
        this.deployer.start(BasicTestCase.SAMPLE_NAME);

        //add a mock on a embedded addon
        IAddonDeployable embeddedAddon = mock(IAddonDeployable.class);
        addon.addDeployable(new SortableDeployable(embeddedAddon, AddonUtil.ADDON_DEPLOYABLE_PRIORITY));

        //build the list of deployment report
        DeploymentReport deploymentReport = new DeploymentReport();
        deploymentReport.setDeployable(embeddedAddon);
        deploymentReport.setDeploymentOk(false);
        List<IDeploymentReport> deploymentReports = new ArrayList<IDeploymentReport>();
        deploymentReports.add(deploymentReport);

        //the addon has onne deployable which cannot be undeployed.
        when(this.deployerManager.undeploy(anyList())).thenReturn(deploymentReports);

        try {
            //undeploy the addon
            this.deployer.undeploy(getDeployable(BasicTestCase.SAMPLE_ADDON));
        } finally {
            //Check that the state of the addon is not ACTIVE but RESOLVE
            Assert.assertEquals(IAddonDeployable.UNINSTALLED, addon.getState());
            Assert.assertEquals(this.deployerLog.getEntries().size(), 0);
        }
    }

    @Test
    public void testThatTheDeploymentOrderIsOkWhenUsingTheDeploymentOfAListOfAddon() throws Exception {

        IDeployable<IAddonDeployable> addon1 = getDeployable(ResolverTestCase.ADDON_1);
        IDeployable<IAddonDeployable> addon2 = getDeployable(ResolverTestCase.ADDON_2);
        IDeployable<IAddonDeployable> addon3 = getDeployable(ResolverTestCase.ADDON_3);
        IDeployable<IAddonDeployable> addon8 = getDeployable(ResolverTestCase.ADDON_8);
        IDeployable<IAddonDeployable> addon18 = getDeployable(ResolverTestCase.ADDON_18);

        List<IDeployable<IAddonDeployable>> deployables = new ArrayList<IDeployable<IAddonDeployable>>();
        deployables.add(addon1);
        deployables.add(addon2);
        deployables.add(addon3);

        AddonDeployerImpl deployer = spy(AddonDeployerImpl.class.cast(this.deployer));
        deployer.deploy(deployables);

        verify(deployer, times(3)).install(any(IAddonDeployable.class), eq(false));
        verify(deployer, times(1)).install(addon1, false);
        verify(deployer, times(1)).install(addon2, false);
        verify(deployer, times(1)).install(addon3, false);
        verify(deployer, times(3)).start(anyString());
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_3_NAME);

        deployer.undeploy(deployables);

        verify(deployer, times(3)).stop(anyString());
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_3_NAME);
        verify(deployer, times(3)).uninstall(anyString(), eq(true));
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_1_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_2_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_3_NAME, true);

        deployables.clear();
        deployables.add(addon1);
        deployables.add(addon3);
        deployables.add(addon2);

        deployer = spy(AddonDeployerImpl.class.cast(this.deployer));
        deployer.deploy(deployables);

        verify(deployer, times(3)).install(any(IAddonDeployable.class), eq(false));
        verify(deployer, times(1)).install(addon1, false);
        verify(deployer, times(1)).install(addon2, false);
        verify(deployer, times(1)).install(addon3, false);
        verify(deployer, times(3)).start(anyString());
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_3_NAME);

        deployer.undeploy(deployables);

        verify(deployer, times(3)).stop(anyString());
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_3_NAME);
        verify(deployer, times(3)).uninstall(anyString(), eq(true));
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_1_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_2_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_3_NAME, true);

        deployables.clear();
        deployables.add(addon2);
        deployables.add(addon1);
        deployables.add(addon3);

        deployer = spy(AddonDeployerImpl.class.cast(this.deployer));
        deployer.deploy(deployables);

        verify(deployer, times(3)).install(any(IAddonDeployable.class), eq(false));
        verify(deployer, times(1)).install(addon1, false);
        verify(deployer, times(1)).install(addon2, false);
        verify(deployer, times(1)).install(addon3, false);
        verify(deployer, times(3)).start(anyString());
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_3_NAME);

        deployer.undeploy(deployables);

        verify(deployer, times(3)).stop(anyString());
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_3_NAME);
        verify(deployer, times(3)).uninstall(anyString(), eq(true));
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_1_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_2_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_3_NAME, true);

        deployables.clear();
        deployables.add(addon2);
        deployables.add(addon3);
        deployables.add(addon1);

        deployer = spy(AddonDeployerImpl.class.cast(this.deployer));
        deployer.deploy(deployables);

        verify(deployer, times(3)).install(any(IAddonDeployable.class), eq(false));
        verify(deployer, times(1)).install(addon1, false);
        verify(deployer, times(1)).install(addon2, false);
        verify(deployer, times(1)).install(addon3, false);
        verify(deployer, times(3)).start(anyString());
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_3_NAME);

        deployer.undeploy(deployables);

        verify(deployer, times(3)).stop(anyString());
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_3_NAME);
        verify(deployer, times(3)).uninstall(anyString(), eq(true));
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_1_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_2_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_3_NAME, true);

        deployables.clear();
        deployables.add(addon3);
        deployables.add(addon1);
        deployables.add(addon2);

        deployer = spy(AddonDeployerImpl.class.cast(this.deployer));
        deployer.deploy(deployables);

        verify(deployer, times(3)).install(any(IAddonDeployable.class), eq(false));
        verify(deployer, times(1)).install(addon1, false);
        verify(deployer, times(1)).install(addon2, false);
        verify(deployer, times(1)).install(addon3, false);
        verify(deployer, times(3)).start(anyString());
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_3_NAME);

        deployer.undeploy(deployables);

        verify(deployer, times(3)).stop(anyString());
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_3_NAME);
        verify(deployer, times(3)).uninstall(anyString(), eq(true));
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_1_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_2_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_3_NAME, true);

        deployables.clear();
        deployables.add(addon3);
        deployables.add(addon2);
        deployables.add(addon1);

        deployer = spy(AddonDeployerImpl.class.cast(this.deployer));
        deployer.deploy(deployables);

        verify(deployer, times(3)).install(any(IAddonDeployable.class), eq(false));
        verify(deployer, times(1)).install(addon1, false);
        verify(deployer, times(1)).install(addon2, false);
        verify(deployer, times(1)).install(addon3, false);
        verify(deployer, times(3)).start(anyString());
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_3_NAME);

        deployer.undeploy(deployables);

        verify(deployer, times(3)).stop(anyString());
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_1_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_2_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_3_NAME);
        verify(deployer, times(3)).uninstall(anyString(), eq(true));
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_1_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_2_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_3_NAME, true);

        deployables.clear();
        deployables.add(addon18);
        deployables.add(addon8);

        deployer = spy(AddonDeployerImpl.class.cast(this.deployer));
        deployer.deploy(deployables);

        verify(deployer, times(2)).install(any(IAddonDeployable.class), eq(false));
        verify(deployer, times(1)).install(addon8, false);
        verify(deployer, times(1)).install(addon18, false);
        verify(deployer, times(2)).start(anyString());
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_8_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_18_NAME);

        deployer.undeploy(deployables);

        verify(deployer, times(2)).stop(anyString());
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_8_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_18_NAME);
        verify(deployer, times(2)).uninstall(anyString(), eq(true));
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_8_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_18_NAME, true);

        deployables.clear();
        deployables.add(addon8);
        deployables.add(addon18);

        deployer = spy(AddonDeployerImpl.class.cast(this.deployer));
        deployer.deploy(deployables);

        verify(deployer, times(2)).install(any(IAddonDeployable.class), eq(false));
        verify(deployer, times(1)).install(addon8, false);
        verify(deployer, times(1)).install(addon18, false);
        verify(deployer, times(2)).start(anyString());
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_8_NAME);
        verify(deployer, times(1)).start(ResolverTestCase.ADDON_18_NAME);

        deployer.undeploy(deployables);

        verify(deployer, times(2)).stop(anyString());
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_8_NAME);
        verify(deployer, times(1)).stop(ResolverTestCase.ADDON_18_NAME);
        verify(deployer, times(2)).uninstall(anyString(), eq(true));
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_8_NAME, true);
        verify(deployer, times(1)).uninstall(ResolverTestCase.ADDON_18_NAME, true);
    }

    @Test
    public void testThatAnAddonWithAAutostartPropertySetToFalseWillNotBeStarted() throws Exception {
        //spy the addon deployer
        IAddonDeployer deployer = spy(this.deployer);

        //deploy the addon
        deployer.deploy(getDeployable(AUTOSTART_ADDON));

        //check that the addon has not been started (its state should be equals to INSTALLED)
        verify(deployer, never()).start(AUTOSTART_ADDON_NAME);

        IAddonLogEntry logEntry = IAddonLogEntry.class.cast(this.deployerLog.getEntries().get(0));
        Assert.assertEquals(logEntry.getName(), AUTOSTART_ADDON_NAME);
        Assert.assertEquals(logEntry.getOriginal(), AUTOSTART_ADDON.getAbsoluteFile());
        Assert.assertEquals(IAddonDeployable.INSTALLED, logEntry.getState());
    }

    @Test
    public void testThatTheListOfInstalledAddonIsOrderedAndTestThatTheirAddonIdAndExportedCapabilitiesAreConsistent() throws Exception {
        this.deployerManager = new DeployerManager();
        this.deployer = getAddonDeployerInstance();
        this.deployerManager.register(this.deployer);

        //install all addons
        IAddonDeployable addon1 = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ResolverTestCase.ADDON_1), false));
        IAddonDeployable addon2 = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ResolverTestCase.ADDON_2), false));
        IAddonDeployable addon3 = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ResolverTestCase.ADDON_3), false));
        IAddonDeployable addon4 = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ResolverTestCase.ADDON_4), false));
        IAddonDeployable addon5 = IAddonDeployable.class.cast(this.deployer.install(getDeployable(BasicTestCase.SAMPLE_ADDON), false));
        IAddonDeployable addon6 = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ResolverTestCase.ADDON_6), false));

        //check addon id v1
        Assert.assertEquals(6, this.deployer.getAddons().size());
        Assert.assertEquals(addon1, this.deployer.getAddon(1));
        Assert.assertEquals(addon2, this.deployer.getAddon(2));
        Assert.assertEquals(addon3, this.deployer.getAddon(3));
        Assert.assertEquals(addon4, this.deployer.getAddon(4));
        Assert.assertEquals(addon5, this.deployer.getAddon(5));
        Assert.assertEquals(addon6, this.deployer.getAddon(6));

        //check addon id v2
        Assert.assertEquals(6, this.deployer.getAddons().size());
        Assert.assertEquals(new Long(1), this.deployer.getAddons().get(0).getAddonId());
        Assert.assertEquals(new Long(2), this.deployer.getAddons().get(1).getAddonId());
        Assert.assertEquals(new Long(3), this.deployer.getAddons().get(2).getAddonId());
        Assert.assertEquals(new Long(4), this.deployer.getAddons().get(3).getAddonId());
        Assert.assertEquals(new Long(5), this.deployer.getAddons().get(4).getAddonId());
        Assert.assertEquals(new Long(6), this.deployer.getAddons().get(5).getAddonId());

        //check addon list order
        Assert.assertEquals(addon1, this.deployer.getAddons().get(0));
        Assert.assertEquals(addon2, this.deployer.getAddons().get(1));
        Assert.assertEquals(addon3, this.deployer.getAddons().get(2));
        Assert.assertEquals(addon4, this.deployer.getAddons().get(3));
        Assert.assertEquals(addon5, this.deployer.getAddons().get(4));
        Assert.assertEquals(addon6, this.deployer.getAddons().get(5));

        //check exported capabilities
        Assert.assertTrue(addon1.getCapabilities().isEmpty());
        Assert.assertTrue(addon2.getCapabilities().isEmpty());
        Assert.assertTrue(addon3.getCapabilities().isEmpty());
        Assert.assertTrue(addon4.getCapabilities().isEmpty());
        Assert.assertTrue(addon5.getCapabilities().isEmpty());
        Assert.assertTrue(addon6.getCapabilities().isEmpty());


        //start all addons...Addon6 will install and start addon 7
        this.deployer.start(ResolverTestCase.ADDON_1_NAME);
        this.deployer.start(ResolverTestCase.ADDON_2_NAME);
        this.deployer.start(ResolverTestCase.ADDON_3_NAME);
        this.deployer.start(ResolverTestCase.ADDON_4_NAME);
        this.deployer.start(BasicTestCase.SAMPLE_NAME);
        this.deployer.start(ResolverTestCase.ADDON_6_NAME);

        //check addon list order
        Assert.assertEquals(7, this.deployer.getAddons().size());
        Assert.assertEquals(addon1, this.deployer.getAddon(1));
        Assert.assertEquals(addon2, this.deployer.getAddon(2));
        Assert.assertEquals(addon3, this.deployer.getAddon(3));
        Assert.assertEquals(addon4, this.deployer.getAddon(4));
        Assert.assertEquals(addon5, this.deployer.getAddon(5));
        Assert.assertEquals(addon6, this.deployer.getAddon(6));
        Assert.assertNotNull(this.deployer.getAddon(7));

        //get addon 7
        IAddonDeployable addon7 = this.deployer.getAddon(7);
        Assert.assertEquals(ResolverTestCase.ADDON_7_NAME, addon7.getMetadata().getName());
        Assert.assertEquals(new Long(7), addon7.getAddonId());

        //check addon id v1
        Assert.assertEquals(7, this.deployer.getAddons().size());
        Assert.assertEquals(addon1, this.deployer.getAddons().get(0));
        Assert.assertEquals(addon2, this.deployer.getAddons().get(1));
        Assert.assertEquals(addon3, this.deployer.getAddons().get(2));
        Assert.assertEquals(addon4, this.deployer.getAddons().get(3));
        Assert.assertEquals(addon5, this.deployer.getAddons().get(4));
        Assert.assertEquals(addon6, this.deployer.getAddons().get(5));
        Assert.assertEquals(addon7, this.deployer.getAddons().get(6));

        //check addon id v2
        Assert.assertEquals(new Long(1), this.deployer.getAddons().get(0).getAddonId());
        Assert.assertEquals(new Long(2), this.deployer.getAddons().get(1).getAddonId());
        Assert.assertEquals(new Long(3), this.deployer.getAddons().get(2).getAddonId());
        Assert.assertEquals(new Long(4), this.deployer.getAddons().get(3).getAddonId());
        Assert.assertEquals(new Long(5), this.deployer.getAddons().get(4).getAddonId());
        Assert.assertEquals(new Long(6), this.deployer.getAddons().get(5).getAddonId());
        Assert.assertEquals(new Long(7), this.deployer.getAddons().get(6).getAddonId());

        //check exported capabilities
        Assert.assertEquals(1, addon1.getCapabilities().size());
        Assert.assertEquals(1, addon2.getCapabilities().size());
        Assert.assertEquals(0, addon3.getCapabilities().size());
        Assert.assertEquals(1, addon4.getCapabilities().size());
        Assert.assertEquals(1, addon5.getCapabilities().size());
        Assert.assertEquals(1, addon6.getCapabilities().size());
        Assert.assertEquals(1, addon7.getCapabilities().size());
        Assert.assertEquals(ResolverTestCase.ADDON_1_CAPABILITY, addon1.getCapabilities().get(0));
        Assert.assertEquals(ResolverTestCase.ADDON_2_CAPABILITY, addon2.getCapabilities().get(0));
        Assert.assertEquals(ResolverTestCase.ADDON_4_CAPABILITY, addon4.getCapabilities().get(0));
        Assert.assertEquals(BasicTestCase.SAMPLE_CAPABILITY, addon5.getCapabilities().get(0));
        Assert.assertEquals(ResolverTestCase.ADDON_6_CAPABILITY, addon6.getCapabilities().get(0));
        Assert.assertEquals(ResolverTestCase.ADDON_7_CAPABILITY, addon7.getCapabilities().get(0));

        //stop & uninstall addon 5
        this.deployer.stop(BasicTestCase.SAMPLE_NAME);
        this.deployer.uninstall(BasicTestCase.SAMPLE_NAME);

        //check addon id consistency v1
        Assert.assertEquals(6, this.deployer.getAddons().size());
        Assert.assertEquals(addon1, this.deployer.getAddons().get(0));
        Assert.assertEquals(addon2, this.deployer.getAddons().get(1));
        Assert.assertEquals(addon3, this.deployer.getAddons().get(2));
        Assert.assertEquals(addon4, this.deployer.getAddons().get(3));
        Assert.assertEquals(addon6, this.deployer.getAddons().get(4));
        Assert.assertEquals(addon7, this.deployer.getAddons().get(5));

        //check addon id consistency v2
        Assert.assertEquals(new Long(1), this.deployer.getAddons().get(0).getAddonId());
        Assert.assertEquals(new Long(2), this.deployer.getAddons().get(1).getAddonId());
        Assert.assertEquals(new Long(3), this.deployer.getAddons().get(2).getAddonId());
        Assert.assertEquals(new Long(4), this.deployer.getAddons().get(3).getAddonId());
        Assert.assertEquals(new Long(6), this.deployer.getAddons().get(4).getAddonId());
        Assert.assertEquals(new Long(7), this.deployer.getAddons().get(5).getAddonId());

        //check addon id consistency v3
        Assert.assertEquals(new Long(1), addon1.getAddonId());
        Assert.assertEquals(new Long(2), addon2.getAddonId());
        Assert.assertEquals(new Long(3), addon3.getAddonId());
        Assert.assertEquals(new Long(4), addon4.getAddonId());
        Assert.assertEquals(new Long(6), addon6.getAddonId());
        Assert.assertEquals(new Long(7), addon7.getAddonId());

        //check exported capabilities
        Assert.assertEquals(1, addon1.getCapabilities().size());
        Assert.assertEquals(1, addon2.getCapabilities().size());
        Assert.assertEquals(0, addon3.getCapabilities().size());
        Assert.assertEquals(1, addon4.getCapabilities().size());
        Assert.assertEquals(1, addon6.getCapabilities().size());
        Assert.assertEquals(1, addon7.getCapabilities().size());
        Assert.assertEquals(ResolverTestCase.ADDON_1_CAPABILITY, addon1.getCapabilities().get(0));
        Assert.assertEquals(ResolverTestCase.ADDON_2_CAPABILITY, addon2.getCapabilities().get(0));
        Assert.assertEquals(ResolverTestCase.ADDON_4_CAPABILITY, addon4.getCapabilities().get(0));
        Assert.assertEquals(ResolverTestCase.ADDON_6_CAPABILITY, addon6.getCapabilities().get(0));
        Assert.assertEquals(ResolverTestCase.ADDON_7_CAPABILITY, addon7.getCapabilities().get(0));


        //install another addon
        IAddonDeployable addon8 = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ResolverTestCase.ADDON_5), false));

        //check addon id
        Assert.assertEquals(new Long(8), addon8.getAddonId());
        Assert.assertEquals(addon8, this.deployer.getAddons().get(6));
        Assert.assertEquals(new Long(8), this.deployer.getAddons().get(6).getAddonId());
        Assert.assertEquals(7, this.deployer.getAddons().size());
    }

    @Test(expectedExceptions = DeployerException.class)
    public void testThatWeCannotStopAnEmbeddedAddonWithoutStopItsParent() throws Exception {
        //get the parent addon
        IAddonDeployable addon8 = getDeployable(ResolverTestCase.ADDON_8);

        //install and start a parent addon
        this.deployer.install(addon8, false);
        this.deployer.start(ResolverTestCase.ADDON_8_NAME);

        //try to stop an embedded addon (it should fail)
        this.deployer.stop(ResolverTestCase.ADDON_9_NAME);
    }

    @Test(expectedExceptions = DeployerException.class)
    public void testThatWeCannotStopAnEmbeddedAddonWithoutStopItsParent2() throws Exception {
        //get the parent addon
        IAddonDeployable addon8 = getDeployable(ResolverTestCase.ADDON_8);

        //install and start a parent addon
        this.deployer.install(addon8, false);
        this.deployer.start(ResolverTestCase.ADDON_8_NAME);

        //try to stop an embedded addon of an embedded addon of the parent
        this.deployer.stop(ResolverTestCase.ADDON_11_NAME);
    }

    @Test
    public void testThatAddonInstalledFromAShelbieCommandWillBeInstallAgainWhenTheServerWillRestarted() throws Exception {
        //get addons
        IAddonDeployable deployable1 = getDeployable(ResolverTestCase.ADDON_1);
        IAddonDeployable deployable2 = getDeployable(BasicTestCase.SAMPLE_ADDON);

        //install addons from the command
        IAddonDeployable addon1 = IAddonDeployable.class.cast(this.deployer.install(deployable1, true));
        IAddonDeployable addon2 = IAddonDeployable.class.cast(this.deployer.install(deployable2, true));

        //start the addon 1
        this.deployer.start(ResolverTestCase.ADDON_1_NAME);

        //check state
        Assert.assertEquals(IAddonDeployable.ACTIVE, addon1.getState());
        Assert.assertEquals(IAddonDeployable.RESOLVED, addon2.getState());

        //restart the server
        this.deployer = getAddonDeployerInstance();

        //check that addon have been reinstalled
        List<IAddonDeployable> addons = this.deployer.getAddons();
        Assert.assertEquals(2, addons.size());
        for (IAddonDeployable addon: addons) {
            if (ResolverTestCase.ADDON_1_NAME.equals(addon.getMetadata().getName())) {
                Assert.assertEquals(IAddonDeployable.ACTIVE, addon.getState());
            } else if (BasicTestCase.SAMPLE_NAME.equals(addon.getMetadata().getName())) {
                if (IAddonDeployable.INSTALLED == addon.getState() || IAddonDeployable.RESOLVED == addon.getState()) {
                    Assert.assertTrue(true);
                } else {
                    Assert.assertTrue(false);
                }
            } else {
                Assert.assertTrue(false);
            }
        }
    }

    @Test
    public void testThatAddonInstalledFromAShelbieCommandWillBeStartedAgainInTheRightOrderWhenTheServerWillRestarted() throws Exception {
        //install all addons from a command
        IAddonDeployable addon2 = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ResolverTestCase.ADDON_2), true));
        IAddonDeployable addon1 = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ResolverTestCase.ADDON_1), true));
        IAddonDeployable addon3 = IAddonDeployable.class.cast(this.deployer.install(getDeployable(ResolverTestCase.ADDON_3), true));

        //start them
        this.deployer.start(ResolverTestCase.ADDON_1_NAME);
        this.deployer.start(ResolverTestCase.ADDON_2_NAME);
        this.deployer.start(ResolverTestCase.ADDON_3_NAME);

        //check state
        Assert.assertEquals(IAddonDeployable.ACTIVE, addon1.getState());
        Assert.assertEquals(IAddonDeployable.ACTIVE, addon2.getState());
        Assert.assertEquals(IAddonDeployable.ACTIVE, addon3.getState());

        //restart the server
        this.deployer = getAddonDeployerInstance();

        //check that addon have been reinstalled and started in the right order
        List<IAddonDeployable> addons = this.deployer.getAddons();
        Assert.assertEquals(3, addons.size());
        Assert.assertEquals(IAddonDeployable.ACTIVE, addons.get(0).getState());
        Assert.assertEquals(IAddonDeployable.ACTIVE, addons.get(0).getState());
        Assert.assertEquals(IAddonDeployable.ACTIVE, addons.get(0).getState());
    }
}
