com.netflix.nicobar.groovy2.plugin.Groovy2PluginTest.java Source code

Java tutorial

Introduction

Here is the source code for com.netflix.nicobar.groovy2.plugin.Groovy2PluginTest.java

Source

/*
 *
 *  Copyright 2013 Netflix, Inc.
 *
 *     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.
 *
 */
package com.netflix.nicobar.groovy2.plugin;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail;

import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;

import org.apache.commons.io.FileUtils;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import com.netflix.nicobar.core.archive.JarScriptArchive;
import com.netflix.nicobar.core.archive.PathScriptArchive;
import com.netflix.nicobar.core.archive.ScriptArchive;
import com.netflix.nicobar.core.archive.ScriptModuleSpec;
import com.netflix.nicobar.core.execution.HystrixScriptModuleExecutor;
import com.netflix.nicobar.core.execution.ScriptModuleExecutable;
import com.netflix.nicobar.core.module.ScriptModule;
import com.netflix.nicobar.core.module.ScriptModuleLoader;
import com.netflix.nicobar.core.module.ScriptModuleUtils;
import com.netflix.nicobar.core.plugin.BytecodeLoadingPlugin;
import com.netflix.nicobar.core.plugin.ScriptCompilerPluginSpec;
import com.netflix.nicobar.groovy2.internal.compile.Groovy2Compiler;
import com.netflix.nicobar.groovy2.testutil.GroovyTestResourceUtil;
import com.netflix.nicobar.groovy2.testutil.GroovyTestResourceUtil.TestScript;

/**
 * Integration tests for the Groovy2 language plugin
 *
 * @author James Kojo
 * @author Vasanth Asokan
 */
public class Groovy2PluginTest {

    private static final String GROOVY2_COMPILER_PLUGIN = Groovy2CompilerPlugin.class.getName();
    private static final Random RANDOM = new Random(System.currentTimeMillis());
    private Path uncompilableArchiveDir;
    private Path uncompilableScriptRelativePath;

    @BeforeClass
    public void setup() throws Exception {
        //Module.setModuleLogger(new StreamModuleLogger(System.err));
        uncompilableArchiveDir = Files.createTempDirectory(Groovy2PluginTest.class.getSimpleName() + "_");
        FileUtils.forceDeleteOnExit(uncompilableArchiveDir.toFile());
        uncompilableScriptRelativePath = Paths.get("Uncompilable.groovy");
        byte[] randomBytes = new byte[1024];
        RANDOM.nextBytes(randomBytes);
        Files.write(uncompilableArchiveDir.resolve(uncompilableScriptRelativePath), randomBytes);
    }

    @Test
    public void testLoadSimpleScript() throws Exception {
        ScriptModuleLoader moduleLoader = createGroovyModuleLoader().build();
        // create a new script archive consisting of HellowWorld.groovy and add it the loader.
        // Declares a dependency on the Groovy2RuntimeModule.
        Path scriptRootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.HELLO_WORLD);
        ScriptArchive scriptArchive = new PathScriptArchive.Builder(scriptRootPath).setRecurseRoot(false)
                .addFile(TestScript.HELLO_WORLD.getScriptPath())
                .setModuleSpec(createGroovyModuleSpec(TestScript.HELLO_WORLD.getModuleId()).build()).build();
        moduleLoader.updateScriptArchives(Collections.singleton(scriptArchive));

