/*
 * $Id: TestForwardConfig.java 471754 2006-11-06 14:55:09Z husted $
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.struts.config;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
 * <p>Unit tests for ForwardConfig.  Currently contains tests for methods
 * supporting configuration inheritance.</p>
 *
 * @version $Rev: 471754 $ $Date: 2005-05-21 19:06:53 -0400 (Sat, 21 May 2005)
 *          $
 */
public class TestForwardConfig extends TestCase {

    // ----------------------------------------------------- Instance Variables

    /**
     * The ModuleConfig we'll use.
     */
    private ModuleConfig moduleConfig = null;

    /**
     * The common base we'll use.
     */
    private ForwardConfig baseConfig = null;

    /**
     * The common subForward we'll use.
     */
    private ForwardConfig subConfig = null;

    /**
     * A ForwardConfig we'll use to test cases where a ForwardConfig declared
     * for an action extends a ForwardConfig declared globally, with both
     * ForwardConfigs using the same name.
     */
    private ForwardConfig actionBaseConfig = null;

    /**
     * An action mapping we'll use within tests.
     */
    private ActionConfig actionConfig = null;

    // ----------------------------------------------------------- Constructors

    /**
     * Construct a new instance of this test case.
     *
     * @param name Name of the test case
     */
    public TestForwardConfig(final String name) {
        super(name);
    }

    // --------------------------------------------------------- Public Methods

    /**
     * Set up instance variables required by this test case.
     */
    @Override
    public void setUp() {
        ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();

        this.moduleConfig = factoryObject.createModuleConfig("");

        // Setup the base and sub forwards, with sub extending base
        this.baseConfig = new ForwardConfig();
        this.baseConfig.setName("baseConfig");
        this.baseConfig.setPath("/somePage.jsp");

        this.subConfig = new ForwardConfig();
        this.subConfig.setName("subConfig");
        this.subConfig.setExtends("baseConfig");
        this.subConfig.setRedirect(true);

        this.actionBaseConfig = new ForwardConfig();
        this.actionBaseConfig.setName("baseConfig");
        this.actionBaseConfig.setExtends("baseConfig");
        this.actionBaseConfig.setModule("/other");

        // Setup the default action config
        this.actionConfig = new ActionConfig();
        this.actionConfig.setPath("/index");
        this.moduleConfig.addActionConfig(this.actionConfig);

        // No forward configs are registered to either module or action configs.
        // Each test will determine where it needs these configs, if at all.
    }

    /**
     * Return the tests included in this test suite.
     * @return Test
     */
    public static Test suite() {
        return new TestSuite(TestForwardConfig.class);
    }

    /**
     * Tear down instance variables required by this test case.
     */
    @Override
    public void tearDown() {
        this.moduleConfig = null;
        this.baseConfig = null;
    }

    // ------------------------------------------------------- Individual Tests

    /**
     * Make sure checkCircularInheritance() works as expected where there is
     * no inheritance set up.
     */
    public void testCheckCircularInheritanceNoExtends() {
        this.moduleConfig.addForwardConfig(this.baseConfig);

        boolean result = this.baseConfig.checkCircularInheritance(
                this.moduleConfig.getForwardConfigs(), null);

        assertTrue("Incorrect result", !result);
    }

    /**
     * Test checkCircularInheritance() for when there is no circular
     * inheritance.
     */
    public void testCheckCircularInheritanceNoConflicts() {
        this.moduleConfig.addForwardConfig(this.baseConfig);
        this.moduleConfig.addForwardConfig(this.subConfig);

        boolean result = this.subConfig.checkCircularInheritance(
                this.moduleConfig.getForwardConfigs(), null);

        assertTrue("Incorrect result", !result);
    }

    /**
     * Test checkCircularInheritance() for circular inheritance between global
     * forwards.
     */
    public void testCheckCircularInheritanceBasicGlobal() {
        this.moduleConfig.addForwardConfig(this.subConfig);
        this.moduleConfig.addForwardConfig(this.baseConfig);

        // set the baseConfig to extend subConfig
        this.baseConfig.setExtends("subConfig");

        boolean result = this.subConfig.checkCircularInheritance(
                this.moduleConfig.getForwardConfigs(), null);

        assertTrue("Circular inheritance not detected.", result);
    }

    /**
     * Test checkCircularInheritance() for circular inheritance between global
     * forwards.
     */
    public void testCheckCircularInheritanceGlobal2Levels() {
        this.moduleConfig.addForwardConfig(this.baseConfig);
        this.moduleConfig.addForwardConfig(this.subConfig);

        ForwardConfig grand = new ForwardConfig();

        grand.setName("grandConfig");
        grand.setExtends("subConfig");
        this.moduleConfig.addForwardConfig(grand);

        // set the baseConfig to extend grandConfig
        this.baseConfig.setExtends("grandConfig");

        boolean result = grand.checkCircularInheritance(
                this.moduleConfig.getForwardConfigs(), null);

        assertTrue("Circular inheritance not detected.", result);
    }

