/*
 * 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.test.storage.profiles;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.extensions.cpsuite.ClasspathClassesFinder;
import org.junit.extensions.cpsuite.ClassTester;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;
import org.xwiki.component.annotation.Requirement;
import org.xwiki.component.phase.Initializable;
import org.xwiki.test.integration.XWikiExecutor;

/**
 * Run all tests in multiple configuration profiles and start/stop XWiki for each profile.
 * Run all tests found in the current classloader using
 * <a href="http://www.johanneslink.net/projects/cpsuite.jsp">cpsuite</a> (we extend it).
 *
 * Tests can be filtered by passing the "pattern" System Property.
 *
 * @version $Id$
 * @since 3.0RC1
 */
public class ForEachProfileSuite extends ClasspathSuite
{
    public static final String PATTERN = ".*" + System.getProperty("pattern", "");

    public ForEachProfileSuite(Class<?> klass, RunnerBuilder builder) throws InitializationError
    {
        super(klass, builder);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected List<Runner> getChildren()
    {
        List<Runner> runners = new ArrayList<Runner>();

        // Filter classes to run
        for (Runner runner : super.getChildren()) {
            if (runner.getDescription().getClassName().matches(PATTERN)) {
                runners.add(runner);
            }
        }

        return runners;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void run(RunNotifier notifier)
    {
        // Get the list of test profiles.
        final List<Class<?>> profiles =
            new ClasspathClassesFinder(IsProfileTester.INSTANCE, "java.class.path").find();

        final Map<Profile, XWikiExecutor> executorByProfile = new HashMap<Profile, XWikiExecutor>();
        for (int i = 0; i < profiles.size(); i++) {
            try {
                // All executors are #0 because they will not be run in parallel.
                executorByProfile.put(((Class<Profile>)profiles.get(i)).newInstance(),
                                      new XWikiExecutor(0));
            } catch (Exception e) {
                throw new RuntimeException("Failed to instanciate configuration profile.", e);
            }
        }

        // Callback to setup executors in the suite class.
        try {
            for (Profile profile : executorByProfile.keySet()) {
                profile.apply(executorByProfile.get(profile));
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to initialize XWiki Executors", e);
        }


        for (final Profile profile : executorByProfile.keySet()) {
            final XWikiExecutor executor = executorByProfile.get(profile);
            try {

                try {
                    executor.start();
                } catch (Exception e) {
                    throw new RuntimeException("Failed to start XWiki", e);
                }

                try {
                    Object instance = this.getTestClass().getJavaClass().newInstance();

                    // If there is a field which is an XWikiExecutor type
                    // and has an @Requirement annotation, inject the current executor.
                    for (Field field : this.getTestClass().getJavaClass().getDeclaredFields()) {
                        if (field.getType() == XWikiExecutor.class
                            && field.getAnnotation(Requirement.class) != null)
                        {
                            field.setAccessible(true);
                            field.set(instance, executor);
                        }
                    }

                    // If the class is initializable then call initialize.
                    final Class[] interfaces = this.getTestClass().getJavaClass().getInterfaces();
                    for (int i = 0; i < interfaces.length; i++) {
                        if (interfaces[i] == Initializable.class) {
                            this.getTestClass().getJavaClass().getMethod("initialize").invoke(instance);
                        }
                    }

                } catch (Exception e) {
                    throw new RuntimeException("Failed to prepare tests to run in config profile.", e);
                }

                super.run(notifier);
            } finally {
                try {
                    executor.stop();
                } catch (Exception e) {
                    // Squash this and let the original exception be thrown.
                }

                // This is a hack because HSQL seems to be leaving stray lock files once in a while when
                // it is shut down. See: http://jira.xwiki.org/jira/browse/XE-952
                final File executionDirectory = new File(executor.getExecutionDirectory());
                final File lock = new File(new File(executionDirectory, "database"), "xwiki_db.lck");
                if (lock.exists()) {
                    lock.delete();
                }
            }
        }
    }

    /**
     * Tester which will help ClassPathSuite find all Profiles.
     */
    private static class IsProfileTester implements ClassTester
    {
        public static IsProfileTester INSTANCE = new IsProfileTester();

        private final String packageName = this.getClass().getPackage().getName();

        public boolean acceptClass(Class<?> klass)
        {
            final Class[] interfaces = klass.getInterfaces();
            for (int i = 0; i < interfaces.length; i++) {
                if (interfaces[i] == Profile.class) {
                    return true;
                }
            }
            return false;
        }

        public boolean acceptClassName(String className)
        {
            return className.startsWith(this.packageName);
        }

        public boolean acceptInnerClass()
        {
            return false;
        }

        public boolean searchInJars()
        {
            return false;
        }
    }
}

@@ -933,29 +933,8 @@
 s;%0A%0A
-import java.io.File;%0A
 impo
@@ -5744,480 +5744,8 @@
    %7D
-%0A%0A                // This is a hack because HSQL seems to be leaving stray lock files once in a while when%0A                // it is shut down. See: http://jira.xwiki.org/jira/browse/XE-952%0A                final File executionDirectory = new File(executor.getExecutionDirectory());%0A                final File lock = new File(new File(executionDirectory, %22database%22), %22xwiki_db.lck%22);%0A                if (lock.exists()) %7B%0A                    lock.delete();%0A                %7D
 %0A   

Revert "XE-952: StorageTests#allTests flickers because an HSQL database lock file is not deleted when the server is shut down."