AnnotatedTestOptIn.java

/*
 *
 * The DbUnit Database Testing Framework
 * Copyright (C)2002-2026, DbUnit.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
package org.dbunit.annotation.runtime;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.dbunit.annotation.DbUnitConfig;
import org.dbunit.annotation.DbUnitExpected;
import org.dbunit.annotation.DbUnitPrep;
import org.dbunit.annotation.DbUnitRowCountCheck;
import org.dbunit.annotation.DbUnitSetup;
import org.dbunit.annotation.DbUnitTearDown;
import org.dbunit.annotation.DbUnitTestCase;
import org.dbunit.annotation.DbUnitTester;

/**
 * Decides whether a test opts into the {@code org.dbunit.annotation} family, and so into the
 * annotation-driven lifecycle: any of the annotations that configure a test
 * ({@link DbUnitConfig}, {@link DbUnitPrep}, {@link DbUnitSetup}, {@link DbUnitExpected},
 * {@link DbUnitTearDown}, {@link DbUnitRowCountCheck}) on the test method or class, or a
 * {@link DbUnitTester @DbUnitTester}/{@link DbUnitTestCase @DbUnitTestCase} field in the test
 * class or one enclosing it. A plain, unannotated {@link org.dbunit.IDatabaseTester} field alone
 * is not an opt-in - that is the 3.5.0 lifecycle-only style, left exactly as it was.
 *
 * <p>A binding passes its own {@link AnnotationLookup} and the test classes in scope, and may
 * add opt-ins of its own, such as {@code DbUnitExtension}'s {@code @DbUnitTest}. The binding
 * decides what an opted-out test gets: {@code DbUnitExtension} keeps its 3.5.0 lifecycle for
 * it, while the Spring listener leaves it entirely alone.
 *
 * <p>Public so a binding outside {@code org.dbunit.annotation.runtime} - such as
 * {@code DbUnitExtension} or a Spring {@code TestExecutionListener} - can reach it too.
 *
 * @author Jeff Jensen
 * @since 3.6.0
 */
public class AnnotatedTestOptIn
{
    /** The annotations any one of which, on the test method or class, opts a test in. */
    private static final List<Class<? extends Annotation>> OPT_IN_ANNOTATIONS = Arrays.asList(
            DbUnitConfig.class, DbUnitPrep.class, DbUnitSetup.class, DbUnitExpected.class,
            DbUnitTearDown.class, DbUnitRowCountCheck.class);

    private AnnotatedTestOptIn()
    {
    }

    /**
     * Returns whether the test opts into the {@code org.dbunit.annotation} family.
     *
     * @param lookup Finds the annotations that apply to the test.
     * @param testClasses The test class and every class enclosing it whose fields count, for
     *            {@code @Nested} support.
     * @return {@code true} when the test carries an annotation that configures it, or a class
     *         in scope declares a {@code @DbUnitTester} or {@code @DbUnitTestCase} field.
     */
    public static boolean isOptedIn(final AnnotationLookup lookup,
            final Collection<Class<?>> testClasses)
    {
        for (final Class<? extends Annotation> annotationType : OPT_IN_ANNOTATIONS)
        {
            if (lookup.find(annotationType) != null)
            {
                return true;
            }
        }
        for (final Class<?> testClass : testClasses)
        {
            if (declaresMarkerField(testClass))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns whether {@code testClass} or a superclass declares a {@link DbUnitTester} or
     * {@link DbUnitTestCase} field.
     */
    private static boolean declaresMarkerField(final Class<?> testClass)
    {
        Class<?> current = testClass;
        while (current != null && current != Object.class)
        {
            for (final Field field : current.getDeclaredFields())
            {
                if (field.isAnnotationPresent(DbUnitTester.class)
                        || field.isAnnotationPresent(DbUnitTestCase.class))
                {
                    return true;
                }
            }
            current = current.getSuperclass();
        }
        return false;
    }
}