    /**
     * Test checkCircularInheritance() for circular inheritance between
     * forwards in an action.
     */
    public void testCheckCircularInheritanceActionForwardsNoConflict() {
        this.actionConfig.addForwardConfig(this.baseConfig);
        this.actionConfig.addForwardConfig(this.subConfig);

        boolean result = this.subConfig.checkCircularInheritance(
                this.moduleConfig.getForwardConfigs(), this.actionConfig.getForwards());

        assertTrue("Incorrect result", !result);
    }

    /**
     * Test checkCircularInheritance() for circular inheritance between
     * forwards in an action.
     */
    public void testCheckCircularInheritanceActionForwardsBasic() {
        this.actionConfig.addForwardConfig(this.baseConfig);
        this.actionConfig.addForwardConfig(this.subConfig);

        // set base to extend sub
        this.baseConfig.setExtends("subConfig");

        boolean result = this.subConfig.checkCircularInheritance(
                this.moduleConfig.getForwardConfigs(), this.actionConfig.getForwards());

        assertTrue("Circular inheritance not detected.", result);
    }

    /**
     * Test checkCircularInheritance() for circular inheritance between a
     * forward declared in an action and a global forward.
     */
    public void testCheckCircularInheritanceActionForwardExtendGlobal() {
        this.actionConfig.addForwardConfig(this.subConfig);
        this.moduleConfig.addForwardConfig(this.baseConfig);

        boolean result = this.subConfig.checkCircularInheritance(
                this.moduleConfig.getForwardConfigs(), this.actionConfig.getForwards());

        assertTrue("Incorrect result", !result);
    }

    /**
     * Test checkCircularInheritance() for circular inheritance between a
     * forward declared in an action and a global forward and both forwards
     * have the same name.
     */
    public void testCheckCircularInheritanceActionForwardExtendGlobalSameName() {
        this.moduleConfig.addForwardConfig(this.baseConfig);
        this.actionConfig.addForwardConfig(this.actionBaseConfig);

        boolean result = this.actionBaseConfig.checkCircularInheritance(
                this.moduleConfig.getForwardConfigs(), this.actionConfig.getForwards());

        assertTrue("Incorrect result", !result);
    }

    /**
     * Make sure processExtends() throws an error when the config is already
     * configured.
     */
    public void testProcessExtendsConfigured() {
        this.baseConfig.freeze();
        this.moduleConfig.addForwardConfig(this.baseConfig);

        try {
            this.baseConfig.processExtends(this.moduleConfig.getForwardConfigs(), null);
            fail("processExtends should not succeed when object is already configured");
        } catch (final IllegalStateException e) {
            // success
            e.printStackTrace();
        }
    }

    /**
     * Test processExtends() when nothing is extended.
     */
    public void testProcessExtendsNoExtension() {

        this.moduleConfig.addForwardConfig(this.baseConfig);

        final String path = this.baseConfig.getPath();
        final String module = this.baseConfig.getModule();
        final String name = this.baseConfig.getName();
        final String inherit = this.baseConfig.getExtends();
        final boolean redirect = this.baseConfig.getRedirect();

        this.baseConfig.processExtends(this.moduleConfig.getForwardConfigs(), null);

        assertEquals("Path shouldn't have changed", path, this.baseConfig.getPath());
        assertEquals("Module shouldn't have changed", module,
                this.baseConfig.getModule());
        assertEquals("Name shouldn't have changed", name, this.baseConfig.getName());
        assertEquals("Extends shouldn't have changed", inherit,
                this.baseConfig.getExtends());
        assertEquals("Redirect shouldn't have changed", redirect,
                this.baseConfig.getRedirect());
    }

    /**
     * Test processExtends() with a basic extension.
     */
    public void testProcessExtendsBasicExtension() {
        String baseCount = "10";
        this.baseConfig.setProperty("count", baseCount);

        String baseLabel = "label a";
        this.baseConfig.setProperty("label", baseLabel);

        String subLabel = "label b";
        this.subConfig.setProperty("label", subLabel);

        this.moduleConfig.addForwardConfig(this.baseConfig);
        this.moduleConfig.addForwardConfig(this.subConfig);

        final String path = this.baseConfig.getPath();
        final String module = this.baseConfig.getModule();
        final String name = this.subConfig.getName();
        final String inherit = this.subConfig.getExtends();
        final boolean redirect = this.subConfig.getRedirect();

        this.subConfig.processExtends(this.moduleConfig.getForwardConfigs(), null);

        assertEquals("Path wasn't inherited", path, this.subConfig.getPath());
        assertEquals("Module wasn't inherited", module, this.subConfig.getModule());
        assertEquals("Name shouldn't have changed", name, this.subConfig.getName());
        assertEquals("Extends shouldn't have changed", inherit,
                this.subConfig.getExtends());
        assertEquals("Redirect shouldn't have changed", redirect,
                this.subConfig.getRedirect());
        assertEquals("Arbitrary config property was not inherited", baseCount,
                this.subConfig.getProperty("count"));
        assertEquals("Overridden config property was not retained", subLabel,
                this.subConfig.getProperty("label"));
    }

