com.ibm.jaggr.core.impl.modulebuilder.javascript.RequireExpansionCompilerPassTest.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.jaggr.core.impl.modulebuilder.javascript.RequireExpansionCompilerPassTest.java

Source

/*
 * (C) Copyright 2012, IBM Corporation
 *
 * 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.ibm.jaggr.core.impl.modulebuilder.javascript;

import com.ibm.jaggr.core.IAggregator;
import com.ibm.jaggr.core.deps.IDependencies;
import com.ibm.jaggr.core.deps.ModuleDeps;
import com.ibm.jaggr.core.options.IOptions;
import com.ibm.jaggr.core.test.TestUtils;
import com.ibm.jaggr.core.util.Features;

import com.google.common.collect.HashMultimap;
import com.google.javascript.jscomp.CompilationLevel;
import com.google.javascript.jscomp.Compiler;
import com.google.javascript.jscomp.Compiler.CodeBuilder;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.CustomPassExecutionTime;
import com.google.javascript.jscomp.JSSourceFile;
import com.google.javascript.rhino.Node;

import org.apache.commons.lang3.mutable.MutableBoolean;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;

public class RequireExpansionCompilerPassTest extends EasyMock {

    private static Map<String, String[]> declaredDependencies = new HashMap<String, String[]>();
    private static String placeHolder0 = String.format(JavaScriptBuildRenderer.REQUIRE_EXPANSION_PLACEHOLDER_FMT,
            0);
    private static String placeHolder1 = String.format(JavaScriptBuildRenderer.REQUIRE_EXPANSION_PLACEHOLDER_FMT,
            1);

    static {
        declaredDependencies.put("foo", new String[] { "bar" });
        declaredDependencies.put("dependsOnBar", new String[] { "bar" });
        declaredDependencies.put("dependsOnModule", new String[] { "module" });
        declaredDependencies.put("x/y", new String[] { "x/y/z", "foo" });

        declaredDependencies.put("bar", new String[] { "a/b" });
        declaredDependencies.put("a/c", new String[] { "c/d" });
        declaredDependencies.put("has1", new String[] { "dep1" });
        declaredDependencies.put("has2", new String[] { "dep2" });
    }

    private IAggregator mockAggregator;
    private IDependencies mockDependencies = createMock(IDependencies.class);
    private String moduleName = "test";

    @Before
    public void setUp() throws Exception {
        mockAggregator = TestUtils.createMockAggregator();
        expect(mockAggregator.getDependencies()).andReturn(mockDependencies).anyTimes();
        expect(mockDependencies.getDelcaredDependencies((String) anyObject()))
                .andAnswer(new IAnswer<List<String>>() {
                    public List<String> answer() throws Throwable {
                        String name = (String) getCurrentArguments()[0];
                        String[] result = declaredDependencies.get(name);
                        return result != null ? Arrays.asList(result) : null;
                    }
                }).anyTimes();
        expect(mockDependencies.getLastModified()).andReturn(0L).anyTimes();
        replay(mockAggregator);
        replay(mockDependencies);
    }

    @Test
    public void testRequireExpansion() throws Exception {
        List<ModuleDeps> expanded = new ArrayList<ModuleDeps>();
        MutableBoolean hasExpandableRequires = new MutableBoolean(false);
        RequireExpansionCompilerPass pass = new RequireExpansionCompilerPass(mockAggregator, new Features(), null,
                expanded, hasExpandableRequires, true, null, false, null);

        String code, output;

        // Ensure require list is modified
        code = "require([\"foo\"],function(foo){});";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\",\"" + placeHolder0 + "\"]"));
        Assert.assertEquals(1, expanded.size());
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());
        Assert.assertTrue(hasExpandableRequires.getValue());

        // Ensure require list is modified
        hasExpandableRequires.setValue(false);
        code = "require([\"foo\"]);";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\",\"" + placeHolder0 + "\"]"));
        Assert.assertEquals(1, expanded.size());
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());
        Assert.assertTrue(hasExpandableRequires.getValue());

        // Ensure require list is NOT modified
        hasExpandableRequires.setValue(false);
        pass = new RequireExpansionCompilerPass(mockAggregator, new Features(), null, expanded,
                hasExpandableRequires, false, null, false, null);
        code = "require([\"foo\"]);";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals(code, output);
        Assert.assertEquals(0, expanded.size());
        Assert.assertTrue(hasExpandableRequires.getValue());

        // Ensure hasExpandableRequires is false
        hasExpandableRequires.setValue(false);
        pass = new RequireExpansionCompilerPass(mockAggregator, new Features(), null, expanded,
                hasExpandableRequires, false, null, false, null);
        code = "define([\"foo\"], function(foo) {});";
        Assert.assertFalse(hasExpandableRequires.getValue());

        // Ensure only array literals are expanded
        pass = new RequireExpansionCompilerPass(mockAggregator, new Features(), null, expanded,
                new MutableBoolean(false), true, null, false, null);
        code = "require(\"foo\");";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals(code, output);

        // Ensure variables are not modified
        code = "require([\"foo\", jsvar])";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\",jsvar,\"" + placeHolder0 + "\"]"));
        Assert.assertEquals(1, expanded.size());
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        // test with compound strings
        code = "require([\"foo\" + jsvar])";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\"+jsvar]"));

        code = "require([\"foo\" + \"bar\"])";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\"+\"bar\"]"));

        code = "require([\"foo\", \"a\"+\"b\", \"x/y\"])";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\",\"a\"+\"b\",\"x/y\",\"" + placeHolder0 + "\"]"));
        Assert.assertEquals(1, expanded.size());
        System.out.println(expanded.get(0).keySet());
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b", "x/y/z" })),
                expanded.get(0).getModuleIds());

        // Ensure relative paths are resolved based on module name
        moduleName = "a/a";
        code = "require([\"foo\",\"./c\"]);";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\",\"./c\",\"" + placeHolder0 + "\"]"));
        Assert.assertEquals(1, expanded.size());
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b", "c/d" })),
                expanded.get(0).getModuleIds());
        moduleName = "test";

        // Ensure enclosing dependencies not expanded
        code = "define([\"bar\"],function(bar){require([\"foo\",\"a/b\"])});";
        moduleName = "dependsOnBar";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals(code, output);

        // No encloding dependencies
        moduleName = "dependsOnModule";
        code = "define([\"module\"],function(bar){require([\"foo\"]);});";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\",\"" + placeHolder0 + "\"]"));
        Assert.assertEquals(1, expanded.size());
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        // multiple require calls
        code = "define([\"module\"],function(bar){require([\"foo\"]); var abc = 123; require([\"x/y\"]);});";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\",\"" + placeHolder0 + "\"]"));
        Assert.assertTrue(output.contains("[\"x/y\",\"" + placeHolder1 + "\"]"));
        Assert.assertEquals(2, expanded.size());
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "foo", "bar", "a/b", "x/y/z" })),
                expanded.get(1).getModuleIds());

        // Enable development mode and make sure a RuntimeDependencyVerificationException
        // is thrown if the dependency list specified in the code does not match
        // the dependency list returned by IDependencies.getDeclaredDependencies()
        // for the module.

        // First, enable development mode
        mockAggregator.getOptions().setOption(IOptions.DEVELOPMENT_MODE, true);
        mockAggregator.getOptions().setOption(IOptions.VERIFY_DEPS, true);

        // this test should not throw because the code matches the dependencies
        code = "define([\"module\"],function(bar){require([\"foo\"]);});";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\",\"" + placeHolder0 + "\"]"));

        // This test should throw because the code doesn't match the dependencies
        boolean exceptionThrown = false;
        code = "define([\"module\",\"another\"],function(bar){require([\"foo\"]);});";
        try {
            output = runPass(pass, code);
            System.out.println(output);
        } catch (RuntimeDependencyVerificationException e) {
            exceptionThrown = true;
        }
        Assert.assertTrue("RuntimeDependencyVerificationException not thrown", exceptionThrown);

        // This test verifies that relative dependency paths are resolved correctly
        // Will throw RuntimeDependencyVerificationException if relative modules
        // specified in define are not normalized and resolved to the absolute paths
        // specified in the dependency map for module x/y.
        moduleName = "x/y";
        code = "define([\"./y/z\",\"../foo\"],function(bar){require([\"foo\"]);});";
        output = runPass(pass, code);
        System.out.println(output);

        moduleName = "dependsOnModule";
        code = "define([\"module\",\"another\"],function(bar){require([\"foo\"]);});";

        // Assert that both development mode and verify deps need to be enabled
        // for an exception to be thrown
        mockAggregator.getOptions().setOption(IOptions.DEVELOPMENT_MODE, true);
        mockAggregator.getOptions().setOption(IOptions.VERIFY_DEPS, false);
        output = runPass(pass, code);
        mockAggregator.getOptions().setOption(IOptions.DEVELOPMENT_MODE, false);
        mockAggregator.getOptions().setOption(IOptions.VERIFY_DEPS, true);
        output = runPass(pass, code);
        mockAggregator.getOptions().setOption(IOptions.DEVELOPMENT_MODE, false);
        mockAggregator.getOptions().setOption(IOptions.VERIFY_DEPS, false);
        output = runPass(pass, code);

    }

    private static final List<JSSourceFile> externs = Collections.emptyList();

    /*
     * Ensure that arrays of strings are not mutated into expressions of the form
     * "abc bcd cde".split(" ") when the require list expansion placeholder
     * module name is inserted at the end of the array list.
     */
    @Test
    public void testArrayMutation() throws Exception {
        String code = "define([\"module\"],function(bar){require([\"foo\", \"abc\", \"bcd\", \"cde\", \"def\", \"efg\", \"fgh\", \"ghi\"]);});";
        JSSourceFile sf = JSSourceFile.fromCode("test", code);
        List<JSSourceFile> sources = new ArrayList<JSSourceFile>();
        sources.add(sf);
        Compiler compiler = new Compiler();
        CompilerOptions compiler_options = new CompilerOptions();
        CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(compiler_options);
        compiler.compile(externs, sources, compiler_options);
        String output = compiler.toSource();
        System.out.println(output);
        // verfiy that array mutation occurs
        Assert.assertTrue(output.endsWith("fgh ghi\".split(\" \"))});"));

        List<ModuleDeps> expanded = new ArrayList<ModuleDeps>();
        RequireExpansionCompilerPass pass = new RequireExpansionCompilerPass(mockAggregator, new Features(), null,
                expanded, new MutableBoolean(false), true, null, false, null);
        compiler = new Compiler();
        compiler_options = new CompilerOptions();
        CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(compiler_options);
        compiler_options.customPasses = HashMultimap.create();
        compiler_options.customPasses.put(CustomPassExecutionTime.BEFORE_CHECKS, pass);
        compiler.compile(externs, sources, compiler_options);
        output = compiler.toSource();
        System.out.println(output);
        // Verify that array mutation doesn't occur
        Assert.assertFalse(output.contains(".split("));
        Assert.assertTrue(output.endsWith("\"ghi\",\"" + placeHolder0 + "\"])});"));
    }

    @Test
    public void testRequireDepsExpansion() throws Exception {
        List<ModuleDeps> expanded = new ArrayList<ModuleDeps>();
        RequireExpansionCompilerPass pass = new RequireExpansionCompilerPass(mockAggregator, new Features(), null,
                expanded, new MutableBoolean(false), true, null, false, null);

        String code, output;

        // Ensure dependency list is modified for the following
        code = "require = { deps:[\"foo\"] };";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("require={deps:[\"foo\",\"" + placeHolder0 + "\"]};", output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        code = "require.deps = [\"foo\"];";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("require.deps=[\"foo\",\"" + placeHolder0 + "\"];", output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        code = "this.require.deps = [\"foo\"];";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("this.require.deps=[\"foo\",\"" + placeHolder0 + "\"];", output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        code = "obj.require.deps = [\"foo\"];";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("obj.require.deps=[\"foo\",\"" + placeHolder0 + "\"];", output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        code = "this.require = {deps:[\"foo\"]};";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("this.require={deps:[\"foo\",\"" + placeHolder0 + "\"]};", output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        code = "obj.require = {deps:[\"foo\"]};";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("obj.require={deps:[\"foo\",\"" + placeHolder0 + "\"]};", output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        code = "var require = {deps:[\"foo\"]};";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("var require={deps:[\"foo\",\"" + placeHolder0 + "\"]};", output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        code = "obj = {require: {deps: [\"foo\"]}};";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("obj={require:{deps:[\"foo\",\"" + placeHolder0 + "\"]}};", output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        code = "var obj = {require: {deps:[\"foo\"]}};";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("var obj={require:{deps:[\"foo\",\"" + placeHolder0 + "\"]}};", output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        code = "call({require: {deps:[\"foo\"]}});";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("call({require:{deps:[\"foo\",\"" + placeHolder0 + "\"]}});", output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());

        // Negative tests.  Dependency list should not be modified
        code = "deps = [\"foo\"];";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\"]"));

        code = "this.deps = [\"foo\"];";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\"]"));

        code = "var obj = {deps:[\"foo\"]};";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\"]"));

        code = "var obj = {top: {deps:[\"foo\"]}};";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\"]"));

        code = "call({obj: {deps:[\"foo\"]}});";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue(output.contains("[\"foo\"]"));

        code = "require.deps=deps;";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals(code, output);

        code = "require.deps={\"foo\":0};";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals(code, output);

        // ensure proper placement of expanded deps
        code = "require.deps=[first,\"foo\",third];";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("require.deps=[first,\"foo\",third,\"" + placeHolder0 + "\"];", output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "bar", "a/b" })),
                expanded.get(0).getModuleIds());
    }

    @Test
    public void testHasPluginResolution() throws Exception {
        Features features = new Features();
        Set<String> dependentFeatures = new TreeSet<String>();
        features.put("feature1", true);
        features.put("feature2", true);
        List<ModuleDeps> expanded = new ArrayList<ModuleDeps>();
        RequireExpansionCompilerPass pass = new RequireExpansionCompilerPass(mockAggregator, features,
                dependentFeatures, expanded, new MutableBoolean(false), true, null, false, null);

        String code, output;
        code = "require([\"has!feature1?has1\",\"has!feature2?has2\"]);";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertEquals("[feature1, feature2]", dependentFeatures.toString());
        Assert.assertEquals("require([\"has!feature1?has1\",\"has!feature2?has2\",\"" + placeHolder0 + "\"]);",
                output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "dep1", "dep2" })),
                expanded.get(0).getModuleIds());

        features.put("feature2", false);
        dependentFeatures.clear();
        output = runPass(pass, code);
        Assert.assertEquals("[feature1, feature2]", dependentFeatures.toString());
        Assert.assertEquals("require([\"has!feature1?has1\",\"has!feature2?has2\",\"" + placeHolder0 + "\"]);",
                output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "dep1" })),
                expanded.get(0).getModuleIds());

        features.put("feature1", false);
        dependentFeatures.clear();
        output = runPass(pass, code);
        Assert.assertEquals("[feature1, feature2]", dependentFeatures.toString());
        Assert.assertEquals("require([\"has!feature1?has1\",\"has!feature2?has2\"]);", output);
        Assert.assertEquals(0, expanded.get(0).getModuleIds().size());

        features.remove("feature2");
        dependentFeatures.clear();
        output = runPass(pass, code);
        Assert.assertEquals("[feature1, feature2]", dependentFeatures.toString());
        Assert.assertEquals("require([\"has!feature1?has1\",\"has!feature2?has2\",\"" + placeHolder0 + "\"]);",
                output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "has!feature2?dep2" })),
                expanded.get(0).getModuleIds());

        mockAggregator.getOptions().setOption(IOptions.DISABLE_HASPLUGINBRANCHING, true);
        output = runPass(pass, code);
        Assert.assertEquals("[feature1, feature2]", dependentFeatures.toString());
        Assert.assertEquals("require([\"has!feature1?has1\",\"has!feature2?has2\"]);", output);
        Assert.assertEquals(0, expanded.get(0).getModuleIds().size());

        mockAggregator.getOptions().setOption(IOptions.DISABLE_HASPLUGINBRANCHING, false);
        features.put("feature1", true);
        dependentFeatures.clear();
        output = runPass(pass, code);
        Assert.assertEquals("[feature1, feature2]", dependentFeatures.toString());
        Assert.assertEquals("require([\"has!feature1?has1\",\"has!feature2?has2\",\"" + placeHolder0 + "\"]);",
                output);
        Assert.assertEquals(new LinkedHashSet<String>(Arrays.asList(new String[] { "dep1", "has!feature2?dep2" })),
                expanded.get(0).getModuleIds());

        features.remove("feature1");
        dependentFeatures.clear();
        output = runPass(pass, code);
        Assert.assertEquals("[feature1, feature2]", dependentFeatures.toString());
        Assert.assertEquals("require([\"has!feature1?has1\",\"has!feature2?has2\",\"" + placeHolder0 + "\"]);",
                output);
        Assert.assertEquals(
                new LinkedHashSet<String>(Arrays.asList(new String[] { "has!feature1?dep1", "has!feature2?dep2" })),
                expanded.get(0).getModuleIds());

        mockAggregator.getOptions().setOption(IOptions.DISABLE_HASPLUGINBRANCHING, true);
        output = runPass(pass, code);
        Assert.assertEquals("[feature1, feature2]", dependentFeatures.toString());
        Assert.assertEquals("require([\"has!feature1?has1\",\"has!feature2?has2\"]);", output);
        Assert.assertEquals(0, expanded.get(0).getModuleIds().size());
    }

    @Test
    public void testDependencyVerificationException() throws Exception {
        Features features = new Features();
        Set<String> dependentFeatures = new TreeSet<String>();
        features.put("feature1", true);
        features.put("feature2", true);
        List<ModuleDeps> expanded = new ArrayList<ModuleDeps>();
        RequireExpansionCompilerPass pass = new RequireExpansionCompilerPass(mockAggregator, features,
                dependentFeatures, expanded, new MutableBoolean(false), true, null, false, null);

        mockAggregator.getOptions().setOption(IOptions.DEVELOPMENT_MODE, true);
        mockAggregator.getOptions().setOption(IOptions.VERIFY_DEPS, true);
        String code, output;
        moduleName = "x/y";
        code = "define([\"x/y/z\", \"foo\", \"dep3\"], function(z, foo, dep3) {});";
        try {
            output = runPass(pass, code);
            Assert.fail();
        } catch (RuntimeDependencyVerificationException ex) {
        }

        // Ensure that duplicate dependencies in define statement doesn't
        //  throw DependencyVerificationException
        code = "define([\"x/y/z\", \"foo\", \"x/y/z\"], function(z, foo) {});";
        output = runPass(pass, code);
        System.out.println(output);

    }

    @Test
    public void testLogging() throws Exception {

        MutableBoolean hasExpandableRequires = new MutableBoolean(false);
        RequireExpansionCompilerPass pass = new RequireExpansionCompilerPass(mockAggregator, new Features(), null,
                new ArrayList<ModuleDeps>(), hasExpandableRequires, true, null, true, null);

        String code, output;
        code = "require([\"foo\"],function(){});";
        output = runPass(pass, code);
        System.out.println(output);
        Assert.assertTrue("Expected pattern not found.",
                Pattern.compile("console\\.log\\(\\\"[^)\"]*Expanding requires list").matcher(output).find());
        Assert.assertTrue("Output does not contain expected value.", output.contains(
                "foo (" + MessageFormat.format(com.ibm.jaggr.core.util.Messages.DependencyList_5, "test") + ")"));
        Assert.assertTrue("Output does not contain expected value.", output.contains(
                "bar (" + MessageFormat.format(com.ibm.jaggr.core.util.Messages.DependencyList_4, "foo") + ")"));
        Assert.assertTrue("Output does not contain expected value.", output.contains(
                "a/b (" + MessageFormat.format(com.ibm.jaggr.core.util.Messages.DependencyList_4, "bar") + ")"));
        Assert.assertTrue(hasExpandableRequires.getValue());

    }

    private String runPass(RequireExpansionCompilerPass pass, String code) {
        Compiler compiler = new Compiler();
        Node root = compiler.parse(JSSourceFile.fromCode(moduleName, code));
        pass.process(null, root);
        CodeBuilder cb = new CodeBuilder();
        compiler.toSource(cb, 0, root);
        return cb.toString();
    }
}