        // locate the class file in the module and execute it
        ScriptModule scriptModule = moduleLoader.getScriptModule(TestScript.HELLO_WORLD.getModuleId());
        Class<?> clazz = findClassByName(scriptModule, TestScript.HELLO_WORLD);
        assertGetMessage(clazz, "Hello, World!");
    }

    @Test
    public void testLoadScriptWithInterface() throws Exception {
        ScriptModuleLoader moduleLoader = createGroovyModuleLoader().build();
        Path scriptRootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.IMPLEMENTS_INTERFACE);
        ScriptArchive scriptArchive = new PathScriptArchive.Builder(scriptRootPath).setRecurseRoot(false)
                .addFile(TestScript.IMPLEMENTS_INTERFACE.getScriptPath())
                .setModuleSpec(createGroovyModuleSpec(TestScript.IMPLEMENTS_INTERFACE.getModuleId()).build())
                .build();
        moduleLoader.updateScriptArchives(Collections.singleton(scriptArchive));

        // locate the class file in the module and execute it via the executor
        ScriptModuleExecutable<String> executable = new ScriptModuleExecutable<String>() {
            @SuppressWarnings({ "rawtypes", "unchecked" })
            @Override
            public String execute(ScriptModule scriptModule) throws Exception {
                Class<Callable> callable = (Class<Callable>) ScriptModuleUtils.findAssignableClass(scriptModule,
                        Callable.class);
                assertNotNull(callable, "couldn't find Callable for module " + scriptModule.getModuleId());
                Callable<String> instance = callable.newInstance();
                String result = instance.call();
                return result;
            }
        };
        HystrixScriptModuleExecutor<String> executor = new HystrixScriptModuleExecutor<String>(
                "TestModuleExecutor");
        List<String> results = executor.executeModules(
                Collections.singletonList(TestScript.IMPLEMENTS_INTERFACE.getModuleId()), executable, moduleLoader);
        assertEquals(results, Collections.singletonList("I'm a Callable<String>"));
    }

    /**
     * Test loading/executing a script which has a dependency on a library
     */
    @Test
    public void testLoadScriptWithLibrary() throws Exception {
        ScriptModuleLoader moduleLoader = createGroovyModuleLoader().build();
        Path dependsOnARootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.DEPENDS_ON_A);

        ScriptArchive dependsOnAArchive = new PathScriptArchive.Builder(dependsOnARootPath).setRecurseRoot(false)
                .addFile(TestScript.DEPENDS_ON_A.getScriptPath())
                .setModuleSpec(createGroovyModuleSpec(TestScript.DEPENDS_ON_A.getModuleId())
                        .addModuleDependency(TestScript.LIBRARY_A.getModuleId()).build())
                .build();
        Path libARootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.LIBRARY_A);
        ScriptArchive libAArchive = new PathScriptArchive.Builder(libARootPath).setRecurseRoot(false)
                .addFile(TestScript.LIBRARY_A.getScriptPath())
                .setModuleSpec(createGroovyModuleSpec(TestScript.LIBRARY_A.getModuleId()).build()).build();
        // load them in dependency order to make sure that transitive dependency resolution is working
        moduleLoader.updateScriptArchives(
                new LinkedHashSet<ScriptArchive>(Arrays.asList(dependsOnAArchive, libAArchive)));

        // locate the class file in the module and execute it
        ScriptModule scriptModule = moduleLoader.getScriptModule(TestScript.DEPENDS_ON_A.getModuleId());
        Class<?> clazz = findClassByName(scriptModule, TestScript.DEPENDS_ON_A);
        assertGetMessage(clazz, "DepondOnA: Called LibraryA and got message:'I'm LibraryA!'");
    }

    /**
     * Test loading/executing a script which has a dependency on a library
     */
    @Test
    public void testReloadLibrary() throws Exception {
        ScriptModuleLoader moduleLoader = createGroovyModuleLoader().build();
        Path dependsOnARootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.DEPENDS_ON_A);
        ScriptArchive dependsOnAArchive = new PathScriptArchive.Builder(dependsOnARootPath).setRecurseRoot(false)
                .addFile(TestScript.DEPENDS_ON_A.getScriptPath())
                .setModuleSpec(createGroovyModuleSpec(TestScript.DEPENDS_ON_A.getModuleId())
                        .addModuleDependency(TestScript.LIBRARY_A.getModuleId()).build())
                .build();

        Path libARootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.LIBRARY_A);
        ScriptArchive libAArchive = new PathScriptArchive.Builder(libARootPath).setRecurseRoot(false)
                .addFile(TestScript.LIBRARY_A.getScriptPath())
                .setModuleSpec(createGroovyModuleSpec(TestScript.LIBRARY_A.getModuleId()).build()).build();

        moduleLoader.updateScriptArchives(
                new LinkedHashSet<ScriptArchive>(Arrays.asList(dependsOnAArchive, libAArchive)));

        // reload the library with version 2
        Path libAV2RootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.LIBRARY_AV2);
        ScriptArchive libAV2Archive = new PathScriptArchive.Builder(libAV2RootPath).setRecurseRoot(false)
                .addFile(TestScript.LIBRARY_AV2.getScriptPath())
                .setModuleSpec(createGroovyModuleSpec(TestScript.LIBRARY_A.getModuleId()).build()).build();
        moduleLoader.updateScriptArchives(Collections.singleton(libAV2Archive));

        // find the dependent and execute it
        ScriptModule scriptModuleDependOnA = moduleLoader.getScriptModule(TestScript.DEPENDS_ON_A.getModuleId());
        Class<?> clazz = findClassByName(scriptModuleDependOnA, TestScript.DEPENDS_ON_A);
        assertGetMessage(clazz, "DepondOnA: Called LibraryA and got message:'I'm LibraryA V2!'");
    }

    /**
     * Tests that if we deploy an uncompilable dependency, the dependents continue to function
     */
    @Test
    public void testDeployBadDependency() throws Exception {
        ScriptModuleLoader moduleLoader = createGroovyModuleLoader().build();
        Path dependsOnARootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.DEPENDS_ON_A);

        ScriptArchive dependsOnAArchive = new PathScriptArchive.Builder(dependsOnARootPath).setRecurseRoot(false)
                .addFile(TestScript.DEPENDS_ON_A.getScriptPath())
                .setModuleSpec(createGroovyModuleSpec(TestScript.DEPENDS_ON_A.getModuleId())
                        .addModuleDependency(TestScript.LIBRARY_A.getModuleId()).build())
                .build();
        Path libARootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.LIBRARY_A);
        ScriptArchive libAArchive = new PathScriptArchive.Builder(libARootPath).setRecurseRoot(false)
                .addFile(TestScript.LIBRARY_A.getScriptPath())
                .setModuleSpec(createGroovyModuleSpec(TestScript.LIBRARY_A.getModuleId()).build()).build();

        moduleLoader.updateScriptArchives(
                new LinkedHashSet<ScriptArchive>(Arrays.asList(dependsOnAArchive, libAArchive)));
        assertEquals(moduleLoader.getAllScriptModules().size(), 2);

        // attempt reload library-A with invalid groovy
        libAArchive = new PathScriptArchive.Builder(uncompilableArchiveDir).setRecurseRoot(false)
                .addFile(uncompilableScriptRelativePath)
                .setModuleSpec(createGroovyModuleSpec(TestScript.LIBRARY_A.getModuleId()).build()).build();
        moduleLoader.updateScriptArchives(new LinkedHashSet<ScriptArchive>(Arrays.asList(libAArchive)));
        assertEquals(moduleLoader.getAllScriptModules().size(), 2);

        // find the dependent and execute it
        ScriptModule scriptModuleDependOnA = moduleLoader.getScriptModule(TestScript.DEPENDS_ON_A.getModuleId());
        Class<?> clazz = findClassByName(scriptModuleDependOnA, TestScript.DEPENDS_ON_A);
        assertGetMessage(clazz, "DepondOnA: Called LibraryA and got message:'I'm LibraryA!'");
    }

    /**
     * Test loading a module which is composed of several interdependent scripts.
     * InternalDepdencyA->InternalDepdencyB-InternalDepdencyC->InternalDepdencyD
     */
    @Test
    public void testLoadScriptWithInternalDependencies() throws Exception {
        ScriptModuleLoader moduleLoader = createGroovyModuleLoader().build();

        Path scriptRootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.INTERNAL_DEPENDENCY_A);
        ScriptArchive scriptArchive = new PathScriptArchive.Builder(scriptRootPath).setRecurseRoot(false)
                .addFile(Paths.get("InternalDependencyB.groovy")).addFile(Paths.get("InternalDependencyA.groovy"))
                .addFile(Paths.get("InternalDependencyD.groovy"))
                .addFile(Paths.get("subpackage/InternalDependencyC.groovy"))
                .setModuleSpec(createGroovyModuleSpec(TestScript.INTERNAL_DEPENDENCY_A.getModuleId()).build())
                .build();
        moduleLoader.updateScriptArchives(Collections.singleton(scriptArchive));

        // locate the class file in the module and execute it
        ScriptModule scriptModule = moduleLoader.getScriptModule(TestScript.INTERNAL_DEPENDENCY_A.getModuleId());
        Class<?> clazz = findClassByName(scriptModule, TestScript.INTERNAL_DEPENDENCY_A);
        assertGetMessage(clazz,
                "I'm A.  Called B and got: I'm B. Called C and got: I'm C. Called D and got: I'm D.");
    }

    @Test
    public void testMixedModule() throws Exception {
        ScriptModuleLoader.Builder moduleLoaderBuilder = createGroovyModuleLoader();
        ScriptModuleLoader loader = moduleLoaderBuilder
                .addPluginSpec(new ScriptCompilerPluginSpec.Builder(BytecodeLoadingPlugin.PLUGIN_ID)
                        .withPluginClassName(BytecodeLoadingPlugin.class.getName()).build())
                .build();
        Path jarPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.MIXED_MODULE)
                .resolve(TestScript.MIXED_MODULE.getScriptPath());
        ScriptArchive archive = new JarScriptArchive.Builder(jarPath).build();
        loader.updateScriptArchives(Collections.singleton(archive));
        ScriptModule scriptModule = loader.getScriptModule(TestScript.MIXED_MODULE.getModuleId());
        Class<?> clazz = findClassByName(scriptModule, TestScript.MIXED_MODULE);
        Object instance = clazz.newInstance();
        Method method = clazz.getMethod("execute");
        String message = (String) method.invoke(instance);
        assertEquals(message, "Hello Mixed Module!");

        // Verify groovy class
        clazz = findClassByName(scriptModule, "com.netflix.nicobar.test.HelloBytecode");
        method = clazz.getMethod("execute");
        message = (String) method.invoke(clazz.newInstance());
        assertEquals(message, "Hello Bytecode!");
    }

    /**
     * Test loading/executing a script with app package import filters,
     * and which is dependent a library.
     *
     */
    @Test
    public void testLoadScriptWithAppImports() throws Exception {
        ScriptModuleLoader moduleLoader = createGroovyModuleLoader().build();
        Path dependsOnARootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.DEPENDS_ON_A);

        ScriptArchive dependsOnAArchive = new PathScriptArchive.Builder(dependsOnARootPath).setRecurseRoot(false)
                .addFile(TestScript.DEPENDS_ON_A.getScriptPath())
                .setModuleSpec(createGroovyModuleSpec(TestScript.DEPENDS_ON_A.getModuleId())
                        .addModuleDependency(TestScript.LIBRARY_A.getModuleId()).addAppImportFilter("test").build())
                .build();
        Path libARootPath = GroovyTestResourceUtil.findRootPathForScript(TestScript.LIBRARY_A);
        ScriptArchive libAArchive = new PathScriptArchive.Builder(libARootPath).setRecurseRoot(false)
                .addFile(TestScript.LIBRARY_A.getScriptPath())
                .setModuleSpec(createGroovyModuleSpec(TestScript.LIBRARY_A.getModuleId()).addAppImportFilter("test")
                        .build())
                .build();
        // load them in dependency order to make sure that transitive dependency resolution is working
        moduleLoader.updateScriptArchives(
                new LinkedHashSet<ScriptArchive>(Arrays.asList(dependsOnAArchive, libAArchive)));

        // locate the class file in the module and execute it
        ScriptModule scriptModule = moduleLoader.getScriptModule(TestScript.DEPENDS_ON_A.getModuleId());
        Class<?> clazz = findClassByName(scriptModule, TestScript.DEPENDS_ON_A);
        assertGetMessage(clazz, "DepondOnA: Called LibraryA and got message:'I'm LibraryA!'");
    }

    /**
     * Create a module loader this is wired up with the groovy compiler plugin
     */
    private ScriptModuleLoader.Builder createGroovyModuleLoader() throws Exception {
        // create the groovy plugin spec. this plugin specified a new module and classloader called "Groovy2Runtime"
        // which contains the groovy-all-2.1.6.jar and the nicobar-groovy2 project.
        ScriptCompilerPluginSpec pluginSpec = new ScriptCompilerPluginSpec.Builder(
                Groovy2Compiler.GROOVY2_COMPILER_ID).addRuntimeResource(GroovyTestResourceUtil.getGroovyRuntime())
                        .addRuntimeResource(GroovyTestResourceUtil.getGroovyPluginLocation())
                        // hack to make the gradle build work. still doesn't seem to properly instrument the code
                        // should probably add a classloader dependency on the system classloader instead
                        .addRuntimeResource(GroovyTestResourceUtil.getCoberturaJar(getClass().getClassLoader()))
                        .withPluginClassName(GROOVY2_COMPILER_PLUGIN).build();

        // create and start the builder with the plugin
        return new ScriptModuleLoader.Builder().addPluginSpec(pluginSpec);
    }

    /**
     * Create a module spec builder with pre-populated groovy dependency
     */
    private ScriptModuleSpec.Builder createGroovyModuleSpec(String moduleId) {
        return new ScriptModuleSpec.Builder(moduleId).addCompilerPluginId(Groovy2CompilerPlugin.PLUGIN_ID);
    }

    private Class<?> findClassByName(ScriptModule scriptModule, TestScript testScript) {
        assertNotNull(scriptModule, "Missing scriptModule for  " + testScript);
        return findClassByName(scriptModule, testScript.getClassName());
    }

    private Class<?> findClassByName(ScriptModule scriptModule, String className) {
        Set<Class<?>> classes = scriptModule.getLoadedClasses();
        for (Class<?> clazz : classes) {
            if (clazz.getName().equals(className)) {
                return clazz;
            }
        }
        fail("couldn't find class " + className);
        return null;
    }

    private void assertGetMessage(Class<?> targetClass, String expectedMessage) throws Exception {
        Object instance = targetClass.newInstance();
        Method method = targetClass.getMethod("getMessage");
        String message = (String) method.invoke(instance);
        assertEquals(message, expectedMessage);
    }
}