Package org.jboss.byteman.contrib.bmunit

Source Code of org.jboss.byteman.contrib.bmunit.BMUnitRunner

package org.jboss.byteman.contrib.bmunit;

import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;

/**
* Specialisation of the BlockJUnit4ClassRunner Runner class which can be attached to a text class
* using the @RunWith annotation. It ensures that Byteman rules are loaded and unloaded for tests
* which are annotated with an @Byteman annotation
*/
public class BMUnitRunner extends BlockJUnit4ClassRunner
{
    BMScript classSingleScriptAnnotation;
    BMScripts classMultiScriptAnnotation;
    BMRules classMultiRuleAnnotation;
    BMRule classSingleRuleAnnotation;
    Class<?> testKlazz;
    String loadDirectory;

    /**
     * Creates a BMUnitRunner to run test in {@code klass}
     *
     * @throws org.junit.runners.model.InitializationError
     *          if the test class is malformed.
     */
    public BMUnitRunner(Class<?> klass) throws InitializationError {
        super(klass);
        testKlazz = getTestClass().getJavaClass();
        classSingleScriptAnnotation = testKlazz.getAnnotation(BMScript.class);
        classMultiScriptAnnotation = testKlazz.getAnnotation(BMScripts.class);
        classMultiRuleAnnotation = testKlazz.getAnnotation(BMRules.class);
        classSingleRuleAnnotation = testKlazz.getAnnotation((BMRule.class));
        if (classMultiRuleAnnotation != null && classSingleRuleAnnotation != null) {
            throw new InitializationError("Use either BMRule or BMRules annotation but not both");
        }
        if (classMultiScriptAnnotation != null && classSingleScriptAnnotation != null) {
            throw new InitializationError("Use either BMScript or BMScripts annotation but not both");
        }
    }

    /*
     * this loads and unloads the class rules around each test. if we override childrenInvoker then
     * we can load and unload the class rules once only around all tests
    @Override
    protected Statement methodBlock(FrameworkMethod method) {
        // if we have a BMRules annotation on the test class then surround the method block
        // with calls to load and unload the per class rules
        final Statement original =  super.methodBlock(method);
        if (classSingleScriptAnnotation !=null) {
            final String name = computeBMRulesName(classSingleScriptAnnotation.value(), testKlazz);
            return new Statement() {
                public void evaluate() throws Throwable {
                    BMUnit.loadScriptFile(testKlazz, name, loadDirectory);
                    try {
                        original.evaluate();
                    } finally {
                        BMUnit.unloadScriptFile(testKlazz, name);
                    }
                }
            };
        } else {
            return original;
        }
    }
    */

    @Override
    protected Statement childrenInvoker(RunNotifier notifier) {
        Statement statement = super.childrenInvoker(notifier);
        // n.b. we add the wrapper code in reverse order to the preferred order of loading
        // as it works by wrapping around and so execution is  in reverse order to wrapping
        // i.e. this ensures that the class script rules get loaded before any rules specified
        // using BMRule(s) annotations
        statement = addClassSingleRuleLoader(statement, notifier);
        statement = addClassMultiRuleLoader(statement, notifier);
        statement = addClassSingleScriptLoader(statement, notifier);
        statement = addClassMultiScriptLoader(statement, notifier);
        return statement;
    }

    protected Statement addClassSingleScriptLoader(final Statement statement, RunNotifier notifier)
    {
        if (classSingleScriptAnnotation == null) {
            return statement;
        } else {
            final String name = computeBMRulesName(classSingleScriptAnnotation.value(), testKlazz);
            final RunNotifier fnotifier = notifier;
            final Description description = Description.createTestDescription(testKlazz, getName(), classSingleScriptAnnotation);
            final String loadDirectory = normaliseLoadDirectory(classSingleScriptAnnotation);
            return new Statement() {
                public void evaluate() throws Throwable {
                    try {
                        BMUnit.loadScriptFile(testKlazz, name, loadDirectory);
                        try {
                            statement.evaluate();
                        } finally {
                            try {
                                BMUnit.unloadScriptFile(testKlazz, name);
                            } catch (Exception e) {
                                fnotifier.fireTestFailure(new Failure(description, e));
                            }
                        }
                    } catch (Exception e) {
                        fnotifier.fireTestFailure(new Failure(description, e));
                    }
                }
            };
        }
    }

