001: // Copyright 2006, 2007 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.internal.services;
016:
017: import javassist.ClassPool;
018: import javassist.CtClass;
019: import javassist.Loader;
020: import javassist.LoaderClassPath;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.tapestry.Binding;
024: import org.apache.tapestry.TapestryConstants;
025: import org.apache.tapestry.internal.InternalComponentResources;
026: import org.apache.tapestry.internal.test.InternalBaseTestCase;
027: import org.apache.tapestry.ioc.internal.services.PropertyAccessImpl;
028: import org.apache.tapestry.ioc.services.PropertyAccess;
029: import org.apache.tapestry.model.MutableComponentModel;
030: import org.apache.tapestry.runtime.Component;
031: import org.apache.tapestry.services.BindingSource;
032: import org.testng.annotations.AfterClass;
033: import org.testng.annotations.Test;
034:
035: /**
036: * There's no point in trying to unit test the code generated by
037: * {@link org.apache.tapestry.internal.services.ParameterWorker}. Instead, we excercize
038: * ParameterWorker, and test that the generated code works correctly in a number of scenarios.
039: */
040: public class ParameterWorkerTest extends InternalBaseTestCase {
041: private final ClassLoader _contextClassLoader = Thread
042: .currentThread().getContextClassLoader();
043:
044: private PropertyAccess _access = new PropertyAccessImpl();
045:
046: /**
047: * Accessed by DefaultParameerBindingMethodComponent.
048: */
049: public static Binding _binding;
050:
051: @AfterClass
052: public void cleanup() {
053: _access = null;
054: _binding = null;
055: }
056:
057: @Test
058: public void page_load_behavior() throws Exception {
059: InternalComponentResources resources = mockInternalComponentResources();
060:
061: assertNotNull(setupForIntegrationTest(resources));
062: }
063:
064: @Test
065: public void invariant_object_retained_after_detach()
066: throws Exception {
067: InternalComponentResources resources = mockInternalComponentResources();
068:
069: Component component = setupForIntegrationTest(resources);
070:
071: // On first invocation, the resources are queried.
072:
073: String value = "To be in Tapestry in the spring time ...";
074:
075: train_isLoaded(resources, true);
076: train_isBound(resources, "invariantObject", true);
077: train_readParameter(resources, "invariantObject", String.class,
078: value);
079:
080: replay();
081:
082: assertSame(_access.get(component, "invariantObject"), value);
083:
084: verify();
085:
086: // No further training needed here.
087:
088: replay();
089:
090: // Still cached ...
091:
092: assertSame(_access.get(component, "invariantObject"), value);
093:
094: component.postRenderCleanup();
095:
096: // Still cached ...
097:
098: assertSame(_access.get(component, "invariantObject"), value);
099:
100: component.containingPageDidDetach();
101:
102: // Still cached ...
103:
104: assertSame(_access.get(component, "invariantObject"), value);
105:
106: verify();
107: }
108:
109: @Test
110: public void invariant_primitive_retained_after_detach()
111: throws Exception {
112: InternalComponentResources resources = mockInternalComponentResources();
113:
114: Component component = setupForIntegrationTest(resources);
115:
116: // On first invocation, the resources are queried.
117:
118: long value = 123456;
119:
120: train_isLoaded(resources, true);
121: train_isBound(resources, "invariantPrimitive", true);
122: train_readParameter(resources, "invariantPrimitive",
123: long.class, value);
124:
125: replay();
126:
127: assertEquals(_access.get(component, "invariantPrimitive"),
128: value);
129:
130: verify();
131:
132: // No further training needed here.
133:
134: replay();
135:
136: // Still cached ...
137:
138: assertEquals(_access.get(component, "invariantPrimitive"),
139: value);
140:
141: component.postRenderCleanup();
142:
143: // Still cached ...
144:
145: assertEquals(_access.get(component, "invariantPrimitive"),
146: value);
147:
148: verify();
149: }
150:
151: /**
152: * This actually checks several things:
153: * <ul>
154: * <li>Changing a parameter property before the page loads doesn't update the binding</li>
155: * <li>Changing a parameter property changes the property AND the default value for the
156: * property</li>
157: * <li>Unbound parameters to do not attempt to read or update their bindings (they'll be
158: * optional)</li>
159: * </ul>
160: *
161: * @throws Exception
162: */
163: @Test
164: public void changes_before_load_become_defaults_and_dont_update_bindings()
165: throws Exception {
166: InternalComponentResources resources = mockInternalComponentResources();
167:
168: Component component = setupForIntegrationTest(resources);
169:
170: train_isLoaded(resources, false);
171:
172: replay();
173:
174: assertNull(_access.get(component, "object"));
175:
176: verify();
177:
178: train_isLoaded(resources, false);
179:
180: replay();
181:
182: _access.set(component, "object", "new-default");
183:
184: verify();
185:
186: train_isLoaded(resources, false);
187:
188: replay();
189:
190: assertEquals(_access.get(component, "object"), "new-default");
191:
192: verify();
193:
194: trainForPageDidLoad(resources);
195:
196: replay();
197:
198: component.containingPageDidLoad();
199:
200: verify();
201:
202: // For the set ...
203:
204: train_isLoaded(resources, true);
205: train_isBound(resources, "object", false);
206: train_isRendering(resources, false);
207:
208: // For the first read ...
209:
210: train_isLoaded(resources, true);
211: train_isBound(resources, "object", false);
212:
213: // For the second read (after postRenderCleanup) ...
214:
215: train_isLoaded(resources, true);
216: train_isBound(resources, "object", false);
217:
218: replay();
219:
220: _access.set(component, "object", "new-value");
221: assertEquals(_access.get(component, "object"), "new-value");
222:
223: component.postRenderCleanup();
224:
225: assertEquals(_access.get(component, "object"), "new-default");
226:
227: verify();
228: }
229:
230: @Test
231: public void cached_object_read() throws Exception {
232: InternalComponentResources resources = mockInternalComponentResources();
233:
234: Component component = setupForIntegrationTest(resources);
235:
236: train_isLoaded(resources, true);
237: train_isBound(resources, "object", true);
238: train_readParameter(resources, "object", String.class, "first");
239: train_isRendering(resources, false);
240:
241: replay();
242:
243: assertEquals(_access.get(component, "object"), "first");
244:
245: verify();
246:
247: // Keeps re-reading the parameter when not rendering.
248:
249: train_isLoaded(resources, true);
250: train_isBound(resources, "object", true);
251: train_readParameter(resources, "object", String.class, "second");
252: train_isRendering(resources, false);
253:
254: replay();
255:
256: assertEquals(_access.get(component, "object"), "second");
257:
258: verify();
259:
260: // Now, when rendering is active, the value is cached
261:
262: train_isLoaded(resources, true);
263: train_isBound(resources, "object", true);
264: train_readParameter(resources, "object", String.class, "third");
265: train_isRendering(resources, true);
266:
267: replay();
268:
269: assertEquals(_access.get(component, "object"), "third");
270:
271: // Does not cause readParameter() to be invoked:
272:
273: assertEquals(_access.get(component, "object"), "third");
274:
275: verify();
276:
277: train_isLoaded(resources, true);
278: train_isBound(resources, "object", true);
279: train_readParameter(resources, "object", String.class, "fourth");
280: train_isRendering(resources, false);
281:
282: replay();
283:
284: component.postRenderCleanup();
285:
286: assertEquals(_access.get(component, "object"), "fourth");
287:
288: verify();
289: }
290:
291: @Test
292: public void cached_object_write() throws Exception {
293: InternalComponentResources resources = mockInternalComponentResources();
294:
295: Component component = setupForIntegrationTest(resources);
296:
297: train_isLoaded(resources, true);
298: train_isBound(resources, "object", true);
299: resources.writeParameter("object", "first");
300: train_isRendering(resources, false);
301:
302: train_isLoaded(resources, true);
303: train_isBound(resources, "object", true);
304: train_readParameter(resources, "object", String.class, "second");
305: train_isRendering(resources, false);
306:
307: replay();
308:
309: _access.set(component, "object", "first");
310: assertEquals(_access.get(component, "object"), "second");
311:
312: verify();
313:
314: // Now try during rendering ...
315:
316: train_isLoaded(resources, true);
317: train_isBound(resources, "object", true);
318: resources.writeParameter("object", "third");
319: train_isRendering(resources, true);
320:
321: replay();
322:
323: _access.set(component, "object", "third");
324: assertEquals(_access.get(component, "object"), "third");
325:
326: verify();
327:
328: // And the cached value is lost after rendering is complete.
329:
330: train_isLoaded(resources, true);
331: train_isBound(resources, "object", true);
332: train_readParameter(resources, "object", String.class, "fourth");
333: train_isRendering(resources, false);
334:
335: replay();
336:
337: component.postRenderCleanup();
338:
339: assertEquals(_access.get(component, "object"), "fourth");
340:
341: verify();
342: }
343:
344: @Test
345: public void cached_primitive_write() throws Exception {
346: InternalComponentResources resources = mockInternalComponentResources();
347:
348: Component component = setupForIntegrationTest(resources);
349:
350: train_isLoaded(resources, true);
351: train_isBound(resources, "primitive", true);
352: resources.writeParameter("primitive", 321);
353:
354: train_isRendering(resources, false);
355:
356: train_isLoaded(resources, true);
357: train_isBound(resources, "primitive", true);
358: train_readParameter(resources, "primitive", int.class, 123);
359: train_isRendering(resources, false);
360:
361: replay();
362:
363: _access.set(component, "primitive", 321);
364: assertEquals(_access.get(component, "primitive"), 123);
365:
366: verify();
367:
368: // Now try during rendering ...
369:
370: train_isLoaded(resources, true);
371: train_isBound(resources, "primitive", true);
372: resources.writeParameter("primitive", 567);
373: train_isRendering(resources, true);
374:
375: replay();
376:
377: _access.set(component, "primitive", 567);
378: assertEquals(_access.get(component, "primitive"), 567);
379:
380: verify();
381:
382: // And the cached value is lost after rendering is complete.
383:
384: train_isLoaded(resources, true);
385: train_isBound(resources, "primitive", true);
386: train_readParameter(resources, "primitive", int.class, 890);
387: train_isRendering(resources, false);
388:
389: replay();
390:
391: component.postRenderCleanup();
392:
393: assertEquals(_access.get(component, "primitive"), 890);
394:
395: verify();
396: }
397:
398: @Test
399: public void uncached_object_read() throws Exception {
400: InternalComponentResources resources = mockInternalComponentResources();
401:
402: Component component = setupForIntegrationTest(resources);
403:
404: // Notice no check for isRendering() since that is irrelevant to uncached parameters.
405: // Also note difference between field name and parameter name, due to Parameter.name() being
406: // specified.
407:
408: train_isLoaded(resources, true);
409: train_isBound(resources, "uncached", true);
410: train_readParameter(resources, "uncached", String.class,
411: "first");
412: train_isLoaded(resources, true);
413: train_isBound(resources, "uncached", true);
414: train_readParameter(resources, "uncached", String.class,
415: "second");
416:
417: replay();
418:
419: assertEquals(_access.get(component, "uncachedObject"), "first");
420: assertEquals(_access.get(component, "uncachedObject"), "second");
421:
422: verify();
423: }
424:
425: protected void train_isBound(InternalComponentResources resources,
426: String parameterName, boolean isBound) {
427: expect(resources.isBound(parameterName)).andReturn(isBound);
428: }
429:
430: @Test
431: public void uncached_object_write() throws Exception {
432: InternalComponentResources resources = mockInternalComponentResources();
433:
434: Component component = setupForIntegrationTest(resources);
435:
436: // Notice no check for isRendering() since that is irrelevant to uncached parameters.
437: // Also note difference between field name and parameter name, due to Parameter.name() being
438: // specified.
439:
440: train_isLoaded(resources, true);
441: train_isBound(resources, "uncached", true);
442: resources.writeParameter("uncached", "first");
443:
444: train_isLoaded(resources, true);
445: train_isBound(resources, "uncached", true);
446: train_readParameter(resources, "uncached", String.class,
447: "second");
448:
449: replay();
450:
451: _access.set(component, "uncachedObject", "first");
452: assertEquals(_access.get(component, "uncachedObject"), "second");
453:
454: verify();
455: }
456:
457: @Test
458: public void parameter_with_default() throws Exception {
459: final BindingSource source = mockBindingSource();
460: final InternalComponentResources resources = mockInternalComponentResources();
461: final Binding binding = mockBinding();
462: String boundValue = "howdy!";
463:
464: MutableComponentModel model = mockMutableComponentModel();
465:
466: model.addParameter("value", false,
467: TapestryConstants.PROP_BINDING_PREFIX);
468:
469: Runnable phaseTwoTraining = new Runnable() {
470: public void run() {
471: train_isBound(resources, "value", false);
472:
473: expect(
474: source.newBinding("default value", resources,
475: TapestryConstants.PROP_BINDING_PREFIX,
476: "literal:greeting")).andReturn(binding);
477:
478: resources.bindParameter("value", binding);
479:
480: train_isInvariant(resources, "value", true);
481:
482: };
483: };
484:
485: Component component = setupForIntegrationTest(resources,
486: mockLog(), DefaultParameterComponent.class.getName(),
487: model, source, phaseTwoTraining);
488:
489: train_isLoaded(resources, true);
490: train_isBound(resources, "value", true);
491: train_readParameter(resources, "value", String.class,
492: boundValue);
493:
494: replay();
495:
496: assertEquals(_access.get(component, "value"), boundValue);
497:
498: verify();
499: }
500:
501: @Test
502: public void default_binding_method() throws Exception {
503: BindingSource source = mockBindingSource();
504: final InternalComponentResources resources = mockInternalComponentResources();
505: _binding = mockBinding();
506: String boundValue = "yowza!";
507:
508: MutableComponentModel model = mockMutableComponentModel();
509:
510: model.addParameter("value", false,
511: TapestryConstants.PROP_BINDING_PREFIX);
512:
513: Runnable phaseTwoTraining = new Runnable() {
514: public void run() {
515: train_isBound(resources, "value", false);
516:
517: // How can this happen? Only if the generated code invokes defaultValue().
518:
519: resources.bindParameter("value", _binding);
520:
521: train_isInvariant(resources, "value", true);
522: };
523: };
524:
525: Component component = setupForIntegrationTest(resources,
526: mockLog(), DefaultParameterBindingMethodComponent.class
527: .getName(), model, source, phaseTwoTraining);
528:
529: train_isLoaded(resources, true);
530: train_isBound(resources, "value", true);
531: train_readParameter(resources, "value", String.class,
532: boundValue);
533:
534: replay();
535:
536: assertEquals(_access.get(component, "value"), boundValue);
537:
538: verify();
539: }
540:
541: protected final void train_isRendering(
542: InternalComponentResources resources, boolean rendering) {
543: expect(resources.isRendering()).andReturn(rendering);
544: }
545:
546: protected final <T> void train_readParameter(
547: InternalComponentResources resources, String parameterName,
548: Class<T> expectedType, T value) {
549: expect(resources.readParameter(parameterName, expectedType))
550: .andReturn(value);
551: }
552:
553: /** This is for the majority of tests. */
554: private Component setupForIntegrationTest(
555: final InternalComponentResources resources)
556: throws Exception {
557: MutableComponentModel model = mockMutableComponentModel();
558:
559: model.addParameter("invariantObject", false,
560: TapestryConstants.PROP_BINDING_PREFIX);
561: model.addParameter("invariantPrimitive", false,
562: TapestryConstants.PROP_BINDING_PREFIX);
563: model.addParameter("object", false,
564: TapestryConstants.PROP_BINDING_PREFIX);
565: model.addParameter("primitive", true,
566: TapestryConstants.PROP_BINDING_PREFIX);
567: model.addParameter("uncached", false,
568: TapestryConstants.LITERAL_BINDING_PREFIX);
569:
570: Runnable phaseTwoTraining = new Runnable() {
571: public void run() {
572: trainForPageDidLoad(resources);
573: }
574: };
575:
576: return setupForIntegrationTest(resources, mockLog(),
577: ParameterComponent.class.getName(), model,
578: mockBindingSource(), phaseTwoTraining);
579: }
580:
581: private Component setupForIntegrationTest(
582: InternalComponentResources resources, Log log,
583: String componentClassName, MutableComponentModel model,
584: BindingSource source, Runnable phaseTwoTraining)
585: throws Exception {
586: ClassPool pool = new ClassPool();
587: ClassLoader contextLoader = Thread.currentThread()
588: .getContextClassLoader();
589: pool.appendClassPath(new LoaderClassPath(contextLoader));
590:
591: Loader loader = new Loader(contextLoader, pool);
592:
593: loader.delegateLoadingOf("org.apache.tapestry.");
594:
595: CtClass ctClass = pool.get(componentClassName);
596: InternalClassTransformation transformation = new InternalClassTransformationImpl(
597: ctClass, _contextClassLoader, log, null);
598:
599: replay();
600:
601: new ParameterWorker(source).transform(transformation, model);
602:
603: verify();
604:
605: transformation.finish();
606:
607: // System.out.println("Transformation: " + transformation);
608:
609: Class transformedClass = pool.toClass(ctClass, loader);
610:
611: Instantiator instantiator = transformation
612: .createInstantiator(transformedClass);
613:
614: phaseTwoTraining.run();
615:
616: replay();
617:
618: Component component = instantiator.newInstance(resources);
619:
620: component.containingPageDidLoad();
621:
622: verify();
623:
624: return component;
625: }
626:
627: private void trainForPageDidLoad(
628: InternalComponentResources resources) {
629: train_isInvariant(resources, "invariantObject", true);
630: train_isInvariant(resources, "invariantPrimitive", true);
631: train_isInvariant(resources, "object", false);
632: train_isInvariant(resources, "primitive", false);
633: train_isInvariant(resources, "uncached", false);
634: }
635:
636: protected final void train_isInvariant(
637: InternalComponentResources resources, String parameterName,
638: boolean invariant) {
639: expect(resources.isInvariant(parameterName)).andReturn(
640: invariant);
641: }
642: }
|