org.xwiki.extension.jar.JarExtensionHandlerTest.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.extension.jar.JarExtensionHandlerTest.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.extension.jar;

import java.io.File;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.apache.commons.lang3.reflect.MethodUtils;
import org.junit.Assert;
import org.junit.Test;
import org.xwiki.classloader.ClassLoaderManager;
import org.xwiki.component.internal.StackingComponentEventManager;
import org.xwiki.component.internal.multi.ComponentManagerManager;
import org.xwiki.component.manager.ComponentLookupException;
import org.xwiki.component.manager.ComponentManager;
import org.xwiki.component.util.DefaultParameterizedType;
import org.xwiki.component.util.ReflectionUtils;
import org.xwiki.extension.ExtensionId;
import org.xwiki.extension.InstallException;
import org.xwiki.extension.InstalledExtension;
import org.xwiki.extension.LocalExtension;
import org.xwiki.extension.repository.internal.installed.DefaultInstalledExtension;
import org.xwiki.extension.test.AbstractExtensionHandlerTest;
import org.xwiki.logging.LogLevel;
import org.xwiki.logging.LoggerManager;
import org.xwiki.observation.ObservationManager;
import org.xwiki.test.annotation.AfterComponent;

import packagefile.jarextension.DefaultTestComponent;
import packagefile.jarextension.TestComponent;
import packagefile.jarextensionwithdeps.DefaultTestComponentWithDeps;
import packagefile.jarextensionwithdeps.TestComponentWithDeps;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

public class JarExtensionHandlerTest extends AbstractExtensionHandlerTest {
    private ComponentManagerManager componentManagerManager;

    private ClassLoader testApplicationClassloader;

    private ClassLoaderManager jarExtensionClassLoader;

    private static final String NAMESPACE = "namespace";

    @AfterComponent
    public void registerComponents() throws Exception {
        // Override the system ClassLoader to isolate class loading of extensions from the current ClassLoader
        // (which already contains the extensions)
        this.mocker.registerComponent(TestJarExtensionClassLoaderManager.class);

        // Make sure to fully enable ObservationManager to test EventListener live registration
        StackingComponentEventManager componentEventManager = new StackingComponentEventManager();
        componentEventManager.shouldStack(false);
        this.mocker.setComponentEventManager(componentEventManager);

        // Ignore warning log during setup
        ((LoggerManager) this.mocker.getInstance(LoggerManager.class)).pushLogListener(null);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();

        this.testApplicationClassloader = Thread.currentThread().getContextClassLoader();

        // lookup
        this.componentManagerManager = this.mocker.getInstance(ComponentManagerManager.class);
        this.jarExtensionClassLoader = this.mocker.getInstance(ClassLoaderManager.class);

        // Make sure to fully enable ObservationManager to test EventListener live registration
        StackingComponentEventManager componentEventManager = (StackingComponentEventManager) this.mocker
                .getComponentEventManager();
        ObservationManager manager = this.mocker.getInstance(ObservationManager.class);
        componentEventManager.setObservationManager(manager);

        ((LoggerManager) this.mocker.getInstance(LoggerManager.class)).popLogListener();
    }

    private void assertNotEquals(Type type1, Type type2) {
        if (type1.equals(type2)) {
            Assert.fail("expected not equals");
        }
    }

    /**
     * @param namespace the namespace to be used
     * @return the extension class loader for the current namespace
     */
    private ClassLoader getExtensionClassloader(String namespace) {
        ClassLoader extensionLoader = this.jarExtensionClassLoader.getURLClassLoader(namespace, false);
        if (extensionLoader == null) {
            extensionLoader = ((TestJarExtensionClassLoaderManager) this.jarExtensionClassLoader).testExtensionClassLoader;
        }

        return extensionLoader;
    }

    /**
     * @param namespace the namespace to be used
     * @return the extension ComponentManager for the current namespace
     */
    private ComponentManager getExtensionComponentManager(String namespace) {
        ComponentManager extensionComponentManager = this.componentManagerManager.getComponentManager(namespace,
                false);
        if (extensionComponentManager == null) {
            try {
                extensionComponentManager = this.mocker.getInstance(ComponentManager.class);
            } catch (Exception e) {
                // Should never happen
            }
        }

        return extensionComponentManager;
    }

    /**
     * Check that the extension is properly reported to be installed in all namespaces.
     *
     * @param installedExtension the local extension to check
     */
    private void checkInstallStatus(InstalledExtension installedExtension) {
        checkInstallStatus(installedExtension, null);
    }