    protected Statement addClassMultiScriptLoader(final Statement statement, RunNotifier notifier)
    {
        if (classMultiScriptAnnotation == null) {
            return statement;
         } else {
            BMScript[] scriptAnnotations = classMultiScriptAnnotation.scripts();
            Statement result = statement;
            // note we iterate down here because we generate statements by wraparound
            // which means the the outer statement gets executed first
            for (int i = scriptAnnotations.length; i> 0; i--) {
                BMScript scriptAnnotation= scriptAnnotations[i - 1];
                final String name = computeBMRulesName(scriptAnnotation.value(), testKlazz);
                final RunNotifier fnotifier = notifier;
                final Description description = Description.createTestDescription(testKlazz, getName(), scriptAnnotation);
                final String loadDirectory = normaliseLoadDirectory(scriptAnnotation);
                final Statement nextStatement = result;
                result = new Statement() {
                    public void evaluate() throws Throwable {
                        try {
                            BMUnit.loadScriptFile(testKlazz, name, loadDirectory);
                            try {
                                nextStatement.evaluate();
                            } finally {
                                try {
                                    BMUnit.unloadScriptFile(testKlazz, name);
                                } catch (Exception e) {
                                    fnotifier.fireTestFailure(new Failure(description, e));
                                }
                            }
                        } catch (Exception e) {
                            fnotifier.fireTestFailure(new Failure(description, e));
                        }
                    }
                };
            }
            return result;
        }
    }

    protected Statement addClassMultiRuleLoader(final Statement statement, RunNotifier notifier)
    {
        if (classMultiRuleAnnotation == null) {
            return statement;
        } else {
            final String scriptText = constructScriptText(classMultiRuleAnnotation.rules());
            final RunNotifier fnotifier = notifier;
            final Description description = Description.createTestDescription(testKlazz, getName(), classMultiRuleAnnotation);
            return new Statement() {
                public void evaluate() throws Throwable {
                    try {
                        BMUnit.loadScriptText(testKlazz, null, scriptText);
                        try {
                            statement.evaluate();
                        } finally {
                            try {
                                BMUnit.unloadScriptText(testKlazz, null);
                            } catch (Exception e) {
                                fnotifier.fireTestFailure(new Failure(description, e));
                            }
                        }
                    } catch (Exception e) {
                        fnotifier.fireTestFailure(new Failure(description, e));
                    }
                }
            };
        }
    }

    protected Statement addClassSingleRuleLoader(final Statement statement, RunNotifier notifier)
    {
        if (classSingleRuleAnnotation == null) {
            return statement;
        } else {
            final String scriptText = constructScriptText(new BMRule[] {classSingleRuleAnnotation});
            final RunNotifier fnotifier = notifier;
            final Description description = Description.createTestDescription(testKlazz, getName(), classSingleRuleAnnotation);
            return new Statement() {
                public void evaluate() throws Throwable {
                    try {
                        BMUnit.loadScriptText(testKlazz, null, scriptText);
                        try {
                            statement.evaluate();
                        } finally {
                            try {
                                BMUnit.unloadScriptText(testKlazz, null);
                            } catch (Exception e) {
                                fnotifier.fireTestFailure(new Failure(description, e));
                            }
                        }
                    } catch (Exception e) {
                        fnotifier.fireTestFailure(new Failure(description, e));
                    }
                }
            };
        }
    }

    @Override
    protected Statement methodInvoker(FrameworkMethod method, Object test)
    {
        Statement statement = super.methodInvoker(method, test);
        // n.b. we add the wrapper code in reverse order to the preferred order of loading
        // as it works by wrapping around and so execution is in reverse order to wrapping
        // i.e. this ensures that the method script rules get loaded before any rules specified
        // using BMRule(s) annotations
        statement = addMethodSingleRuleLoader(statement, method);
        statement = addMethodMultiRuleLoader(statement, method);
        statement = addMethodSingleScriptLoader(statement, method);
        statement = addMethodMultiScriptLoader(statement, method);
        return statement;
    }

    /**
     * wrap the test method execution statement with the necessary load and unload calls if it has
     * a BMScript annotation
     * @param statement
     * @param method
     * @return
     */
    protected Statement addMethodSingleScriptLoader(final Statement statement, FrameworkMethod method)
    {
        BMScript annotation = method.getAnnotation(BMScript.class);
        if (annotation == null) {
            return statement;
        } else {
            // ensure we always have an actual name here instead of null because using
            // null will clash with the name used for looking up rules when the clas
            // has a BMRules annotation
            final String name = computeBMRulesName(annotation.value(), method);
            final String loadDirectory = computeLoadDirectory(annotation.dir(), this.loadDirectory);
            return new Statement() {
                public void evaluate() throws Throwable {
                    BMUnit.loadScriptFile(testKlazz, name, loadDirectory);
                    try {
                        statement.evaluate();
                    } finally {
                        BMUnit.unloadScriptFile(testKlazz, name);
                    }
                }
            };
        }
    }

    /**
     * wrap the test method execution statement with the necessary load and unload calls if it has
     * a BMScripts annotation
     * @param statement
     * @param method
     * @return
     */
    protected Statement addMethodMultiScriptLoader(final Statement statement, FrameworkMethod method)
    {
        BMScripts scriptsAnnotation = method.getAnnotation(BMScripts.class);
        if (scriptsAnnotation == null) {
            return statement;
        } else {
            BMScript[] scriptAnnotations = scriptsAnnotation.scripts();
            Statement result = statement;
            // note we iterate down here because we generate statements by wraparound
            // which means the the outer statement gets executed first
            for (int i = scriptAnnotations.length; i> 0; i--) {
                BMScript scriptAnnotation = scriptAnnotations[i - 1];
                final Statement nextStatement = result;
                // ensure we always have an actual name here instead of null because using
                // null will clash with the name used for looking up rules when the clas
                // has a BMRules annotation
                final String name = computeBMRulesName(scriptAnnotation.value(), method);
                final String loadDirectory = computeLoadDirectory(scriptAnnotation.dir(), this.loadDirectory);
                result = new Statement() {
                    public void evaluate() throws Throwable {
                        BMUnit.loadScriptFile(testKlazz, name, loadDirectory);
                        try {
                            nextStatement.evaluate();
                        } finally {
                            BMUnit.unloadScriptFile(testKlazz, name);
                        }
                    }
                };
            }
            return result;
        }
    }

