001    // Copyright 2005, 2006 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry.listener;
016    
017    import org.apache.hivemind.ApplicationRuntimeException;
018    import org.apache.tapestry.BaseComponentTestCase;
019    import org.apache.tapestry.IPage;
020    import org.apache.tapestry.IRequestCycle;
021    import org.apache.tapestry.engine.ILink;
022    import org.apache.tapestry.event.BrowserEvent;
023    import static org.easymock.EasyMock.expect;
024    import org.testng.annotations.Test;
025    
026    import java.lang.reflect.Method;
027    
028    /**
029     * Tests for {@link org.apache.tapestry.listener.ListenerMapSourceImpl} and
030     * {@link org.apache.tapestry.listener.ListenerMethodInvokerImpl}.
031     * 
032     * @author Howard M. Lewis Ship
033     * @since 4.0
034     */
035    @Test
036    public class TestListenerMapSource extends BaseComponentTestCase
037    {
038    
039        private IRequestCycle newLCycle(Object[] listenerParameters)
040        {
041            IRequestCycle cycle = newCycle();
042    
043            expect(cycle.getListenerParameters()).andReturn(listenerParameters);
044    
045            return cycle;
046        }
047    
048        private Method findMethod(Class clazz, String name)
049        {
050            Method[] methods = clazz.getMethods();
051    
052            for(int i = 0; i < methods.length; i++)
053            {
054                if (methods[i].getName().equals(name)) return methods[i];
055            }
056    
057            throw new IllegalArgumentException("No method '" + name + "' in " + clazz + ".");
058        }
059    
060        private void attemptReturnType(boolean expected, Class clazz, String methodName)
061        {
062            Method m = findMethod(clazz, methodName);
063    
064            ListenerMapSourceImpl lms = new ListenerMapSourceImpl();
065    
066            assertEquals(expected, lms.isAcceptibleListenerMethodReturnType(m));
067        }
068    
069        public void testAcceptibleListenerMethodReturnTypes()
070        {
071            Class clazz = ListenerMethodHolder.class;
072    
073            attemptReturnType(true, clazz, "fred");
074            attemptReturnType(true, clazz, "returnsString");
075            attemptReturnType(true, clazz, "returnsBasePage");
076            attemptReturnType(false, clazz, "returnsObject");
077            attemptReturnType(false, clazz, "returnsInt");
078            attemptReturnType(true, clazz, "returnsLink");
079        }
080    
081        public void testFoundWithParameters()
082        {
083            IRequestCycle cycle = newLCycle(new Object[] { "Hello", new Integer(7) });
084            ListenerMethodHolder holder = newHolder();
085    
086            holder.fred("Hello", 7);
087    
088            replay();
089    
090            ListenerMapSource source = new ListenerMapSourceImpl();
091    
092            ListenerMap map = source.getListenerMapForObject(holder);
093    
094            map.getListener("fred").actionTriggered(null, cycle);
095    
096            verify();
097        }
098    
099        public void testFoundWithCycleAndParameters()
100        {
101            IRequestCycle cycle = newLCycle(new Object[] { new Integer(7) });
102            ListenerMethodHolder holder = newHolder();
103    
104            holder.wilma(cycle, 7);
105    
106            replay();
107    
108            ListenerMapSource source = new ListenerMapSourceImpl();
109    
110            ListenerMap map = source.getListenerMapForObject(holder);
111    
112            map.getListener("wilma").actionTriggered(null, cycle);
113    
114            verify();
115        }
116        
117        public void testFoundWithAllParameters()
118        {
119            BrowserEvent event = new BrowserEvent("onClick", null);
120            IRequestCycle cycle = newLCycle(new Object[] { new Integer(8), event });
121            ListenerMethodHolder holder = newHolder();
122            
123            holder.bangbangClicked(cycle, event, 8);
124            
125            replay();
126            
127            ListenerMapSource source = new ListenerMapSourceImpl();
128            
129            ListenerMap map = source.getListenerMapForObject(holder);
130            
131            map.getListener("bangbangClicked").actionTriggered(null, cycle);
132    
133            verify();
134        }
135        
136        /**
137         * No exact match on parameter count, fall through to the no-arguments
138         * method implementation.
139         */
140    
141        public void testNoParameterMatch()
142        {
143            IRequestCycle cycle = newLCycle(new Object[] { "Hello", new Integer(7) });
144            ListenerMethodHolder holder = newHolder();
145    
146            holder.barney();
147    
148            replay();
149    
150            ListenerMapSource source = new ListenerMapSourceImpl();
151    
152            ListenerMap map = source.getListenerMapForObject(holder);
153    
154            map.getListener("barney").actionTriggered(null, cycle);
155    
156            verify();
157        }
158    
159        public void testFallbackToJustCycle()
160        {
161            IRequestCycle cycle = newLCycle(new Object[] { "Hello", new Integer(7) });
162    
163            ListenerMethodHolder holder = newHolder();
164    
165            holder.pebbles(cycle);
166    
167            replay();
168    
169            ListenerMapSource source = new ListenerMapSourceImpl();
170    
171            ListenerMap map = source.getListenerMapForObject(holder);
172    
173            map.getListener("pebbles").actionTriggered(null, cycle);
174    
175            verify();
176        }
177    
178        public void testReturnPageName()
179        {
180            IRequestCycle cycle = newLCycle(null);
181            ListenerMethodHolder holder = new ListenerMethodHolder("PageName");
182    
183            cycle.activate("PageName");
184    
185            replay();
186    
187            ListenerMapSource source = new ListenerMapSourceImpl();
188    
189            ListenerMap map = source.getListenerMapForObject(holder);
190    
191            map.getListener("returnsPageName").actionTriggered(null, cycle);
192    
193            verify();
194        }
195    
196        public void testReturnLink()
197        {
198            IRequestCycle cycle = newLCycle(null);
199            
200            ILink link = newLink("http://foo/bar");
201            
202            cycle.sendRedirect("http://foo/bar");
203            
204            ListenerMethodHolder holder = new ListenerMethodHolder(link);
205    
206            replay();
207    
208            ListenerMapSource source = new ListenerMapSourceImpl();
209    
210            ListenerMap map = source.getListenerMapForObject(holder);
211    
212            map.getListener("returnsLink").actionTriggered(null, cycle);
213    
214            verify();
215        }
216    
217        private ILink newLink(String absoluteURL)
218        {
219            ILink link = newMock(ILink.class);
220    
221            expect(link.getAbsoluteURL()).andReturn(absoluteURL);
222    
223            return link;
224        }
225    
226        public void testReturnPageInstance()
227        {
228            IPage page = newMock(IPage.class);
229            IRequestCycle cycle = newLCycle(null);
230            ListenerMethodHolder holder = new ListenerMethodHolder(page);
231    
232            cycle.activate(page);
233    
234            replay();
235    
236            ListenerMapSource source = new ListenerMapSourceImpl();
237    
238            ListenerMap map = source.getListenerMapForObject(holder);
239    
240            map.getListener("returnsPage").actionTriggered(null, cycle);
241    
242            verify();
243        }
244    
245        @Test(expectedExceptions = ApplicationRuntimeException.class)
246        public void test_No_Match()
247        {
248            IRequestCycle cycle = newLCycle(new Object[] { "Hello", new Integer(7) });
249    
250            replay();
251    
252            ListenerMethodHolder holder = new ListenerMethodHolder();
253    
254            ListenerMapSource source = new ListenerMapSourceImpl();
255    
256            ListenerMap map = source.getListenerMapForObject(holder);
257    
258            map.getListener("noMatchFound").actionTriggered(null, cycle);
259    
260            verify();
261        }
262    
263        public void testMismatchedTypes()
264        {
265            IRequestCycle cycle = newLCycle(new Object[] { "Hello" });
266    
267            replay();
268    
269            ListenerMethodHolder holder = new ListenerMethodHolder();
270    
271            ListenerMapSource source = new ListenerMapSourceImpl();
272    
273            ListenerMap map = source.getListenerMapForObject(holder);
274    
275            try
276            {
277                map.getListener("wrongTypes").actionTriggered(null, cycle);
278                unreachable();
279            }
280            catch (ApplicationRuntimeException ex)
281            {
282                assertEquals(IllegalArgumentException.class, ex.getRootCause().getClass());
283                assertTrue(ex.getMessage().startsWith(
284                        "Failure invoking listener method 'public void "
285                                + "org.apache.tapestry.listener.ListenerMethodHolder."
286                                + "wrongTypes(java.util.Map)' on ListenerMethodHolder:"));
287                
288                // TODO: IBM jre doesn't format these messages the same as sun's
289                // jre,
290                // IBM's message has no message string source for the
291                // IllegalArgumentException
292                assertSame(holder, ex.getComponent());
293            }
294    
295            verify();
296        }
297    
298        public void testInvocationTargetException()
299        {
300            IRequestCycle cycle = newLCycle(new Object[] { "Hello", new Integer(7) });
301    
302            ListenerMethodHolder holder = new ListenerMethodHolder();
303    
304            RuntimeException exception = new IllegalArgumentException("Just for kicks.");
305    
306            holder.setException(exception);
307    
308            replay();
309    
310            ListenerMapSource source = new ListenerMapSourceImpl();
311    
312            ListenerMap map = source.getListenerMapForObject(holder);
313    
314            try
315            {
316                map.getListener("throwsException").actionTriggered(null, cycle);
317                unreachable();
318            }
319            catch (ApplicationRuntimeException ex)
320            {
321                assertEquals(
322                        "Failure invoking listener method 'public void org.apache.tapestry.listener.ListenerMethodHolder.throwsException()' on ListenerMethodHolder: Just for kicks.",
323                        ex.getMessage());
324                assertSame(holder, ex.getComponent());
325                assertSame(exception, ex.getRootCause());
326            }
327    
328            verify();
329        }
330    
331        public void testInvocationTargetExceptionForApplicationRuntimeException()
332        {
333            IRequestCycle cycle = newLCycle(new Object[] { "Hello", new Integer(7) });
334    
335            ListenerMethodHolder holder = new ListenerMethodHolder();
336    
337            RuntimeException exception = new ApplicationRuntimeException("Just for kicks.");
338    
339            holder.setException(exception);
340    
341            replay();
342    
343            ListenerMapSource source = new ListenerMapSourceImpl();
344    
345            ListenerMap map = source.getListenerMapForObject(holder);
346    
347            try
348            {
349                map.getListener("throwsException").actionTriggered(null, cycle);
350                unreachable();
351            }
352            catch (ApplicationRuntimeException ex)
353            {
354                assertSame(exception, ex);
355            }
356    
357            verify();
358        }
359    
360        private ListenerMethodHolder newHolder()
361        {
362            return (ListenerMethodHolder)newInstance(ListenerMethodHolder.class);
363        }
364    }