View Javadoc

    1   /*
    2    * $Id: AbstractUITagTest.java 474560 2006-11-13 23:09:31Z hermanns $
    3    *
    4    * Licensed to the Apache Software Foundation (ASF) under one
    5    * or more contributor license agreements.  See the NOTICE file
    6    * distributed with this work for additional information
    7    * regarding copyright ownership.  The ASF licenses this file
    8    * to you under the Apache License, Version 2.0 (the
    9    * "License"); you may not use this file except in compliance
    10   * with the License.  You may obtain a copy of the License at
    11   *
    12   *  http://www.apache.org/licenses/LICENSE-2.0
    13   *
    14   * Unless required by applicable law or agreed to in writing,
    15   * software distributed under the License is distributed on an
    16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    17   * KIND, either express or implied.  See the License for the
    18   * specific language governing permissions and limitations
    19   * under the License.
    20   */
    21  package org.apache.struts2.views.jsp;
    22
    23  import java.beans.IntrospectionException;
    24  import java.beans.Introspector;
    25  import java.beans.PropertyDescriptor;
    26  import java.io.InputStream;
    27  import java.lang.reflect.InvocationTargetException;
    28  import java.net.URL;
    29  import java.util.Arrays;
    30  import java.util.Collections;
    31  import java.util.HashMap;
    32  import java.util.Iterator;
    33  import java.util.List;
    34  import java.util.Map;
    35  import java.util.StringTokenizer;
    36
    37  import org.apache.commons.logging.Log;
    38  import org.apache.commons.logging.LogFactory;
    39  import org.apache.struts2.ServletActionContext;
    40  import org.apache.struts2.views.jsp.ui.AbstractUITag;
    41
    42  import com.opensymphony.xwork2.ActionContext;
    43
    44
    45  /***
    46   */
    47  public abstract class AbstractUITagTest extends AbstractTagTest {
    48
    49      private static final Log LOG = LogFactory.getLog(AbstractUITagTest.class);
    50
    51      static final String FREEMARKER_ERROR_EXPECTATION = "Java backtrace for programmers:";
    52
    53      /***
    54       * Simple helper class for generic tag property testing mechanism. Basically it holds a property name, a property
    55       * value and an output to be expected in tag output when property was accordingly set.
    56       *
    57       * @author <a href="mailto:gielen@it-neering.net">Rene Gielen</a>
    58       */
    59      public class PropertyHolder {
    60          String name, value, expectation;
    61
    62          public String getName() {
    63              return name;
    64          }
    65
    66          public String getValue() {
    67              return value;
    68          }
    69
    70          public String getExpectation() {
    71              return expectation;
    72          }
    73
    74          /***
    75           * Construct simple holder with default expectation.
    76           *
    77           * @param name  The property name to use.
    78           * @param value The property value to set.
    79           * @see #PropertyHolder(String, String, String)
    80           */
    81          public PropertyHolder(String name, String value) {
    82              this(name, value, null);
    83          }
    84
    85          /***
    86           * Construct property holder.
    87           *
    88           * @param name        The property name to use.
    89           * @param value       The property value to set.
    90           * @param expectation The expected String to occur in tag output caused by setting given tag property. If
    91           *                    <tt>null</tt>, will be set to <pre>name + "=\"" + value + "\"</pre>.
    92           */
    93          public PropertyHolder(String name, String value, String expectation) {
    94              this.name = name;
    95              this.value = value;
    96              if (expectation != null) {
    97                  this.expectation = expectation;
    98              } else {
    99                  this.expectation = name + "=\"" + value + "\"";
    100             }
    101         }
    102
    103         /***
    104          * Convenience method for easily adding anonymous constructed instance to a given map, with {@link #getName()}
    105          * as key.
    106          *
    107          * @param map The map to place this instance in.
    108          */
    109         public void addToMap(Map map) {
    110             if (map != null) {
    111                 map.put(this.name, this);
    112             }
    113         }
    114     }
    115
    116     /***
    117      * Simple Helper for setting bean properties. Although BeanUtils from oscore should provide bean property setting
    118      * functionality, it does not work (at least with my JDK 1.5.0_05), failing in jdk's PropertyDescriptor constructor.
    119      * This implementation works safely in any case, and does not add dependency on commons-beanutils for building.
    120      * TODO: Check how we can remove this crap again.
    121      *
    122      * @author <a href="mailto:gielen@it-neering.net">Rene Gielen</a>
    123      */
    124     public class BeanHelper {
    125         Map propDescriptors;
    126         Object bean;
    127
    128         public BeanHelper(Object bean) {
    129             this.bean = bean;
    130
    131             try {
    132                 PropertyDescriptor[] pds;
    133                 pds = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
    134                 propDescriptors = new HashMap(pds.length + 1, 1f);
    135                 for (int i = 0; i < pds.length; i ++) {
    136                     propDescriptors.put(pds[i].getName(), pds[i]);
    137                 }
    138             } catch (IntrospectionException e) {
    139                 e.printStackTrace();
    140             }
    141         }
    142
    143         public void set(String name, Object value) throws IllegalAccessException, InvocationTargetException {
    144             PropertyDescriptor pd = (PropertyDescriptor) propDescriptors.get(name);
    145
    146             if (pd != null) {
    147                 pd.getWriteMethod().invoke(bean, new Object[]{value});
    148             }
    149         }
    150
    151     }
    152
    153     /***
    154      * Initialize a map of {@link PropertyHolder} for generic tag property testing. Will be used when calling {@link
    155      * #verifyGenericProperties(org.apache.struts2.views.jsp.ui.AbstractUITag, String, String[])} as properties to
    156      * verify.<p/> This implementation defines testdata for all common AbstractUITag properties and may be overridden in
    157      * subclasses.
    158      *
    159      * @return A Map of PropertyHolders values bound to {@link org.apache.struts2.views.jsp.AbstractUITagTest.PropertyHolder#getName()}
    160      *         as key.
    161      */
    162     protected Map initializedGenericTagTestProperties() {
    163         Map result = new HashMap();
    164         new PropertyHolder("name", "someName").addToMap(result);
    165         new PropertyHolder("id", "someId").addToMap(result);
    166         new PropertyHolder("cssClass", "cssClass1", "class=\"cssClass1\"").addToMap(result);
    167         new PropertyHolder("cssStyle", "cssStyle1", "style=\"cssStyle1\"").addToMap(result);
    168         new PropertyHolder("title", "someTitle").addToMap(result);
    169         new PropertyHolder("disabled", "true", "disabled=\"disabled\"").addToMap(result);
    170         //new PropertyHolder("label", "label", "label=\"label\"").addToMap(result);
    171         //new PropertyHolder("required", "someTitle").addToMap(result);
    172         new PropertyHolder("tabindex", "99").addToMap(result);
    173         new PropertyHolder("value", "someValue").addToMap(result);
    174         new PropertyHolder("onclick", "onclick1").addToMap(result);
    175         new PropertyHolder("ondblclick", "ondblclick1").addToMap(result);
    176         new PropertyHolder("onmousedown", "onmousedown1").addToMap(result);
    177         new PropertyHolder("onmouseup", "onmouseup1").addToMap(result);
    178         new PropertyHolder("onmouseover", "onmouseover1").addToMap(result);
    179         new PropertyHolder("onmousemove", "onmousemove1").addToMap(result);
    180         new PropertyHolder("onmouseout", "onmouseout1").addToMap(result);
    181         new PropertyHolder("onfocus", "onfocus1").addToMap(result);
    182         new PropertyHolder("onblur", "onblur1").addToMap(result);
    183         new PropertyHolder("onkeypress", "onkeypress1").addToMap(result);
    184         new PropertyHolder("onkeydown", "onkeydown1").addToMap(result);
    185         new PropertyHolder("onkeyup", "onkeyup1").addToMap(result);
    186         new PropertyHolder("onclick", "onclick1").addToMap(result);
    187         new PropertyHolder("onselect", "onchange").addToMap(result);
    188         return result;
    189     }
    190
    191     /***
    192      * Do a generic verification that setting certain properties on a tag causes expected output regarding this
    193      * property. In most cases you would not call this directly, instead use {@link
    194      * #verifyGenericProperties(org.apache.struts2.views.jsp.ui.AbstractUITag, String, String[])}.
    195      *
    196      * @param tag              The fresh created tag instance to test.
    197      * @param theme            The theme to use. If <tt>null</tt>, use configured default theme.
    198      * @param propertiesToTest Map of {@link PropertyHolder}s, defining properties to test.
    199      * @param exclude          Names of properties to exclude from particular test.
    200      * @throws Exception
    201      */
    202     public void verifyGenericProperties(AbstractUITag tag, String theme, Map propertiesToTest, String[] exclude) throws Exception {
    203         if (tag != null && propertiesToTest != null) {
    204             List excludeList;
    205             if (exclude != null) {
    206                 excludeList = Arrays.asList(exclude);
    207             } else {
    208                 excludeList = Collections.EMPTY_LIST;
    209             }
    210
    211             tag.setPageContext(pageContext);
    212             if (theme != null) {
    213                 tag.setTheme(theme);
    214             }
    215
    216             BeanHelper beanHelper = new BeanHelper(tag);
    217             Iterator it = propertiesToTest.values().iterator();
    218             while (it.hasNext()) {
    219                 PropertyHolder propertyHolder = (PropertyHolder) it.next();
    220                 if (! excludeList.contains(propertyHolder.getName())) {
    221                     beanHelper.set(propertyHolder.getName(), propertyHolder.getValue());
    222                 }
    223             }
    224             tag.doStartTag();
    225             tag.doEndTag();
    226             String writerString = normalize(writer.toString(), true);
    227             if (LOG.isInfoEnabled()) {
    228                 LOG.info("AbstractUITagTest - [verifyGenericProperties]: Tag output is " + writerString);
    229             }
    230
    231             assertTrue("Freemarker error detected in tag output: " + writerString, writerString.indexOf(FREEMARKER_ERROR_EXPECTATION) == -1);
    232
    233             it = propertiesToTest.values().iterator();
    234             while (it.hasNext()) {
    235                 PropertyHolder propertyHolder = (PropertyHolder) it.next();
    236                 if (! excludeList.contains(propertyHolder.getName())) {
    237                     assertTrue("Expected to find: " + propertyHolder.getExpectation() + " in resulting String: " + writerString, writerString.indexOf(propertyHolder.getExpectation()) > -1);
    238                 }
    239             }
    240         }
    241     }
    242
    243     /***
    244      * Do a generic verification that setting certain properties on a tag causes expected output regarding this
    245      * property. Which properties to test with which expectations will be determined by the Map retrieved by {@link #initializedGenericTagTestProperties()}.
    246      *
    247      * @param tag              The fresh created tag instance to test.
    248      * @param theme            The theme to use. If <tt>null</tt>, use configured default theme.
    249      * @param exclude          Names of properties to exclude from particular test.
    250      * @throws Exception
    251      */
    252     public void verifyGenericProperties(AbstractUITag tag, String theme, String[] exclude) throws Exception {
    253         verifyGenericProperties(tag, theme, initializedGenericTagTestProperties(), exclude);
    254     }
    255
    256     /***
    257      * Attempt to verify the contents of this.writer against the contents of the URL specified.  verify() performs a
    258      * trim on both ends
    259      *
    260      * @param url the HTML snippet that we want to validate against
    261      * @throws Exception if the validation failed
    262      */
    263     public void verify(URL url) throws Exception {
    264         if (url == null) {
    265             fail("unable to verify a null URL");
    266         } else if (this.writer == null) {
    267             fail("AbstractJspWriter.writer not initialized.  Unable to verify");
    268         }
    269
    270         StringBuffer buffer = new StringBuffer(128);
    271         InputStream in = url.openStream();
    272         byte[] buf = new byte[4096];
    273         int nbytes;
    274
    275         while ((nbytes = in.read(buf)) > 0) {
    276             buffer.append(new String(buf, 0, nbytes));
    277         }
    278
    279         in.close();
    280
    281         /***
    282          * compare the trimmed values of each buffer and make sure they're equivalent.  however, let's make sure to
    283          * normalize the strings first to account for line termination differences between platforms.
    284          */
    285         String writerString = normalize(writer.toString(), true);
    286         String bufferString = normalize(buffer.toString(), true);
    287
    288         assertEquals(bufferString, writerString);
    289     }
    290
    291     /***
    292      * Attempt to verify the contents of this.writer against the contents of the URL specified.  verify() performs a
    293      * trim on both ends
    294      *
    295      * @param url the HTML snippet that we want to validate against
    296      * @throws Exception if the validation failed
    297      */
    298     public void verify(URL url, String[] excluded) throws Exception {
    299         if (url == null) {
    300             fail("unable to verify a null URL");
    301         } else if (this.writer == null) {
    302             fail("AbstractJspWriter.writer not initialized.  Unable to verify");
    303         }
    304
    305         StringBuffer buffer = new StringBuffer(128);
    306         InputStream in = url.openStream();
    307         byte[] buf = new byte[4096];
    308         int nbytes;
    309
    310         while ((nbytes = in.read(buf)) > 0) {
    311             buffer.append(new String(buf, 0, nbytes));
    312         }
    313
    314         in.close();
    315
    316         /***
    317          * compare the trimmed values of each buffer and make sure they're equivalent.  however, let's make sure to
    318          * normalize the strings first to account for line termination differences between platforms.
    319          */
    320         String writerString = normalize(writer.toString(), true);
    321         String bufferString = normalize(buffer.toString(), true);
    322
    323         assertEquals(bufferString, writerString);
    324     }
    325
    326     protected void setUp() throws Exception {
    327         super.setUp();
    328
    329         ServletActionContext.setServletContext(pageContext.getServletContext());
    330     }
    331
    332     protected void tearDown() throws Exception {
    333         super.tearDown();
    334         ActionContext.setContext(null);
    335     }
    336
    337     /***
    338      * normalizes a string so that strings generated on different platforms can be compared.  any group of one or more
    339      * space, tab, \r, and \n characters are converted to a single space character
    340      *
    341      * @param obj the object to be normalized.  normalize will perform its operation on obj.toString().trim() ;
    342      * @param appendSpace
    343      * @return the normalized string
    344      */
    345     public static String normalize(Object obj, boolean appendSpace) {
    346         StringTokenizer st = new StringTokenizer(obj.toString().trim(), " \t\r\n");
    347         StringBuffer buffer = new StringBuffer(128);
    348
    349         while (st.hasMoreTokens()) {
    350             buffer.append(st.nextToken());
    351
    352             /*
    353             if (appendSpace && st.hasMoreTokens()) {
    354                 buffer.append("");
    355             }
    356             */
    357         }
    358
    359         return buffer.toString();
    360     }
    361 }