    /**
     * wrap the test method execution statement with the necessary load and unload calls if it has
     * a BMRules annotation
     * @param statement
     * @param method
     * @return
     */
    protected Statement addMethodMultiRuleLoader(final Statement statement, FrameworkMethod method)
    {
        BMRules annotation = method.getAnnotation(BMRules.class);
        if (annotation == null) {
            return statement;
        } else {
            final String name = method.getName();
            final String script = constructScriptText(annotation.rules());
            return new Statement() {
                public void evaluate() throws Throwable {
                    BMUnit.loadScriptText(testKlazz, name, script);
                    try {
                        statement.evaluate();
                    } finally {
                        BMUnit.unloadScriptText(testKlazz, name);
                    }
                }
            };
        }
    }

    /**
     * wrap the test method execution statement with the necessary load and unload calls if it has
     * a BMRule annotation
     * @param statement
     * @param method
     * @return
     */
    protected Statement addMethodSingleRuleLoader(final Statement statement, FrameworkMethod method)
    {
        BMRule annotation = method.getAnnotation(BMRule.class);
        if (annotation == null) {
            return statement;
        } else {
            final String name = method.getName();
            final String script = constructScriptText(new BMRule[] {annotation });
            return new Statement() {
                public void evaluate() throws Throwable {
                    BMUnit.loadScriptText(testKlazz, name, script);
                    try {
                        statement.evaluate();
                    } finally {
                        BMUnit.unloadScriptText(testKlazz, name);
                    }
                }
            };
        }
    }

    public String normaliseLoadDirectory(BMScript annotation)
    {
        String loadDirectory = annotation.dir();
        if (loadDirectory != null && loadDirectory.length() > 0) {
            return loadDirectory;
        }

        return null;
    }

    /**
     * construct the text of a rule script from a  set of BMRule annotations
     * @param bmRules
     * @return
     */
    protected String constructScriptText(BMRule[] bmRules) {
        StringBuilder builder = new StringBuilder();
        builder.append("# BMUnit autogenerated script ");
        for (BMRule bmRule : bmRules) {
            builder.append("\nRULE ");
            builder.append(bmRule.name());
            if (bmRule.isInterface()) {
                builder.append("\nINTERFACE ");
            } else {
                builder.append("\nCLASS ");
            }
            if (bmRule.isOverriding()) {
                builder.append("^");
            }
            builder.append(bmRule.targetClass());
            builder.append("\nMETHOD ");
            builder.append(bmRule.targetMethod());
            String location = bmRule.targetLocation();
            if (location  !=  null && location.length() > 0) {
                builder.append("\nAT ");
                builder.append(location);
            }
            String helper = bmRule.helper();
            if (helper  !=  null && helper.length() > 0) {
                builder.append("\nHELPER ");
                builder.append(helper);
            }
            builder.append("\nIF ");
            builder.append(bmRule.condition());
            builder.append("\nDO ");
            builder.append(bmRule.action());
            builder.append("\nENDRULE\n");
        }
        return builder.toString();
    }

    /**
     * method which computes the name of the BMRules file for a method test if it is not supplied in the
     * method annotation
     * @param name the value supplied in the annotation or "" if it has been defaulted
     * @param method the Framework method annotated with an @BMRules annotation
     * @return by default this returns the annotation value or the the bare method name if the annotation
     * value is null or empty
     */
    protected String computeBMRulesName(String name, FrameworkMethod method)
    {
        // if the annotation has a real name  then use  it
        if (name != null && name.length() > 0) {
            return name;
        }
        // use the method name

        return method.getName();
    }

    /**
     * method which computes the name of the BMRules file for a test class if it is not supplied in the
     * class annotation
     * @param name the value supplied in the annotation or "" if it has been defaulted
     * @param testClass the test class annotated with an @BMRules annotation
     * @return by default this returns the annotation value or null if the annotation value is null or empty.
     */
    protected String computeBMRulesName(String name, Class<?> testClass)
    {
        if (name != null && name.length() > 0) {
            return name;
        }

        return null;
    }

    protected String computeLoadDirectory(String methodAnnotationDir, String classAnnotationDir)
    {
        if (methodAnnotationDir != null && methodAnnotationDir.length() > 0) {
            return methodAnnotationDir;
        }
        return classAnnotationDir;
    }
}
TOP

Related Classes of org.jboss.byteman.contrib.bmunit.BMUnitRunner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.