/*
 * $Id: TestFormBeanConfig.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 java.util.Map;

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

/**
 * Unit tests for the <code>org.apache.struts.config.FormBeanConfig</code>
 * class.  Currently only contains code to test the methods that support
 * configuration inheritance.
 *
 * @version $Rev: 471754 $ $Date: 2005-05-25 19:35:00 -0400 (Wed, 25 May 2005)
 *          $
 */
public class TestFormBeanConfig extends TestCase {
    // ----------------------------------------------------- Instance Variables

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

    /**
     * The common base we'll use.
     */
    private FormBeanConfig baseForm = null;

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

    /**
     * Construct a new instance of this test case.
     *
     * @param name Name of the test case
     */
    public TestFormBeanConfig(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.config = factoryObject.createModuleConfig("");

        // setup the base form
        this.baseForm = new FormBeanConfig();
        this.baseForm.setName("baseForm");
        this.baseForm.setType("org.apache.struts.mock.MockActionForm");

        // set up id, name, and score
        FormPropertyConfig property = new FormPropertyConfig();

        property.setName("id");
        property.setType("java.lang.String");
        this.baseForm.addFormPropertyConfig(property);

        property = new FormPropertyConfig();
        property.setName("name");
        property.setType("java.lang.String");
        property.setProperty("count", "10");
        this.baseForm.addFormPropertyConfig(property);

        property = new FormPropertyConfig();
        property.setName("score");
        property.setType("java.lang.String");
        this.baseForm.addFormPropertyConfig(property);

        property = new FormPropertyConfig();
        property.setName("message");
        property.setType("java.lang.String");
        this.baseForm.addFormPropertyConfig(property);

        // register it to our config
        this.config.addFormBeanConfig(this.baseForm);
    }

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

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

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

    /**
     * Basic check that shouldn't detect an error.
     */
    public void testCheckCircularInheritance() {
        FormBeanConfig child = new FormBeanConfig();

        child.setName("child");
        child.setExtends("baseForm");

        FormBeanConfig grandChild = new FormBeanConfig();

        grandChild.setName("grandChild");
        grandChild.setExtends("child");

        this.config.addFormBeanConfig(child);
        this.config.addFormBeanConfig(grandChild);

        assertTrue("Circular inheritance shouldn't have been detected",
            !grandChild.checkCircularInheritance(this.config.getFormBeanConfigs()));
    }

    /**
     * Basic check that SHOULD detect an error.
     */
    public void testCheckCircularInheritanceError() {
        FormBeanConfig child = new FormBeanConfig();

        child.setName("child");
        child.setExtends("baseForm");

        FormBeanConfig grandChild = new FormBeanConfig();

        grandChild.setName("grandChild");
        grandChild.setExtends("child");

        // establish the circular relationship with base
        this.baseForm.setExtends("grandChild");

        this.config.addFormBeanConfig(child);
        this.config.addFormBeanConfig(grandChild);

        assertTrue("Circular inheritance should've been detected",
            grandChild.checkCircularInheritance(this.config.getFormBeanConfigs()));
    }

    /**
     * Test that processExtends() makes sure that a base form's own extension
     * has been processed.
     */
    public void testProcessExtendsBaseFormExtends() {
        CustomFormBeanConfig first = new CustomFormBeanConfig();

        first.setName("first");

        CustomFormBeanConfig second = new CustomFormBeanConfig();

        second.setName("second");
        second.setExtends("first");

        this.config.addFormBeanConfig(first);
        this.config.addFormBeanConfig(second);

        // set baseForm to extend second
        this.baseForm.setExtends("second");

        this.baseForm.processExtends(this.config.getFormBeanConfigs());

        assertTrue("The first form's processExtends() wasn't called",
            first.isProcessExtendsCalled());
        assertTrue("The second form's processExtends() wasn't called",
            second.isProcessExtendsCalled());
    }

    /**
     * Make sure that correct exception is thrown if a base form can't be
     * found.
     */
    public void testProcessExtendsMissingBaseForm() {
        this.baseForm.setExtends("someMissingForm");

        try {
            this.baseForm.processExtends(this.config.getFormBeanConfigs());
            fail(
                "An exception should be thrown if a super form can't be found.");
        } catch (final NullPointerException e) {
            // succeed
            e.printStackTrace();
        }
    }