    /**
     * Check that the extension is properly reported to be installed in the given namespace.
     *
     * @param installedExtension the local extension to check
     * @param namespace the namespace where it has been installed
     */
    private void checkInstallStatus(InstalledExtension installedExtension, String namespace) {
        // check extension status
        Assert.assertNotNull(installedExtension);
        Assert.assertNotNull(installedExtension.getFile());
        Assert.assertTrue(new File(installedExtension.getFile().getAbsolutePath()).exists());
        Assert.assertTrue(installedExtension.isInstalled(namespace));
        if (namespace != null) {
            Assert.assertFalse(installedExtension.isInstalled(null));
        }

        // check repository status
        Assert.assertNotNull(this.installedExtensionRepository
                .getInstalledExtension(installedExtension.getId().getId(), namespace));
        if (namespace != null) {
            Assert.assertNull(this.installedExtensionRepository
                    .getInstalledExtension(installedExtension.getId().getId(), null));
        }
    }

    private Type getLoadedType(Type role, ClassLoader extensionLoader) throws ClassNotFoundException {
        if (role instanceof Class) {
            return Class.forName(((Class<?>) role).getName(), true, extensionLoader);
        } else if (role instanceof ParameterizedType) {
            Class<?> rawType = Class.forName(((Class<?>) ((ParameterizedType) role).getRawType()).getName(), true,
                    extensionLoader);
            return new DefaultParameterizedType(((ParameterizedType) role).getOwnerType(), rawType,
                    ((ParameterizedType) role).getActualTypeArguments());
        }

        return null;
    }

    /**
     * Check that an extension is effectively available in all namespace and that the global component manager provide
     * the expected default implementation.
     *
     * @param role the role expected to be provided by the extension
     * @param implementation the implementation expected for the given role
     * @param <T> the role class
     * @return the effective role class in the extension class loader
     * @throws Exception on error
     */
    private <T> Type checkJarExtensionAvailability(Type role, Class<? extends T> implementation) throws Exception {
        return checkJarExtensionAvailability(role, implementation, null);
    }

    /**
     * Check that an extension is effectively available in the given namespace and that the corresponding component
     * manager provide the expected default implementation.
     *
     * @param role the role expected to be provided by the extension
     * @param implementation the implementation expected for the given role
     * @param namespace the namespace where the extension is expected to installed
     * @param <T> the role class
     * @return the effective role class in the extension class loader
     * @throws Exception on error
     */
    private <T> Type checkJarExtensionAvailability(Type role, Class<? extends T> implementation, String namespace)
            throws Exception {
        ClassLoader extensionLoader = getExtensionClassloader(namespace);
        Assert.assertNotNull(extensionLoader);
        Assert.assertNotSame(this.testApplicationClassloader, extensionLoader);

        Type loadedRole = getLoadedType(role, extensionLoader);
        // Ensure the loaded role does not came from the application classloader (a check to validate the test)
        Assert.assertFalse(loadedRole.equals(role));

        if (namespace != null) {
            try {
                this.jarExtensionClassLoader.getURLClassLoader(null, false)
                        .loadClass(ReflectionUtils.getTypeClass(loadedRole).getName());
                Assert.fail("the interface should not be in the root class loader");
            } catch (ClassNotFoundException expected) {
                // expected
            }
        }

        // check components managers
        Class<?> componentInstanceClass = null;
        if (namespace != null) {
            componentInstanceClass = getExtensionComponentManager(namespace).getInstance(loadedRole).getClass();

            try {
                this.mocker.getInstance(loadedRole);
                Assert.fail("the component should not be in the root component manager");
            } catch (ComponentLookupException expected) {
                // expected
            }
        } else {
            componentInstanceClass = this.mocker.getInstance(loadedRole).getClass();
        }
        Assert.assertEquals(implementation.getName(), componentInstanceClass.getName());
        Assert.assertNotSame(implementation, componentInstanceClass);

        return loadedRole;
    }

    /**
     * Check that the extension is properly reported to be not installed in all namespace.
     *
     * @param localExtension the local extension to check
     */
    private void ckeckUninstallStatus(LocalExtension localExtension) {
        ckeckUninstallStatus(localExtension, null);
    }

    /**
     * Check that the extension is properly reported to be not installed in the given namespace.
     *
     * @param localExtension the local extension to check
     * @param namespace the namespace where it should not be installed
     */
    private void ckeckUninstallStatus(LocalExtension localExtension, String namespace) {
        // check extension status
        Assert.assertFalse(DefaultInstalledExtension.isInstalled(localExtension, namespace));
        Assert.assertFalse(DefaultInstalledExtension.isInstalled(localExtension, null));

        // check repository status
        InstalledExtension installedExtension = this.installedExtensionRepository
                .getInstalledExtension(localExtension.getId().getId(), namespace);
        if (installedExtension != null) {
            Assert.assertNotEquals(localExtension.getId(), installedExtension.getId());
        }
    }

