All Downloads are FREE. Search and download functionalities are using the official Maven repository.

patterntesting.runtime.PublicForTesting4Aspect.aj Maven / Gradle / Ivy

/*
 * Copyright (c) 2009-2019 by Oliver Boehm
 *
 * 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 orimplied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * (c)reated 18.03.2009 by oliver ([email protected])
 */
package patterntesting.runtime;

import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.SuppressAjWarnings;
import patterntesting.runtime.util.Assertions;
import patterntesting.runtime.util.JoinPointHelper;

/**
 * Methods and constructors marked as "@PublicForTesting" should be only called
 * by test methods or by the class itself. Test methods are recognized by
 * the @Test annotation (from JUnit) or @OnlyForTesting annotation.
 *
 * @author oliver
 * @version $Revision: 1.4 $
 * @since 18.03.2009
 */
public aspect PublicForTesting4Aspect {

    /**
     * JUnit4 test or setup/teardown methods.
     */
    private pointcut belowJUnit4Methods() :
        cflowbelow(execution(@org.junit.Test * *..*()))
        || cflowbelow(execution(@org.junit.Before * *..*()))
        || cflowbelow(execution(@org.junit.BeforeClass * *..*()))
        || cflowbelow(execution(@org.junit.After * *..*()))
        || cflowbelow(execution(@org.junit.AfterClass * *..*()))
        ;

    /**
     * JUnit test or setup/teardown methods and methods marked with 
     * @OnlyForTesting.
     */
    private pointcut belowTestMethods() :
        belowJUnit4Methods()
        || cflowbelow(execution(@patterntesting.annotation.check.ct.OnlyForTesting * *..*()))
        || cflowbelow(execution(@patterntesting.annotation.check.ct.OnlyForTesting *..new()))
        || cflowbelow(@within(patterntesting.annotation.check.ct.OnlyForTesting))
        ;

    /**
     * All methods and constructors annotated with "@PublicForTesting" should
     * be called only from test methods or from the class itself.
     */
    public pointcut applicationCode() :
        (call(@patterntesting.annotation.check.runtime.PublicForTesting * *..*(..))
            || call(@patterntesting.annotation.check.runtime.PublicForTesting *..new(..)))
        && !belowTestMethods()
        ;

    /**
     * We check the caller hierarchy after the pointcut "applicationCode()"
     * because otherwise the stacktrace from the failed assert points to
     * the line before the call (and not the call itself).
     * As a result the protected method is executed but you see the
     * AssertionError afterwards.
     */
    @SuppressAjWarnings({"adviceDidNotMatch"})
    after() : applicationCode()  {
        if (Assertions.ENABLED) {
            String caller = getCallerClassName();
            Signature sig = thisJoinPointStaticPart.getSignature();
            String self = sig.getDeclaringTypeName();
            assert self.equals(caller) :
                "only test methods or object itself may call "
                + JoinPointHelper.getAsShortString(thisJoinPoint);
        }
    }

    /**
     * The 4th stack element was experimentally guessed and my be wrong
     * in other circumstances. The 3rd stack element is this aspect
     * (PublicForTestingAspect) itself.
     *
     * @return the caller of the protected method (actually the caller of
     *         this aspect here)
     */
    private static String getCallerClassName() {
        Thread t = Thread.currentThread();
        StackTraceElement[] stacktrace = t.getStackTrace();
        return stacktrace[4].getClassName();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy