001: /*
002: * Copyright (c) 2002-2003 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.webwork.views.jsp;
006:
007: import com.opensymphony.webwork.ServletActionContext;
008: import com.opensymphony.webwork.views.jsp.ui.AbstractUITag;
009: import com.opensymphony.xwork.ActionContext;
010: import org.apache.commons.logging.Log;
011: import org.apache.commons.logging.LogFactory;
012:
013: import java.beans.IntrospectionException;
014: import java.beans.Introspector;
015: import java.beans.PropertyDescriptor;
016: import java.io.InputStream;
017: import java.lang.reflect.InvocationTargetException;
018: import java.net.URL;
019: import java.util.*;
020:
021: /**
022: * @author Matt Ho <a href="mailto:matt@indigoegg.com"><matt@indigoegg.com></a>
023: * @author <a href="mailto:gielen@it-neering.net">Rene Gielen</a>
024: * @version $Id: AbstractUITagTest.java 2335 2006-03-08 18:09:29Z rainerh $
025: */
026: public abstract class AbstractUITagTest extends AbstractTagTest {
027:
028: private static final Log LOG = LogFactory
029: .getLog(AbstractUITagTest.class);
030:
031: static final String FREEMARKER_ERROR_EXPECTATION = "Java backtrace for programmers:";
032:
033: /**
034: * Simple helper class for generic tag property testing mechanism. Basically it holds a property name, a property
035: * value and an output to be expected in tag output when property was accordingly set.
036: *
037: * @author <a href="mailto:gielen@it-neering.net">Rene Gielen</a>
038: */
039: public class PropertyHolder {
040: String name, value, expectation;
041:
042: public String getName() {
043: return name;
044: }
045:
046: public String getValue() {
047: return value;
048: }
049:
050: public String getExpectation() {
051: return expectation;
052: }
053:
054: /**
055: * Construct simple holder with default expectation.
056: *
057: * @param name The property name to use.
058: * @param value The property value to set.
059: * @see #PropertyHolder(String, String, String)
060: */
061: public PropertyHolder(String name, String value) {
062: this (name, value, null);
063: }
064:
065: /**
066: * Construct property holder.
067: *
068: * @param name The property name to use.
069: * @param value The property value to set.
070: * @param expectation The expected String to occur in tag output caused by setting given tag property. If
071: * <tt>null</tt>, will be set to <pre>name + "=\"" + value + "\"</pre>.
072: */
073: public PropertyHolder(String name, String value,
074: String expectation) {
075: this .name = name;
076: this .value = value;
077: if (expectation != null) {
078: this .expectation = expectation;
079: } else {
080: this .expectation = name + "=\"" + value + "\"";
081: }
082: }
083:
084: /**
085: * Convenience method for easily adding anonymous constructed instance to a given map, with {@link #getName()}
086: * as key.
087: *
088: * @param map The map to place this instance in.
089: */
090: public void addToMap(Map map) {
091: if (map != null) {
092: map.put(this .name, this );
093: }
094: }
095: }
096:
097: /**
098: * Simple Helper for setting bean properties. Although BeanUtils from oscore should provide bean property setting
099: * functionality, it does not work (at least with my JDK 1.5.0_05), failing in jdk's PropertyDescriptor constructor.
100: * This implementation works safely in any case, and does not add dependency on commons-beanutils for building.
101: * TODO: Check how we can remove this crap again.
102: *
103: * @author <a href="mailto:gielen@it-neering.net">Rene Gielen</a>
104: */
105: public class BeanHelper {
106: Map propDescriptors;
107: Object bean;
108:
109: public BeanHelper(Object bean) {
110: this .bean = bean;
111:
112: try {
113: PropertyDescriptor[] pds;
114: pds = Introspector.getBeanInfo(bean.getClass())
115: .getPropertyDescriptors();
116: propDescriptors = new HashMap(pds.length + 1, 1f);
117: for (int i = 0; i < pds.length; i++) {
118: propDescriptors.put(pds[i].getName(), pds[i]);
119: }
120: } catch (IntrospectionException e) {
121: e.printStackTrace();
122: }
123: }
124:
125: public void set(String name, Object value)
126: throws IllegalAccessException,
127: InvocationTargetException {
128: PropertyDescriptor pd = (PropertyDescriptor) propDescriptors
129: .get(name);
130:
131: if (pd != null) {
132: pd.getWriteMethod()
133: .invoke(bean, new Object[] { value });
134: }
135: }
136:
137: }
138:
139: /**
140: * Initialize a map of {@link PropertyHolder} for generic tag property testing. Will be used when calling {@link
141: * #verifyGenericProperties(com.opensymphony.webwork.views.jsp.ui.AbstractUITag, String, String[])} as properties to
142: * verify.<p/> This implementation defines testdata for all common AbstractUITag properties and may be overridden in
143: * subclasses.
144: *
145: * @return A Map of PropertyHolders values bound to {@link com.opensymphony.webwork.views.jsp.AbstractUITagTest.PropertyHolder#getName()}
146: * as key.
147: */
148: protected Map initializedGenericTagTestProperties() {
149: Map result = new HashMap();
150: new PropertyHolder("name", "someName").addToMap(result);
151: new PropertyHolder("id", "someId").addToMap(result);
152: new PropertyHolder("cssClass", "cssClass1",
153: "class=\"cssClass1\"").addToMap(result);
154: new PropertyHolder("cssStyle", "cssStyle1",
155: "style=\"cssStyle1\"").addToMap(result);
156: new PropertyHolder("title", "someTitle").addToMap(result);
157: new PropertyHolder("disabled", "true", "disabled=\"disabled\"")
158: .addToMap(result);
159: //new PropertyHolder("label", "label", "label=\"label\"").addToMap(result);
160: //new PropertyHolder("required", "someTitle").addToMap(result);
161: new PropertyHolder("tabindex", "99").addToMap(result);
162: new PropertyHolder("value", "someValue").addToMap(result);
163: new PropertyHolder("onclick", "onclick1").addToMap(result);
164: new PropertyHolder("ondblclick", "ondblclick1")
165: .addToMap(result);
166: new PropertyHolder("onmousedown", "onmousedown1")
167: .addToMap(result);
168: new PropertyHolder("onmouseup", "onmouseup1").addToMap(result);
169: new PropertyHolder("onmouseover", "onmouseover1")
170: .addToMap(result);
171: new PropertyHolder("onmousemove", "onmousemove1")
172: .addToMap(result);
173: new PropertyHolder("onmouseout", "onmouseout1")
174: .addToMap(result);
175: new PropertyHolder("onfocus", "onfocus1").addToMap(result);
176: new PropertyHolder("onblur", "onblur1").addToMap(result);
177: new PropertyHolder("onkeypress", "onkeypress1")
178: .addToMap(result);
179: new PropertyHolder("onkeydown", "onkeydown1").addToMap(result);
180: new PropertyHolder("onkeyup", "onkeyup1").addToMap(result);
181: new PropertyHolder("onclick", "onclick1").addToMap(result);
182: new PropertyHolder("onselect", "onchange").addToMap(result);
183: return result;
184: }
185:
186: /**
187: * Do a generic verification that setting certain properties on a tag causes expected output regarding this
188: * property. In most cases you would not call this directly, instead use {@link
189: * #verifyGenericProperties(com.opensymphony.webwork.views.jsp.ui.AbstractUITag, String, String[])}.
190: *
191: * @param tag The fresh created tag instance to test.
192: * @param theme The theme to use. If <tt>null</tt>, use configured default theme.
193: * @param propertiesToTest Map of {@link PropertyHolder}s, defining properties to test.
194: * @param exclude Names of properties to exclude from particular test.
195: * @throws Exception
196: */
197: public void verifyGenericProperties(AbstractUITag tag,
198: String theme, Map propertiesToTest, String[] exclude)
199: throws Exception {
200: if (tag != null && propertiesToTest != null) {
201: List excludeList;
202: if (exclude != null) {
203: excludeList = Arrays.asList(exclude);
204: } else {
205: excludeList = Collections.EMPTY_LIST;
206: }
207:
208: tag.setPageContext(pageContext);
209: if (theme != null) {
210: tag.setTheme(theme);
211: }
212:
213: BeanHelper beanHelper = new BeanHelper(tag);
214: Iterator it = propertiesToTest.values().iterator();
215: while (it.hasNext()) {
216: PropertyHolder propertyHolder = (PropertyHolder) it
217: .next();
218: if (!excludeList.contains(propertyHolder.getName())) {
219: beanHelper.set(propertyHolder.getName(),
220: propertyHolder.getValue());
221: }
222: }
223: tag.doStartTag();
224: tag.doEndTag();
225: String writerString = normalize(writer.toString(), true);
226: if (LOG.isInfoEnabled()) {
227: LOG
228: .info("AbstractUITagTest - [verifyGenericProperties]: Tag output is "
229: + writerString);
230: }
231:
232: assertTrue("Freemarker error detected in tag output: "
233: + writerString, writerString
234: .indexOf(FREEMARKER_ERROR_EXPECTATION) == -1);
235:
236: it = propertiesToTest.values().iterator();
237: while (it.hasNext()) {
238: PropertyHolder propertyHolder = (PropertyHolder) it
239: .next();
240: if (!excludeList.contains(propertyHolder.getName())) {
241: assertTrue("Expected to find: "
242: + propertyHolder.getExpectation(),
243: writerString.indexOf(propertyHolder
244: .getExpectation()) > -1);
245: }
246: }
247: }
248: }
249:
250: /**
251: * Do a generic verification that setting certain properties on a tag causes expected output regarding this
252: * property. Which properties to test with which expectations will be determined by the Map retrieved by {@link #initializedGenericTagTestProperties()}.
253: *
254: * @param tag The fresh created tag instance to test.
255: * @param theme The theme to use. If <tt>null</tt>, use configured default theme.
256: * @param exclude Names of properties to exclude from particular test.
257: * @throws Exception
258: */
259: public void verifyGenericProperties(AbstractUITag tag,
260: String theme, String[] exclude) throws Exception {
261: verifyGenericProperties(tag, theme,
262: initializedGenericTagTestProperties(), exclude);
263: }
264:
265: /**
266: * Attempt to verify the contents of this.writer against the contents of the URL specified. verify() performs a
267: * trim on both ends
268: *
269: * @param url the HTML snippet that we want to validate against
270: * @throws Exception if the validation failed
271: */
272: public void verify(URL url) throws Exception {
273: if (url == null) {
274: fail("unable to verify a null URL");
275: } else if (this .writer == null) {
276: fail("AbstractJspWriter.writer not initialized. Unable to verify");
277: }
278:
279: StringBuffer buffer = new StringBuffer(128);
280: InputStream in = url.openStream();
281: byte[] buf = new byte[4096];
282: int nbytes;
283:
284: while ((nbytes = in.read(buf)) > 0) {
285: buffer.append(new String(buf, 0, nbytes));
286: }
287:
288: in.close();
289:
290: /**
291: * compare the trimmed values of each buffer and make sure they're equivalent. however, let's make sure to
292: * normalize the strings first to account for line termination differences between platforms.
293: */
294: String writerString = normalize(writer.toString(), true);
295: String bufferString = normalize(buffer.toString(), true);
296:
297: assertEquals(bufferString, writerString);
298: }
299:
300: /**
301: * Attempt to verify the contents of this.writer against the contents of the URL specified. verify() performs a
302: * trim on both ends
303: *
304: * @param url the HTML snippet that we want to validate against
305: * @throws Exception if the validation failed
306: */
307: public void verify(URL url, String[] excluded) throws Exception {
308: if (url == null) {
309: fail("unable to verify a null URL");
310: } else if (this .writer == null) {
311: fail("AbstractJspWriter.writer not initialized. Unable to verify");
312: }
313:
314: StringBuffer buffer = new StringBuffer(128);
315: InputStream in = url.openStream();
316: byte[] buf = new byte[4096];
317: int nbytes;
318:
319: while ((nbytes = in.read(buf)) > 0) {
320: buffer.append(new String(buf, 0, nbytes));
321: }
322:
323: in.close();
324:
325: /**
326: * compare the trimmed values of each buffer and make sure they're equivalent. however, let's make sure to
327: * normalize the strings first to account for line termination differences between platforms.
328: */
329: String writerString = normalize(writer.toString(), true);
330: String bufferString = normalize(buffer.toString(), true);
331:
332: assertEquals(bufferString, writerString);
333: }
334:
335: protected void setUp() throws Exception {
336: super .setUp();
337:
338: ServletActionContext.setServletContext(pageContext
339: .getServletContext());
340: }
341:
342: protected void tearDown() throws Exception {
343: super .tearDown();
344: ActionContext.setContext(null);
345: }
346:
347: /**
348: * normalizes a string so that strings generated on different platforms can be compared. any group of one or more
349: * space, tab, \r, and \n characters are converted to a single space character
350: *
351: * @param obj the object to be normalized. normalize will perform its operation on obj.toString().trim() ;
352: * @param appendSpace
353: * @return the normalized string
354: */
355: public static String normalize(Object obj, boolean appendSpace) {
356: StringTokenizer st = new StringTokenizer(obj.toString().trim(),
357: " \t\r\n");
358: StringBuffer buffer = new StringBuffer(128);
359:
360: while (st.hasMoreTokens()) {
361: buffer.append(st.nextToken());
362:
363: /*
364: if (appendSpace && st.hasMoreTokens()) {
365: buffer.append("");
366: }
367: */
368: }
369:
370: return buffer.toString();
371: }
372: }
|