/******************************************************************
Copyright (c) 2014 Renato Garcia and others. All rights reserved.
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.

Contributors:
    ...
 *****************************************************************/
package org.datanucleus.tests;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.datanucleus.tests.annotations.TransactionMode.Mode.OPTIMISTIC;
import static org.datanucleus.tests.annotations.TransactionMode.Mode.PESSIMISTIC;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Optional;

import org.datanucleus.tests.annotations.Datastore;
import org.datanucleus.tests.annotations.Datastore.DatastoreKey;
import org.datanucleus.tests.annotations.TransactionMode;
import org.datanucleus.tests.annotations.TransactionMode.Mode;
import org.datanucleus.util.NucleusLogger;
import org.junit.Assume;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;

import junit.framework.AssertionFailedError;

/**
 * Provides support for custom annotations such as {@link Datastore} which allows to skip tests for specific configurations
 */
public class DataNucleusTestWatcher extends TestWatcher
{
    /** Log for unit testing. */
    private static final NucleusLogger LOG = NucleusLogger.getLoggerInstance("DataNucleus.Test");
    
    private Description description;

    /**
     * Log the error to using DN log
     */
    @Override
    protected void failed(Throwable e, Description description)
    {
        if(!(e instanceof AssertionFailedError))
        {
            // JUnit assert errors are already logged by tests that are not using this feature of
            // having the errors automatically sent to the log by the runner.
            // New test will not catch the exception or should raise a java.lang.AssertionError 
            // using AssertJ
            LOG.error("Exception running " + description.getDisplayName(), e);
        }
        super.failed(e, description);
    }
    public String getTestName()
    {
        return description.getTestClass().getName() + " " + description.getMethodName();
    }

    @Override
    protected void starting(Description description)
    {
        this.description = description;

        Class<?> testClass = description.getTestClass();
        if (JDOPersistenceTestCase.class.isAssignableFrom(testClass))
        {
            filterDatastores(testClass);
            filterTransactionMode(testClass);
        }
    }

    // Filter based on the Optimistic and Pessimistic transaction configuration
    private void filterTransactionMode(Class<?> testClass)
    {
        List<Mode> filterModes = findAnnotationAtMethodOrClass(TransactionMode.class, description, testClass)
                .map(annotation -> asList(annotation.value()))
                .orElse(emptyList());
        if (!filterModes.isEmpty())
        {
            boolean optimistic = JDOPersistenceTestCase.pmf.getOptimistic();

            Assume.assumeTrue(
                    filterModes
                            .stream()
                            .anyMatch(filter ->
                                (filter.equals(OPTIMISTIC) && optimistic) || (filter.equals(PESSIMISTIC) && !optimistic)
                            ));
        }
    }

    private void filterDatastores(Class<?> testClass)
    {
        List<DatastoreKey> filterDatastores =
                findAnnotationAtMethodOrClass(Datastore.class, description, testClass)
                        .map(annotation -> asList(annotation.value()))
                        .orElse(emptyList());

        if (!filterDatastores.isEmpty())
        {
            String datastoreKey = JDOPersistenceTestCase.storeMgr.getStoreManagerKey();
            DatastoreKey currentDatastore = DatastoreKey.valueOf(datastoreKey.toUpperCase());
            DatastoreKey vendorIdDatastore = JDOPersistenceTestCase.vendorID == null ?
                    null : DatastoreKey.valueOf(JDOPersistenceTestCase.vendorID.toUpperCase());

            Assume.assumeTrue(
                    filterDatastores.stream()
                            .anyMatch(filter -> filter.equals(currentDatastore) || filter.equals(vendorIdDatastore)));
        }
    }

    private <T extends Annotation> Optional<T> findAnnotationAtMethodOrClass(Class<T> annotation, Description description, Class<?> testClass)
    {
        Annotation foundAnnotation = description.getAnnotation(annotation);
        if (foundAnnotation == null)
        {
            foundAnnotation = testClass.getAnnotation(annotation);
        }

        return Optional.ofNullable((T) foundAnnotation);
    }
}

@@ -1392,24 +1392,65 @@
 leusLogger;%0A
+import org.datanucleus.util.StringUtils;%0A
 import org.j
@@ -2954,32 +2954,113 @@
 ass))%0A        %7B%0A
+            NucleusLogger.GENERAL.error(%22%3E%3E TestWatcher.starting %22 + testClass);%0A
             filt
@@ -4251,24 +4251,136 @@
 mptyList());
+%0A        NucleusLogger.GENERAL.error(%22%3E%3E filterDatastores=%22 + StringUtils.collectionToString(filterDatastores));
 %0A%0A        if
@@ -4781,25 +4781,24 @@
 perCase());%0A
-%0A
             
@@ -4801,46 +4801,153 @@
     
-Assume.assumeTrue(%0A                   
+NucleusLogger.GENERAL.error(%22%3E%3E %22 + testClass + %22 current=%22 + currentDatastore + %22 vendor=%22 + vendorIdDatastore);%0A%0A            boolean filtered =
  fil
@@ -4965,32 +4965,24 @@
 es.stream()%0A
-        
             
@@ -5077,16 +5077,257 @@
 astore))
+;%0A            NucleusLogger.GENERAL.error(%22%3E%3E filtered=%22 + filtered);%0A            // TODO With JUNIT 4.11+ this does NOT halt the test (unlike what the Javadoc says, and what it did in earlier versions)%0A            Assume.assumeTrue(filtered
 );%0A     

Add TODO where JUnit is fscking up