    /**
     * Check that an extension is effectively not available in all namespace and that the corresponding component
     * manager does not provide an implementation.
     *
     * @param role the role expected to not be provide
     * @throws Exception on error
     */
    private void checkJarExtensionUnavailability(Type role) throws Exception {
        checkJarExtensionUnavailability(role, null);
    }

    /**
     * Check that an extension is effectively not available in the given namespace and that the corresponding component
     * manager does not provide an implementation.
     *
     * @param role the role expected to not be provide
     * @param namespace the namespace where the extension is not expected to be installed
     * @throws Exception on error
     */
    private void checkJarExtensionUnavailability(Type role, String namespace) throws Exception {
        try {
            ClassLoader extensionLoader = getExtensionClassloader(namespace);
            Type loadedRole = getLoadedType(role, extensionLoader);

            // check components managers
            this.mocker.getInstance(loadedRole);
            Assert.fail("the extension has not been uninstalled, component found!");
        } catch (ComponentLookupException unexpected) {
            Assert.fail("the extension has not been uninstalled, role found!");
        } catch (ClassNotFoundException expected) {
            // expected
        }
    }

    @Test
    public void testInstallAndUninstallExtension() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension", "test");

        // actual install test
        InstalledExtension installedExtension = install(extensionId);

        checkInstallStatus(installedExtension);

        Assert.assertNotNull(this.installedExtensionRepository.getInstalledExtension("feature", null));

