001    // Copyright 2004, 2005 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.junit.script;
016    
017    import static org.easymock.EasyMock.expect;
018    
019    import java.util.Arrays;
020    import java.util.Collections;
021    import java.util.HashMap;
022    import java.util.Map;
023    
024    import org.apache.hivemind.ClassResolver;
025    import org.apache.hivemind.Resource;
026    import org.apache.hivemind.impl.DefaultClassResolver;
027    import org.apache.hivemind.util.ClasspathResource;
028    import org.apache.tapestry.IRequestCycle;
029    import org.apache.tapestry.IScript;
030    import org.apache.tapestry.Tapestry;
031    import org.apache.tapestry.engine.RequestCycle;
032    import org.apache.tapestry.enhance.ClassFactoryImpl;
033    import org.apache.tapestry.junit.TapestryTestCase;
034    import org.apache.tapestry.script.ScriptParser;
035    import org.apache.tapestry.script.ScriptSession;
036    import org.apache.tapestry.script.ScriptSessionImpl;
037    import org.apache.tapestry.services.ExpressionEvaluator;
038    import org.apache.tapestry.services.impl.ExpressionCacheImpl;
039    import org.apache.tapestry.services.impl.ExpressionEvaluatorImpl;
040    import org.apache.tapestry.spec.IApplicationSpecification;
041    import org.apache.tapestry.util.xml.DocumentParseException;
042    import org.testng.annotations.AfterMethod;
043    import org.testng.annotations.Test;
044    
045    /**
046     * A collection of tests for Tapestry scripting.
047     * 
048     * @author Howard Lewis Ship
049     * @since 2.2
050     */
051    @Test(sequential=true)
052    public class TestScript extends TapestryTestCase
053    {
054        private MockScriptProcessor _processor = new MockScriptProcessor();
055    
056        private ExpressionEvaluatorImpl _eval;
057        
058        @AfterMethod
059        public void reset()
060        {
061            _processor.reset();
062        }
063        
064        protected ExpressionEvaluator createExpressionEvaluator()
065        {
066            IApplicationSpecification spec = newMock(IApplicationSpecification.class);
067            
068            expect(spec.checkExtension(Tapestry.OGNL_TYPE_CONVERTER)).andReturn(false);
069            
070            ExpressionCacheImpl cache = new ExpressionCacheImpl();
071            _eval = new ExpressionEvaluatorImpl();
072            _eval.setExpressionCache(cache);
073            _eval.setClassFactory(new ClassFactoryImpl());
074            
075            cache.setEvaluator(_eval);
076            
077            _eval.setApplicationSpecification(spec);
078            _eval.setContributions(Collections.EMPTY_LIST);
079            _eval.setNullHandlerContributions(Collections.EMPTY_LIST);
080            
081            return _eval;
082        }
083    
084        private IScript read(String file) throws DocumentParseException
085        {
086            ClassResolver resolver = new DefaultClassResolver();
087            ScriptParser parser = new ScriptParser(resolver, createExpressionEvaluator(), null);
088    
089            String classAsPath = "/" + getClass().getName().replace('.', '/');
090    
091            Resource classLocation = new ClasspathResource(resolver, classAsPath);
092            Resource scriptLocation = classLocation.getRelativeResource(file);
093    
094            return parser.parse(scriptLocation);
095        }
096    
097        private IScript execute(String file, Map symbols) throws DocumentParseException
098        {
099            IScript script = read(file);
100    
101            IRequestCycle cycle = newMock(IRequestCycle.class);
102            
103            replay();
104            
105            _eval.initializeService();
106            
107            script.execute(cycle, _processor, symbols);
108    
109            verify();
110    
111            return script;
112        }
113    
114        private void assertSymbol(Map symbols, String key, Object expected)
115        {
116            Object actual = symbols.get(key);
117    
118            assertEquals(expected, actual);
119        }
120    
121        /**
122         * Simple test where the body and initialization are static.
123         */
124    
125        public void testSimple() throws Exception
126        {
127            execute("simple.script", null);
128            
129            assertEquals("\nBODY\n", _processor.getBody());
130            assertEquals("\nINITIALIZATION\n", _processor.getInitialization());
131            
132            assert _processor.getExternalScripts() == null 
133            || _processor.getExternalScripts().length == 0;
134        }
135    
136        /**
137         * Test the <unique> element, new in the 1.3 DTD
138         * 
139         * @since 3.0
140         */
141    
142        public void testUnique() throws Exception
143        {
144            IScript script = read("unique.script");
145    
146            IRequestCycle cycle = new RequestCycle();
147    
148            script.execute(cycle, _processor, null);
149            script.execute(cycle, _processor, null);
150    
151            assertEquals("Block1\nBlock2\nNotUnique\n\n\n\nNotUnique", _processor.getBody().trim());
152        }
153    
154        /**
155         * Test omitting body and initialization, ensure they return null.
156         */
157    
158        public void testEmpty() throws Exception
159        {
160            execute("empty.script", null);
161            
162            assert _processor.getBody() == null || _processor.getBody().length() == 0;
163            assert _processor.getInitialization() == null || _processor.getInitialization().length() == 0;
164        }
165    
166        /**
167         * Test the ability of the let element to create an output symbol. Also, test the insert
168         * element.
169         */
170    
171        public void testLet() throws Exception
172        {
173            String inputSymbol = Long.toHexString(System.currentTimeMillis());
174            Map symbols = new HashMap();
175            symbols.put("inputSymbol", inputSymbol);
176    
177            execute("let.script", symbols);
178    
179            // Unlike body, the let element trims whitespace.
180    
181            String outputSymbol = "output: " + inputSymbol;
182    
183            assertEquals(outputSymbol, symbols.get("outputSymbol"));
184        }
185    
186        /**
187         * Test the unique attribute on the <let> element. New in the 1.3 DTD
188         * 
189         * @since 3.0
190         */
191        public void testUniqueLet() throws Exception
192        {
193            Map symbols = new HashMap();
194    
195            execute("unique-let.script", symbols);
196    
197            assertSymbol(symbols, "alpha", "Alpha");
198            assertSymbol(symbols, "beta", "Alpha_0");
199            assertSymbol(symbols, "gamma", "Alpha_1");
200            
201            assertSymbol(symbols, "id1", "form1_field");
202            assertSymbol(symbols, "id2", "form1_field_0");
203            assertSymbol(symbols, "id3", "form1_field_last_name");
204        }
205    
206        public void testIncludeScript() throws Exception
207        {
208            IScript script = execute("include-script.script", null);
209    
210            Resource scriptLocation = script.getScriptResource();
211    
212            Resource[] expected = new Resource[]
213            { scriptLocation.getRelativeResource("first"),
214                    scriptLocation.getRelativeResource("second"),
215                    scriptLocation.getRelativeResource("third") };
216    
217            assertEquals(Arrays.asList(expected), Arrays.asList(_processor
218                    .getExternalScripts()));
219        }
220    
221        public void testAntSyntax() throws Exception
222        {
223            Map form = new HashMap();
224    
225            form.put("name", "gallahad");
226    
227            Map component = new HashMap();
228            component.put("form", form);
229            component.put("name", "lancelot");
230    
231            Map symbols = new HashMap();
232            symbols.put("component", component);
233    
234            execute("ant-syntax.script", symbols);
235    
236            assertSymbol(symbols, "functionName", "gallahad_lancelot");
237            assertSymbol(symbols, "incomplete1", "Incomplete: $");
238            assertSymbol(symbols, "incomplete2", "Incomplete: ${");
239            assertSymbol(symbols, "nopath", "This ${} ends up as literal.");
240            assertSymbol(symbols, "OGNL", "This is a brace: }.");
241        }
242    
243        public void testSet() throws Exception
244        {
245            Map symbols = new HashMap();
246    
247            execute("set.script", symbols);
248    
249            assertSymbol(symbols, "element2", new Character('p'));
250        }
251    
252        public void testInvalidKeyLet() throws Exception
253        {
254            try
255            {
256                execute("invalid-key-let.script", new HashMap());
257    
258                unreachable();
259            }
260            catch (DocumentParseException ex)
261            {
262                checkException(ex, "key");
263            }
264        }
265    
266        public void testInvalidKeySet() throws Exception
267        {
268            try
269            {
270                execute("invalid-key-set.script", new HashMap());
271    
272                unreachable();
273            }
274            catch (DocumentParseException ex)
275            {
276                checkException(ex, "key");
277            }
278        }
279    
280        public void testInputSymbolClass() throws Exception
281        {
282            try
283            {
284                Map symbols = new HashMap();
285                symbols.put("input", new Integer(20));
286    
287                execute("input-symbol-class.script", symbols);
288    
289                unreachable();
290            }
291            catch (Exception ex)
292            {
293                checkException(ex, "Integer");
294                checkException(ex, "Long");
295            }
296        }
297    
298        public void testInputSymbol() throws Exception
299        {
300            Map symbols = new HashMap();
301            symbols.put("input", new Long(20));
302    
303            execute("input-symbol.script", symbols);
304    
305            assertSymbol(symbols, "success", "Success");
306        }
307    
308        public void testInputSymbolRequired() throws Exception
309        {
310            try
311            {
312                execute("input-symbol-required.script", new HashMap());
313    
314                unreachable();
315            }
316            catch (Exception ex)
317            {
318                checkException(ex, "required");
319            }
320        }
321    
322        public void testInputSymbolInvalidKey() throws Exception
323        {
324            try
325            {
326                execute("input-symbol-invalid-key.script", new HashMap());
327    
328                unreachable();
329            }
330            catch (DocumentParseException ex)
331            {
332                checkException(ex, "key");
333            }
334    
335        }
336    
337        /** @since 3.0 */
338    
339        public void testNameAppend() throws Exception
340        {
341            Map symbols = new HashMap();
342    
343            symbols.put("name", "fred");
344            execute("name-append.script", symbols);
345    
346            assertSymbol(symbols, "output", "fred$suffix");
347        }
348    
349        /**
350         * A bunch of quickies to push up the code coverage numbers.
351         */
352        public void testCheats() throws Exception
353        {
354            IScript script = execute("simple.script", null);
355    
356            ScriptSession session = new ScriptSessionImpl(script.getScriptResource(), null, null,
357                    createExpressionEvaluator(), null, null);
358            assertEquals("ScriptSession[" + script.getScriptResource() + "]", session.toString());
359        }
360    }