    /**
     * Test processExtends() with a basic extension between an action config
     * and a global config.
     */
    public void testProcessExtendsBasicGlobalExtension() {

        this.moduleConfig.addForwardConfig(this.baseConfig);
        this.actionConfig.addForwardConfig(this.subConfig);

        final String path = this.baseConfig.getPath();
        final String module = this.baseConfig.getModule();
        final String name = this.subConfig.getName();
        final String inherit = this.subConfig.getExtends();
        final boolean redirect = this.subConfig.getRedirect();

        this.subConfig.processExtends(
                this.moduleConfig.getForwardConfigs(), this.actionConfig.getForwards());

        assertEquals("Path wasn't inherited", path, this.subConfig.getPath());
        assertEquals("Module wasn't inherited", module, this.subConfig.getModule());
        assertEquals("Name shouldn't have changed", name, this.subConfig.getName());
        assertEquals("Extends shouldn't have changed", inherit,
                this.subConfig.getExtends());
        assertEquals("Redirect shouldn't have changed", redirect,
                this.subConfig.getRedirect());
    }

    /**
     * Test processExtends() with an incorrect setup where a global config
     * attempts to extend an action config.
     */
    public void testProcessExtendsGlobalExtendingAction() {
        this.moduleConfig.addForwardConfig(this.subConfig);
        this.actionConfig.addForwardConfig(this.baseConfig);

        try {
            this.subConfig.processExtends(
                    this.moduleConfig.getForwardConfigs(), this.actionConfig.getForwards());
            fail("Should not find config from actionConfig when *this* is global");
        } catch (final NullPointerException e) {
            // succeed
            e.printStackTrace();
        }
    }

    /**
     * Test processExtends() with an action config that extends a global
     * config with the same name.
     */
    public void testProcessExtendsSameNames() {

        this.moduleConfig.addForwardConfig(this.baseConfig);
        this.actionConfig.addForwardConfig(this.actionBaseConfig);

        final String path = this.baseConfig.getPath();
        final String module = this.actionBaseConfig.getModule();
        final String name = this.actionBaseConfig.getName();
        final String inherit = this.actionBaseConfig.getExtends();
        final boolean redirect = this.baseConfig.getRedirect();

        this.actionBaseConfig.processExtends(
                this.moduleConfig.getForwardConfigs(), this.actionConfig.getForwards());

        assertEquals("Path wasn't inherited", path, this.actionBaseConfig.getPath());
        assertEquals("Module shouldn't have changed", module,
                this.actionBaseConfig.getModule());
        assertEquals("Name shouldn't have changed", name,
                this.actionBaseConfig.getName());
        assertEquals("Extends shouldn't have changed", inherit,
                this.actionBaseConfig.getExtends());
        assertEquals("Redirect shouldn't have changed", redirect,
                this.actionBaseConfig.getRedirect());
    }

    /**
     * Test processExtends() where an action ForwardConfig extends another
     * ForwardConfig, which in turn extends a global ForwardConfig with the
     * same name.
     */
    public void testProcessExtendsActionExtendsActionExtendsGlobalWithSameName() {

        this.moduleConfig.addForwardConfig(this.baseConfig);
        this.actionConfig.addForwardConfig(this.actionBaseConfig);
        this.actionConfig.addForwardConfig(this.subConfig);

        final String path = this.baseConfig.getPath();
        final String module = this.actionBaseConfig.getModule();
        final boolean redirect = this.subConfig.getRedirect();
        final String inherit = this.subConfig.getExtends();
        final String name = this.subConfig.getName();

        this.subConfig.processExtends(
                this.moduleConfig.getForwardConfigs(), this.actionConfig.getForwards());

        assertTrue("baseConfig's processExtends() should've been called",
                this.baseConfig.isExtensionProcessed());
        assertTrue("actionBaseConfig's processExtends() should've been called",
                this.actionBaseConfig.isExtensionProcessed());

        assertEquals("Path wasn't inherited", path, this.subConfig.getPath());
        assertEquals("Module wasn't inherited", module, this.subConfig.getModule());
        assertEquals("Name shouldn't have changed", name, this.subConfig.getName());
        assertEquals("Extends shouldn't have changed", inherit,
                this.subConfig.getExtends());
        assertEquals("Redirect shouldn't have changed", redirect,
                this.subConfig.getRedirect());
    }
}