        Type extensionRole1 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);

        // try to install again
        try {
            install(extensionId);
            Assert.fail("installExtension should have failed");
        } catch (InstallException expected) {
            // expected
        }

        // actual uninstall test
        LocalExtension localExtension = uninstall(extensionId, null);

        ckeckUninstallStatus(localExtension);

        checkJarExtensionUnavailability(TestComponent.TYPE_STRING);

        // actual reinstall test
        installedExtension = install(extensionId);

        checkInstallStatus(installedExtension);

        Type extensionRole2 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        assertNotEquals(extensionRole1, extensionRole2);
    }

    @Test
    public void testUpgradeAndDowngradeExtensionOnNamespace() throws Throwable {
        final ExtensionId extensionId1 = new ExtensionId("test", "1.0");
        final ExtensionId extensionId2 = new ExtensionId("test", "2.0");

        // install 1.0
        InstalledExtension installedExtension = install(extensionId1, NAMESPACE);

        checkInstallStatus(installedExtension, NAMESPACE);

        Assert.assertSame(installedExtension,
                this.installedExtensionRepository.getInstalledExtension(extensionId1.getId(), NAMESPACE));
        Assert.assertNull(this.installedExtensionRepository.getInstalledExtension(extensionId1.getId(), null));

        Class<?> clazz = Class.forName("test.TestClass", true, getExtensionClassloader(NAMESPACE));
        assertNotNull(MethodUtils.getAccessibleMethod(clazz, "method1"));
        assertNull(MethodUtils.getAccessibleMethod(clazz, "method2"));

        // upgrade to 2.0
        installedExtension = install(extensionId2, NAMESPACE);

        checkInstallStatus(installedExtension, NAMESPACE);

        Assert.assertSame(installedExtension,
                this.installedExtensionRepository.getInstalledExtension(extensionId2.getId(), NAMESPACE));
        Assert.assertNull(this.installedExtensionRepository.getInstalledExtension(extensionId2.getId(), null));

        clazz = Class.forName("test.TestClass", true, getExtensionClassloader(NAMESPACE));
        assertNotNull(MethodUtils.getAccessibleMethod(clazz, "method1"));
        assertNotNull(MethodUtils.getAccessibleMethod(clazz, "method2"));

        // downgrade to 1.0
        installedExtension = install(extensionId1, NAMESPACE);

        checkInstallStatus(installedExtension, NAMESPACE);

        Assert.assertSame(installedExtension,
                this.installedExtensionRepository.getInstalledExtension(extensionId1.getId(), NAMESPACE));
        Assert.assertNull(this.installedExtensionRepository.getInstalledExtension(extensionId1.getId(), null));

        clazz = Class.forName("test.TestClass", true, getExtensionClassloader(NAMESPACE));
        assertNotNull(MethodUtils.getAccessibleMethod(clazz, "method1"));
        assertNull(MethodUtils.getAccessibleMethod(clazz, "method2"));
    }

    @Test
    public void testInstallAndUninstallExtensionOnNamespace() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension", "test");

        // actual install test
        InstalledExtension installedExtension = install(extensionId, NAMESPACE);

        checkInstallStatus(installedExtension, NAMESPACE);

        Assert.assertNotNull(this.installedExtensionRepository.getInstalledExtension("feature", NAMESPACE));
        Assert.assertNull(this.installedExtensionRepository.getInstalledExtension("feature", null));

        checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class, NAMESPACE);

        try {
            install(extensionId, NAMESPACE);
            Assert.fail("installExtension should have failed");
        } catch (InstallException expected) {
            // expected
        }

        // actual uninstall test
        LocalExtension localExtension = uninstall(extensionId, null);

        ckeckUninstallStatus(localExtension, NAMESPACE);

        checkJarExtensionUnavailability(TestComponent.TYPE_STRING, NAMESPACE);
    }

    @Test
    public void testInstallAndUninstallExtensionWithoutComponents() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("simplejar", "test");

        // actual install test
        InstalledExtension installedExtension = install(extensionId);

        checkInstallStatus(installedExtension);

        // actual uninstall test
        LocalExtension localExtension = uninstall(extensionId, null);

        ckeckUninstallStatus(localExtension);

        // actual reinstall test
        installedExtension = install(extensionId);

        checkInstallStatus(installedExtension);
    }

    @Test
    public void testInstallAndUninstallExtensionWithDependency() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");

        // actual install test
        InstalledExtension installedExtension = install(extensionId);

        checkInstallStatus(installedExtension);

        Assert.assertNotNull(this.installedExtensionRepository.getInstalledExtension("feature-with-deps", null));
        Assert.assertNotNull(this.installedExtensionRepository.getInstalledExtension("feature", null));

        Type extensionRole1 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class);
        Type extensionDep1 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);

        // actual uninstall test
        LocalExtension localExtension = uninstall(extensionId, null);

        ckeckUninstallStatus(localExtension);

        checkJarExtensionUnavailability(TestComponentWithDeps.class);
        Type extensionDep2 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        assertNotEquals(extensionDep1, extensionDep2);

        // actual reinstall test
        installedExtension = install(extensionId);

        checkInstallStatus(installedExtension);

        Type extensionRole2 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class);
        Type extensionDep3 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        assertNotEquals(extensionRole1, extensionRole2);
        Assert.assertEquals(extensionDep2, extensionDep3);
    }

    @Test
    public void testInstallAndUninstallExtensionWithDependencyOnANamespace() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");
        final String namespace = NAMESPACE;

        // actual install test
        InstalledExtension installExtension = install(extensionId, namespace);

        checkInstallStatus(installExtension, namespace);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace));
        Assert.assertNull(this.installedExtensionRepository.getInstalledExtension("feature-with-deps", null));

        checkJarExtensionAvailability(TestComponentWithDeps.class, DefaultTestComponentWithDeps.class, namespace);
        checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class, namespace);

        // actual uninstall test
        LocalExtension localExtension = uninstall(extensionId, null);

        ckeckUninstallStatus(localExtension, namespace);

        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace);
        checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class, namespace);
    }

    @Test
    public void testInstallEntensionAndUninstallDependency() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");
        final ExtensionId dependencyId = new ExtensionId("org.xwiki.test:test-extension", "test");

        // actual install test
        InstalledExtension installedExtension = install(extensionId);

        checkInstallStatus(installedExtension);

        Assert.assertNotNull(this.installedExtensionRepository.getInstalledExtension("feature-with-deps", null));
        Assert.assertNotNull(this.installedExtensionRepository.getInstalledExtension("feature", null));

        Type extensionRole1 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class);
        Type extensionDep1 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);

        // actual uninstall test
        LocalExtension localExtension = uninstall(dependencyId, null);

        ckeckUninstallStatus(localExtension);

        checkJarExtensionUnavailability(TestComponent.TYPE_STRING);
        checkJarExtensionUnavailability(TestComponentWithDeps.class);

        // actual reinstall test
        installedExtension = install(extensionId);

        checkInstallStatus(installedExtension);

        Type extensionRole2 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class);
        Type extensionDep2 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        assertNotEquals(extensionRole1, extensionRole2);
        assertNotEquals(extensionDep1, extensionDep2);
    }

    @Test
    public void testInstallExtensionAndUninstallDependencyOnANamespace() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");
        final ExtensionId dependencyId = new ExtensionId("org.xwiki.test:test-extension", "test");
        final String namespace = NAMESPACE;

        // actual install test
        InstalledExtension installedExtension = install(extensionId, namespace);

        checkInstallStatus(installedExtension, namespace);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace));
        Assert.assertNull(this.installedExtensionRepository.getInstalledExtension("feature-with-deps", null));

        checkJarExtensionAvailability(TestComponentWithDeps.class, DefaultTestComponentWithDeps.class, namespace);
        checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class, namespace);

        // actual uninstall test
        LocalExtension localExtension = uninstall(dependencyId, null);

        ckeckUninstallStatus(localExtension, namespace);

        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace);
        checkJarExtensionUnavailability(TestComponentWithDeps.class);
    }

    @Test
    public void testInstallDependencyInstallExtensionOnANamespaceAndUninstallExtension() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");
        final ExtensionId dependencyId = new ExtensionId("org.xwiki.test:test-extension", "test");
        final String namespace = NAMESPACE;

        // actual install test
        InstalledExtension installedExtension = install(dependencyId);

        checkInstallStatus(installedExtension);
        Assert.assertNotNull(this.installedExtensionRepository.getInstalledExtension("feature", null));
        Type extensionDep1 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);

        // actual install test
        installedExtension = install(extensionId, namespace);

        checkInstallStatus(installedExtension, namespace);
        checkInstallStatus(this.installedExtensionRepository.resolve(dependencyId));

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace));
        Type extensionRole1 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace);
        Type extensionDep2 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        Assert.assertEquals(extensionDep1, extensionDep2);

        // actual uninstall test
        LocalExtension localExtension = uninstall(extensionId, namespace);

        ckeckUninstallStatus(localExtension, namespace);
        checkInstallStatus(this.installedExtensionRepository.resolve(dependencyId));

        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace);
        Type extensionDep3 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        Assert.assertEquals(extensionDep1, extensionDep3);

        // actual reinstall test
        installedExtension = install(extensionId, namespace);

        checkInstallStatus(installedExtension, namespace);

        Type extensionRole2 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace);
        Type extensionDep4 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        assertNotEquals(extensionRole1, extensionRole2);
        Assert.assertEquals(extensionDep1, extensionDep4);
    }

    @Test
    public void testInstallDependencyInstallExtensionOnANamespaceAndUninstallDependency() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");
        final ExtensionId dependencyId = new ExtensionId("org.xwiki.test:test-extension", "test");
        final String namespace = NAMESPACE;

        // actual install test
        InstalledExtension installedExtension = install(dependencyId);

        checkInstallStatus(installedExtension);
        Assert.assertNotNull(this.installedExtensionRepository.getInstalledExtension("feature", null));
        Type extensionDep1 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);

        // actual install test
        installedExtension = install(extensionId, namespace);

        checkInstallStatus(installedExtension, namespace);
        checkInstallStatus(this.installedExtensionRepository.resolve(dependencyId));

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace));
        Type extensionRole1 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace);
        Type extensionDep2 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        Assert.assertEquals(extensionDep1, extensionDep2);

        // actual uninstall test
        LocalExtension localExtension = uninstall(dependencyId, null);

        ckeckUninstallStatus(localExtension);
        ckeckUninstallStatus(this.localExtensionRepository.resolve(extensionId), namespace);

        checkJarExtensionUnavailability(TestComponent.TYPE_STRING);
        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace);

        // actual reinstall test
        installedExtension = install(extensionId, namespace);

        checkInstallStatus(installedExtension, namespace);
        checkInstallStatus(this.installedExtensionRepository.resolve(dependencyId), namespace);

        Type extensionRole2 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace);
        Type extensionDep3 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace);
        assertNotEquals(extensionRole1, extensionRole2);
        assertNotEquals(extensionDep1, extensionDep3);
    }

    @Test
    public void testMultipleInstallOnANamespaceAndUninstall() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");
        final String namespace1 = "namespace1";
        final String namespace2 = "namespace2";

        // actual install test
        InstalledExtension installedExtension = install(extensionId, namespace1);

        checkInstallStatus(installedExtension, namespace1);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace1));
        Type extensionRole1 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace1);
        Type extensionDep1 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace1);

        // actual install test
        // TODO: go back to LogLevel.WARN when http://jira.xwiki.org/browse/XCOMMONS-213 is fixed
        installedExtension = install(extensionId, namespace2, LogLevel.ERROR);

        checkInstallStatus(installedExtension, namespace2);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace2));
        Type extensionRole2 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace2);
        Type extensionDep2 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace2);
        assertNotEquals(extensionRole1, extensionRole2);
        assertNotEquals(extensionDep1, extensionDep2);

        // actual uninstall test
        // FIXME: Ignore warning because of https://jira.xwiki.org/browse/XCOMMONS-213
        LocalExtension localExtension = uninstall(extensionId, namespace1, LogLevel.ERROR);

        ckeckUninstallStatus(localExtension);

        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace1);
        Type extensionDep3 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace1);
        assertNotEquals(extensionDep1, extensionDep3);

        Type extensionRole3 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace2);
        Type extensionDep4 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace2);
        Assert.assertEquals(extensionRole2, extensionRole3);
        Assert.assertEquals(extensionDep2, extensionDep4);

        // actual uninstall test
        // FIXME: Ignore warning because of https://jira.xwiki.org/browse/XCOMMONS-213
        localExtension = uninstall(extensionId, namespace2, LogLevel.ERROR);

        ckeckUninstallStatus(localExtension);

        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace2);
        Type extensionDep5 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace2);
        assertNotEquals(extensionDep2, extensionDep5);

        Type extensionDep6 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace1);
        Assert.assertEquals(extensionDep3, extensionDep6);
    }

    @Test
    public void testMultipleInstallOnANamespaceAndUninstallDependency() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");
        final ExtensionId dependencyId = new ExtensionId("org.xwiki.test:test-extension", "test");
        final String namespace1 = "namespace1";
        final String namespace2 = "namespace2";

        InstalledExtension installedExtension = install(extensionId, namespace1);

        checkInstallStatus(installedExtension, namespace1);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace1));
        Type extensionRole1 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace1);
        Type extensionDep1 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace1);

        // TODO: go back to LogLevel.WARN when http://jira.xwiki.org/browse/XCOMMONS-213 is fixed
        installedExtension = install(extensionId, namespace2, LogLevel.ERROR);

        checkInstallStatus(installedExtension, namespace2);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace2));
        Type extensionRole2 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace2);
        Type extensionDep2 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace2);
        assertNotEquals(extensionRole1, extensionRole2);
        assertNotEquals(extensionDep1, extensionDep2);

        // actual uninstall test
        LocalExtension localExtension = uninstall(dependencyId, namespace1);

        ckeckUninstallStatus(localExtension);

        checkJarExtensionUnavailability(TestComponent.TYPE_STRING, namespace1);
        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace1);

        Type extensionRole3 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace2);
        Type extensionDep3 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace2);
        Assert.assertEquals(extensionRole2, extensionRole3);
        Assert.assertEquals(extensionDep2, extensionDep3);

        // actual uninstall test
        localExtension = uninstall(dependencyId, namespace2);

        ckeckUninstallStatus(localExtension);

        checkJarExtensionUnavailability(TestComponent.TYPE_STRING, namespace2);
        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace2);
    }

    @Test
    public void testMultipleInstallOnANamespaceAndUninstallAll() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");
        final String namespace1 = "namespace1";
        final String namespace2 = "namespace2";

        InstalledExtension installedExtension = install(extensionId, namespace1);

        checkInstallStatus(installedExtension, namespace1);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace1));
        Type extensionRole1 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace1);
        Type extensionDep1 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace1);

        // TODO: go back to LogLevel.WARN when http://jira.xwiki.org/browse/XCOMMONS-213 is fixed
        installedExtension = install(extensionId, namespace2, LogLevel.ERROR);

        checkInstallStatus(installedExtension, namespace2);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace2));
        Type extensionRole2 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace2);
        Type extensionDep2 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace2);
        assertNotEquals(extensionRole1, extensionRole2);
        assertNotEquals(extensionDep1, extensionDep2);

        // actual uninstall test
        // FIXME: Ignore warning because of https://jira.xwiki.org/browse/XCOMMONS-213
        LocalExtension localExtension = uninstall(extensionId, null, LogLevel.ERROR);

        ckeckUninstallStatus(localExtension);

        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace1);
        Type extensionDep3 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace1);
        assertNotEquals(extensionDep1, extensionDep3);
        Type extensionDep4 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace2);
        assertNotEquals(extensionDep2, extensionDep4);
    }

    @Test
    public void testMultipleInstallOnANamespaceAndUninstallDependencyAll() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");
        final ExtensionId dependencyId = new ExtensionId("org.xwiki.test:test-extension", "test");
        final String namespace1 = "namespace1";
        final String namespace2 = "namespace2";

        InstalledExtension installedExtension = install(extensionId, namespace1);

        checkInstallStatus(installedExtension, namespace1);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace1));
        Type extensionRole1 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace1);
        Type extensionDep1 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace1);

        // TODO: go back to LogLevel.WARN when http://jira.xwiki.org/browse/XCOMMONS-213 is fixed
        installedExtension = install(extensionId, namespace2, LogLevel.ERROR);

        checkInstallStatus(installedExtension, namespace2);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace2));
        Type extensionRole2 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace2);
        Type extensionDep2 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class,
                namespace2);
        assertNotEquals(extensionRole1, extensionRole2);
        assertNotEquals(extensionDep1, extensionDep2);

        // actual uninstall test
        LocalExtension localExtension = uninstall(dependencyId, null);

        ckeckUninstallStatus(localExtension);

        checkJarExtensionUnavailability(TestComponent.TYPE_STRING, namespace1);
        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace1);
        checkJarExtensionUnavailability(TestComponent.TYPE_STRING, namespace2);
        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace2);
    }

    @Test
    public void testMultipleInstallOnANamespaceWithGlobalDependencyAndUninstall() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");
        final ExtensionId dependencyId = new ExtensionId("org.xwiki.test:test-extension", "test");
        final String namespace1 = "namespace1";
        final String namespace2 = "namespace2";

        // install global deps
        InstalledExtension installedExtension = install(dependencyId);

        checkInstallStatus(installedExtension);

        Assert.assertNotNull(this.installedExtensionRepository.getInstalledExtension("feature", null));
        Type extensionDep1 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);

        // actual install test
        installedExtension = install(extensionId, namespace1);

        checkInstallStatus(installedExtension, namespace1);
        checkInstallStatus(this.installedExtensionRepository.resolve(dependencyId));

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace1));
        Type extensionRole1 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace1);
        Type extensionDep2 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        Assert.assertEquals(extensionDep1, extensionDep2);

        // actual install test
        installedExtension = install(extensionId, namespace2);

        checkInstallStatus(installedExtension, namespace2);
        checkInstallStatus(this.installedExtensionRepository.resolve(dependencyId));

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace2));
        Type extensionRole2 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace2);
        Type extensionDep3 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        assertNotEquals(extensionRole1, extensionRole2);
        Assert.assertEquals(extensionDep1, extensionDep3);

        // actual uninstall test
        LocalExtension localExtension = uninstall(extensionId, namespace1);

        ckeckUninstallStatus(localExtension);
        checkInstallStatus(this.installedExtensionRepository.resolve(dependencyId));

        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace1);

        Type extensionRole3 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace2);
        Type extensionDep4 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        Assert.assertEquals(extensionRole2, extensionRole3);
        Assert.assertEquals(extensionDep1, extensionDep4);

        // actual uninstall test
        localExtension = uninstall(extensionId, namespace2);

        ckeckUninstallStatus(localExtension);
        checkInstallStatus(this.installedExtensionRepository.resolve(dependencyId));

        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace2);
        Type extensionDep5 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        Assert.assertEquals(extensionDep1, extensionDep5);
    }

    @Test
    public void testMultipleInstallOnANamespaceWithGlobalDependencyAndUninstallDependency() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension-with-deps", "test");
        final ExtensionId dependencyId = new ExtensionId("org.xwiki.test:test-extension", "test");
        final String namespace1 = "namespace1";
        final String namespace2 = "namespace2";

        // install global deps
        InstalledExtension installedExtension = install(dependencyId);

        checkInstallStatus(installedExtension);

        Assert.assertNotNull(this.installedExtensionRepository.getInstalledExtension("feature", null));
        Type extensionDep1 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);

        installedExtension = install(extensionId, namespace1);

        checkInstallStatus(installedExtension, namespace1);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace1));
        Type extensionRole1 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace1);
        Type extensionDep2 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        Assert.assertEquals(extensionDep1, extensionDep2);

        installedExtension = install(extensionId, namespace2);

        checkInstallStatus(installedExtension, namespace2);

        Assert.assertNotNull(
                this.installedExtensionRepository.getInstalledExtension("feature-with-deps", namespace2));
        Type extensionRole2 = checkJarExtensionAvailability(TestComponentWithDeps.class,
                DefaultTestComponentWithDeps.class, namespace2);
        Type extensionDep3 = checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
        assertNotEquals(extensionRole1, extensionRole2);
        Assert.assertEquals(extensionDep1, extensionDep3);

        // actual uninstall test
        LocalExtension localExtension = uninstall(dependencyId, null);

        ckeckUninstallStatus(localExtension);
        ckeckUninstallStatus(this.localExtensionRepository.resolve(extensionId), namespace1);
        ckeckUninstallStatus(this.localExtensionRepository.resolve(extensionId), namespace2);

        checkJarExtensionUnavailability(TestComponent.TYPE_STRING);
        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace1);
        checkJarExtensionUnavailability(TestComponentWithDeps.class, namespace2);
    }

    @Test
    public void testUninstallInvalidExtensionFromNamespace() throws Throwable {
        ExtensionId extensionId = new ExtensionId("invalidextensiononnamespace", "version");

        uninstall(extensionId, "namespaceofinvalidextension");
    }

    @Test
    public void testUninstallInvalidExtensionFromRoot() throws Throwable {
        ExtensionId extensionId = new ExtensionId("invalidextensiononroot", "version");

        uninstall(extensionId, null);
    }

    @Test
    public void testExtensionInstalledOnNamespaceAtInit() throws Throwable {
        ExtensionId extensionId = new ExtensionId("installedextensiononnamespace", "1.0");

        InstalledExtension installedExtension = this.installedExtensionRepository.resolve(extensionId);

        checkInstallStatus(installedExtension, NAMESPACE);

        checkJarExtensionAvailability(packagefile.installedextensiononnamespace.TestInstalledComponent.TYPE_STRING,
                packagefile.installedextensiononnamespace.DefaultTestInstalledComponent.class, NAMESPACE);

        uninstall(extensionId, NAMESPACE);
    }

    @Test
    public void testExtensionInstalledOnRootAtInit() throws Throwable {
        ExtensionId extensionId = new ExtensionId("installedextensiononroot", "1.0");

        InstalledExtension installedExtension = this.installedExtensionRepository.resolve(extensionId);

        checkInstallStatus(installedExtension, null);

        checkJarExtensionAvailability(packagefile.installedextensiononroot.TestInstalledComponent.TYPE_STRING,
                packagefile.installedextensiononroot.DefaultTestInstalledComponent.class, null);

        uninstall(extensionId, null);
    }

    @Test
    public void testInstallOnNamespaceThenOnRoot() throws Throwable {
        final ExtensionId extensionId = new ExtensionId("org.xwiki.test:test-extension", "test");

        // install on namespace
        InstalledExtension installedExtension = install(extensionId, NAMESPACE);

        checkInstallStatus(installedExtension, NAMESPACE);

        Assert.assertSame(installedExtension,
                this.installedExtensionRepository.getInstalledExtension(extensionId.getId(), NAMESPACE));
        Assert.assertNull(this.installedExtensionRepository.getInstalledExtension(extensionId.getId(), null));

        checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class, NAMESPACE);

        // install on root
        installedExtension = install(extensionId);

        checkInstallStatus(installedExtension);

        checkJarExtensionAvailability(TestComponent.TYPE_STRING, DefaultTestComponent.class);
    }

    @Test
    public void testInstallOnNamespaceThenUpgradeOnRoot() throws Throwable {
        final ExtensionId extensionId1 = new ExtensionId("jarupgrade", "1.0");
        final ExtensionId extensionId2 = new ExtensionId("jarupgrade", "2.0");

        // install on namespace
        InstalledExtension installedExtension1 = install(extensionId1, NAMESPACE);

        checkInstallStatus(installedExtension1, NAMESPACE);

        Assert.assertSame(installedExtension1,
                this.installedExtensionRepository.getInstalledExtension(extensionId1.getId(), NAMESPACE));
        Assert.assertNull(this.installedExtensionRepository.getInstalledExtension(extensionId1.getId(), null));

        checkJarExtensionAvailability(packagefile.jarupgrade1.TestComponent.class,
                packagefile.jarupgrade1.DefaultTestComponent.class, NAMESPACE);

        // install on root
        InstalledExtension installedExtension2 = install(extensionId2);

        LocalExtension localExtension = this.localExtensionRepository.getLocalExtension(extensionId1);

        ckeckUninstallStatus(localExtension, NAMESPACE);
        checkInstallStatus(installedExtension2);

        checkJarExtensionUnavailability(packagefile.jarupgrade1.TestComponent.class, NAMESPACE);
        checkJarExtensionAvailability(packagefile.jarupgrade2.TestComponent.class,
                packagefile.jarupgrade2.DefaultTestComponent.class);
    }
}