/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.arquillian.core.impl.loadable;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.jboss.arquillian.core.impl.loadable.util.FakeService;
import org.jboss.arquillian.core.impl.loadable.util.ShouldBeExcluded;
import org.jboss.arquillian.core.impl.loadable.util.ShouldBeIncluded;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.classloader.ShrinkWrapClassLoader;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Assert;
import org.junit.Test;

/**
 * JavaSPIExtensionLoaderTestCase
 *
 * @author Davide D'Alto
 * @version $Revision: $
 */
public class JavaSPIExtensionLoaderTestCase {

    private static final String NEW_LINE = System.getProperty("line.separator");

    @Test
    public void shouldBeAbleToAddSelectedProvider() throws Exception {
        Collection<FakeService> all = new JavaSPIExtensionLoader().all(
            JavaSPIExtensionLoaderTestCase.class.getClassLoader(), FakeService.class);

        Assert.assertEquals("Unexpected number of provider loaded", 1, all.size());
        Assert.assertEquals("Wrong provider loaded", ShouldBeIncluded.class, all.iterator().next().getClass());
    }

    @Test
    public void shouldBeAbleToAddSelectedProviderFromClassLoader() throws Exception {
        Archive<JavaArchive> jarWithDefaultServiceImpl = createJarWithDefaultServiceImpl();
        Archive<JavaArchive> jarThatReplaceServiceImpl = createJarThatReplaceServiceImpl();

        ClassLoader emptyParent = null;
        ShrinkWrapClassLoader swClassloader =
            new ShrinkWrapClassLoader(emptyParent, jarThatReplaceServiceImpl, jarWithDefaultServiceImpl);

        ClassLoader emptyClassLoader = new ClassLoader(null) {
        };
        ClassLoader originalClassLoader = SecurityActions.getThreadContextClassLoader();

        Collection<?> providers = null;
        Class<?> expectedImplClass = null;
        try {
            Thread.currentThread().setContextClassLoader(emptyClassLoader);

            Class<?> serviceClass = swClassloader.loadClass("org.jboss.arquillian.core.impl.loadable.util.FakeService");
            expectedImplClass = swClassloader.loadClass("org.jboss.arquillian.core.impl.loadable.util.ShouldBeIncluded");

            providers = new JavaSPIExtensionLoader().all(swClassloader, serviceClass);
        } finally {
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }

        Assert.assertEquals("Unexpected number of providers loaded", 1, providers.size());
        Assert.assertEquals("Wrong provider loaded", expectedImplClass, providers.iterator().next().getClass());
    }

    @Test
    public void shouldBeAbleToLoadVetoedClasses() throws Exception {
        Archive<JavaArchive> jarWithVetoedServiceImpl = createJarWithVetoedServices();

        ClassLoader emptyParent = null;
        ShrinkWrapClassLoader swClassloader = new ShrinkWrapClassLoader(emptyParent, jarWithVetoedServiceImpl);

        ClassLoader emptyClassLoader = new ClassLoader(null) {
        };
        ClassLoader originalClassLoader = SecurityActions.getThreadContextClassLoader();

        Map<Class<?>, Set<Class<?>>> vetoed = null;
        Class<?> service;
        try {
            Thread.currentThread().setContextClassLoader(emptyClassLoader);

            service = swClassloader.loadClass("org.jboss.arquillian.core.impl.loadable.util.FakeService");
            swClassloader.loadClass("org.jboss.arquillian.core.impl.loadable.util.ShouldBeIncluded");
            swClassloader.loadClass("org.jboss.arquillian.core.impl.loadable.util.ShouldBeExcluded");

            vetoed = new JavaSPIExtensionLoader().loadVetoed(swClassloader);
        } finally {
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }

        Assert.assertEquals("Unexpected number of vetoed services", 1, vetoed.size());
        Assert.assertEquals("Unexpected number of vetoed services impl", 2, vetoed.get(service).size());
    }

    private Archive<JavaArchive> createJarWithVetoedServices() {
        StringAsset exclusions = new StringAsset(""
            +
            "org.jboss.arquillian.core.impl.loadable.util.FakeService=org.jboss.arquillian.core.impl.loadable.util.ShouldBeIncluded, "
            +
            "org.jboss.arquillian.core.impl.loadable.util.ShouldBeExcluded");

        Archive<JavaArchive> archive = ShrinkWrap.create(JavaArchive.class)
            .addClasses(FakeService.class, ShouldBeIncluded.class, ShouldBeExcluded.class)
            .addAsManifestResource(exclusions, "exclusions");

        return archive;
    }

    private Archive<JavaArchive> createJarWithDefaultServiceImpl() {
        Archive<JavaArchive> archive = ShrinkWrap.create(JavaArchive.class)
            .addClasses(FakeService.class, ShouldBeExcluded.class)
            .addAsServiceProvider(FakeService.class, ShouldBeExcluded.class);
        return archive;
    }

    private Archive<JavaArchive> createJarThatReplaceServiceImpl() {
        StringAsset serviceConfig = new StringAsset(
            "!org.jboss.arquillian.core.impl.loadable.util.ShouldBeExcluded"
                + NEW_LINE +
                "org.jboss.arquillian.core.impl.loadable.util.ShouldBeIncluded");

        Archive<JavaArchive> archive2 = ShrinkWrap.create(JavaArchive.class)
            .addClasses(ShouldBeIncluded.class)
            .addAsManifestResource(serviceConfig, "/services/org.jboss.arquillian.core.impl.loadable.util.FakeService");
        return archive2;
    }
}