    /**
     * Test a typical form bean configuration extension where various
     * properties should be inherited from a base form.  This method checks
     * all the properties.
     */
    public void testInheritFrom() {
        // give baseForm some arbitrary parameters
        String baseFormCount = "1";

        this.baseForm.setProperty("count", baseFormCount);

        // create a basic subform
        FormBeanConfig subForm = new FormBeanConfig();
        String subFormName = "subForm";

        subForm.setName(subFormName);
        subForm.setExtends("baseForm");

        // override score
        FormPropertyConfig property = new FormPropertyConfig();

        property.setName("score");
        property.setType("java.lang.Integer");
        subForm.addFormPropertyConfig(property);

        // ... and id
        property = new FormPropertyConfig();
        property.setName("id");
        property.setType("java.lang.String");
        property.setInitial("unknown");
        subForm.addFormPropertyConfig(property);

        // ... and message
        property = new FormPropertyConfig();
        property.setName("message");
        property.setType("java.lang.String");
        property.setSize(10);
        subForm.addFormPropertyConfig(property);

        this.config.addFormBeanConfig(subForm);

        subForm.inheritFrom(this.baseForm);

        // check that our subForm is still the one in the config
        assertSame("subForm no longer in ModuleConfig", subForm,
                this.config.findFormBeanConfig("subForm"));

        // check our configured sub form
        assertNotNull("Form bean type was not inherited", subForm.getType());
        assertEquals("Wrong form bean name", subFormName, subForm.getName());
        assertEquals("Wrong form bean type", this.baseForm.getType(),
            subForm.getType());
        assertEquals("Wrong restricted value", this.baseForm.isRestricted(),
            subForm.isRestricted());

        FormPropertyConfig[] formPropertyConfigs =
            subForm.findFormPropertyConfigs();

        assertEquals("Wrong prop count", 4, formPropertyConfigs.length);

        // we want to check that the form is EXACTLY as we want it, so
        //  here are some fine grain checks
        property = subForm.findFormPropertyConfig("name");

        FormPropertyConfig original = this.baseForm.findFormPropertyConfig("name");

        assertNotNull("'name' property was not inherited", property);
        assertEquals("Wrong type for name", original.getType(),
            property.getType());
        assertEquals("Wrong initial value for name", original.getInitial(),
            property.getInitial());
        assertEquals("Wrong size value for name", original.getSize(),
            property.getSize());

        property = subForm.findFormPropertyConfig("id");
        original = this.baseForm.findFormPropertyConfig("id");
        assertNotNull("'id' property was not found", property);
        assertEquals("Wrong type for id", original.getType(), property.getType());
        assertEquals("Wrong initial value for id", "unknown",
            property.getInitial());
        assertEquals("Wrong size value for id", original.getSize(),
            property.getSize());

        property = subForm.findFormPropertyConfig("score");
        original = this.baseForm.findFormPropertyConfig("score");
        assertNotNull("'score' property was not found", property);
        assertEquals("Wrong type for score", "java.lang.Integer",
            property.getType());
        assertEquals("Wrong initial value for score", original.getInitial(),
            property.getInitial());
        assertEquals("Wrong size value for score", original.getSize(),
            property.getSize());

        property = subForm.findFormPropertyConfig("message");
        original = this.baseForm.findFormPropertyConfig("message");
        assertNotNull("'message' property was not found", property);
        assertEquals("Wrong type for message", original.getType(),
            property.getType());
        assertEquals("Wrong initial value for message", original.getInitial(),
            property.getInitial());
        assertEquals("Wrong size value for message", 10, property.getSize());

        property = subForm.findFormPropertyConfig("name");
        original = this.baseForm.findFormPropertyConfig("name");
        assertEquals("Arbitrary property not found",
            original.getProperty("count"), property.getProperty("count"));

        String count = subForm.getProperty("count");

        assertEquals("Arbitrary property was not inherited", baseFormCount,
            count);
    }

    /**
     * Used to detect that FormBeanConfig is making the right calls.
     */
    public static class CustomFormBeanConfig extends FormBeanConfig {

        /** serialVersionUID */
        private static final long serialVersionUID = -7039656280597815087L;

        private boolean processExtendsCalled = false;

        /**
         * @see org.apache.struts.config.FormBeanConfig#processExtends(java.util.Map)
         */
        @Override
        public void processExtends(final Map<String, FormBeanConfig> moduleConfig) {
            super.processExtends(moduleConfig);
            this.processExtendsCalled = true;
        }

        /**
         * @return processExtendsCalled
         */
        public boolean isProcessExtendsCalled() {
            return this.processExtendsCalled;
        }
    }
}
