1   /*
2    * Copyright 2002-2005 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    * 
8    * http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package org.springframework.binding.value.support;
17  
18  import org.springframework.binding.support.BeanPropertyAccessStrategy;
19  import org.springframework.binding.support.TestBean;
20  import org.springframework.binding.support.TestPropertyChangeListener;
21  import org.springframework.binding.value.CommitTrigger;
22  import org.springframework.binding.value.ValueChangeDetector;
23  import org.springframework.binding.value.ValueModel;
24  import org.springframework.richclient.application.ApplicationServicesLocator;
25  import org.springframework.richclient.test.SpringRichTestCase;
26  
27  
28  /**
29   * Tests class {@link BufferedValueModel}.
30   * 
31   * @author Jeanette Winzenburg
32   * @author Karsten Lentzsch
33   * @author Oliver Hutchison
34   */
35  public final class BufferedValueModelTests extends SpringRichTestCase {
36  
37      private static final Object INITIAL_VALUE = "initial value";
38      private static final Object RESET_VALUE   = "reset value";
39  
40      private ValueModel wrapped;
41      private CommitTrigger commitTrigger;
42          
43      protected void doSetUp() throws Exception {
44          wrapped = new ValueHolder(INITIAL_VALUE);
45          commitTrigger = new CommitTrigger();
46      }
47      
48      public void testGetWrappedValueModel() {
49          BufferedValueModel buffer = createDefaultBufferedValueModel();
50          
51          assertSame(wrapped, buffer.getWrappedValueModel());
52          assertSame(wrapped, buffer.getInnerMostWrappedValueModel());
53          
54          ValueModel nestedValueModel = new AbstractValueModelWrapper(wrapped) {};
55          buffer = new BufferedValueModel(nestedValueModel);
56          assertSame(nestedValueModel, buffer.getWrappedValueModel());
57          assertSame(wrapped, buffer.getInnerMostWrappedValueModel());
58      }
59  
60      public void testReturnsWrappedValueIfNoValueAssigned() {
61          BufferedValueModel buffer = createDefaultBufferedValueModel();
62          assertEquals(
63              "Buffer value equals the wrapped value before any changes.",
64              buffer.getValue(),
65              wrapped.getValue());
66  
67          wrapped.setValue("change1");
68          assertEquals(
69              "Buffer value equals the wrapped value changes.",
70              buffer.getValue(),
71              wrapped.getValue());
72  
73          wrapped.setValue(null);
74          assertEquals(
75              "Buffer value equals the wrapped value changes.",
76              buffer.getValue(),
77              wrapped.getValue());
78  
79          wrapped.setValue("change2");
80          assertEquals(
81              "Buffer value equals the wrapped value changes.",
82              buffer.getValue(),
83              wrapped.getValue());
84      }
85  
86      /**
87       * Tests that the BufferedValueModel returns the buffered values
88       * once a value has been assigned. 
89       */
90      public void testReturnsBufferedValueIfValueAssigned() {
91          BufferedValueModel buffer = createDefaultBufferedValueModel();
92  
93          Object newValue1 = wrapped.getValue();
94          buffer.setValue(newValue1);
95          assertSame(
96              "Buffer value == new value once a value has been assigned.",
97              buffer.getValue(),
98              newValue1);
99  
100         Object newValue2 = "change1";
101         buffer.setValue(newValue2);
102         assertSame(
103             "Buffer value == new value once a value has been assigned.",
104             buffer.getValue(),
105             newValue2);
106 
107         Object newValue3 = null;
108         buffer.setValue(newValue3);
109         assertSame(
110             "Buffer value == new value once a value has been assigned.",
111             buffer.getValue(),
112             newValue3);
113         
114         Object newValue4 = "change2";
115         buffer.setValue(newValue4);
116         assertSame(
117             "Buffer value == new value once a value has been assigned.",
118             buffer.getValue(),
119             newValue4);
120     }
121 
122     public void testDetectedWrappedValueChangeIfValueAssigned() {
123         BufferedValueModel buffer = createDefaultBufferedValueModel();
124 
125         Object newValue1 = "change1";
126         buffer.setValue(newValue1);
127         wrapped.setValue("change3");
128         assertSame(
129             "Buffer value == new value once a value has been assigned.",
130             buffer.getValue(),
131             "change3");
132         wrapped.setValue(newValue1);
133         assertSame(
134             "Buffer value == new value once a value has been assigned.",
135             buffer.getValue(),
136             newValue1);
137         wrapped.setValue(null);
138         assertSame(
139             "Buffer value == new value once a value has been assigned.",
140             buffer.getValue(),
141             null);
142     }
143 
144     /**
145      * Tests that the BufferedValueModel returns the wrapped's values
146      * after a commit. 
147      */
148     public void testReturnsWrappedValueAfterCommit() {
149         BufferedValueModel buffer = createDefaultBufferedValueModel();
150         buffer.setValue("change1");  // shall buffer now
151         commit();
152         assertEquals(
153             "Buffer value equals the wrapped value after a commit.",
154             buffer.getValue(),
155             wrapped.getValue());
156 
157         wrapped.setValue("change2");
158         assertEquals(
159             "Buffer value equals the wrapped value after a commit.",
160             buffer.getValue(),
161             wrapped.getValue());
162 
163         wrapped.setValue(buffer.getValue());
164         assertEquals(
165             "Buffer value equals the wrapped value after a commit.",
166             buffer.getValue(),
167             wrapped.getValue());
168     }
169 
170     /**
171      * Tests that the BufferedValueModel returns the wrapped's values
172      * after a flush. 
173      */
174     public void testReturnsWrappedValueAfterFlush() {
175         BufferedValueModel buffer = createDefaultBufferedValueModel();
176         buffer.setValue("change1");  // shall buffer now
177         revert();
178         assertEquals(
179             "Buffer value equals the wrapped value after a flush.",
180             wrapped.getValue(),
181             buffer.getValue());
182 
183         wrapped.setValue("change2");
184         assertEquals(
185             "Buffer value equals the wrapped value after a flush.",
186             wrapped.getValue(),
187             buffer.getValue());
188     }
189 
190 
191     // Testing Proper Value Commit and Flush **********************************
192 
193     /**
194      * Tests the core of the buffering feature: buffer modifications 
195      * do not affect the wrapped before a commit.
196      */
197     public void testWrappedValuesUnchangedBeforeCommit() {
198         BufferedValueModel buffer = createDefaultBufferedValueModel();
199         Object oldWrappedValue = wrapped.getValue();
200         buffer.setValue("changedBuffer1");
201         assertEquals(
202             "Buffer changes do not change the wrapped value before a commit.",
203             wrapped.getValue(),
204             oldWrappedValue
205         );
206         buffer.setValue(null);
207         assertEquals(
208             "Buffer changes do not change the wrapped value before a commit.",
209             wrapped.getValue(),
210             oldWrappedValue
211         );
212         buffer.setValue(oldWrappedValue);
213         assertEquals(
214             "Buffer changes do not change the wrapped value before a commit.",
215             wrapped.getValue(),
216             oldWrappedValue
217         );
218         buffer.setValue("changedBuffer2");
219         assertEquals(
220             "Buffer changes do not change the wrapped value before a commit.",
221             wrapped.getValue(),
222             oldWrappedValue
223         );
224     }
225 
226     /**
227      * Tests the core of a commit: buffer changes are written through on commit
228      * and change the wrapped value.
229      */
230     public void testCommitChangesWrappedValue() {
231         BufferedValueModel buffer = createDefaultBufferedValueModel();
232         Object oldWrappedValue = wrapped.getValue();
233         Object newValue1 = "change1";
234         buffer.setValue(newValue1);
235         assertEquals(
236             "Wrapped value is unchanged before the first commit.",
237             wrapped.getValue(),
238             oldWrappedValue);
239         commit();
240         assertEquals(
241             "Wrapped value is the new value after the first commit.",
242             wrapped.getValue(),
243             newValue1);
244         
245         // Set the buffer to the current wrapped value to check whether 
246         // the starts buffering, even if there's no value difference.
247         Object newValue2 = wrapped.getValue();
248         buffer.setValue(newValue2);
249         commit();
250         assertEquals(
251             "Wrapped value is the new value after the second commit.",
252             wrapped.getValue(),
253             newValue2);
254     }
255 
256     /**
257      * Tests the core of a flush action: buffer changes are overridden
258      * by wrapped changes after a flush.
259      */
260     public void testFlushResetsTheBufferedValue() {
261         BufferedValueModel buffer = createDefaultBufferedValueModel();
262         Object newValue1 = "new value1";
263         buffer.setValue(newValue1);
264         assertSame(
265             "Buffer value reflects changes before the first flush.",
266             buffer.getValue(), 
267             newValue1);
268         revert();
269         assertEquals(
270             "Buffer value is the wrapped value after the first flush.",
271             buffer.getValue(),
272             wrapped.getValue());
273 
274         // Set the buffer to the current wrapped value to check whether 
275         // the starts buffering, even if there's no value difference.
276         Object newValue2 = wrapped.getValue();
277         buffer.setValue(newValue2);
278         assertSame(
279             "Buffer value reflects changes before the flush.",
280             buffer.getValue(), 
281             newValue2);
282         revert();
283         assertEquals(
284             "Buffer value is the wrapped value after the second flush.",
285             buffer.getValue(),
286             wrapped.getValue());
287     }
288 
289     // Tests a Proper Buffering State *****************************************
290 
291     /**
292      * Tests that a buffer isn't buffering as long as no value has been assigned.
293      */
294     public void testIsNotBufferingIfNoValueAssigned() {
295         BufferedValueModel buffer = createDefaultBufferedValueModel();
296         assertFalse(
297             "Initially the buffer does not buffer.",
298             buffer.isBuffering());
299 
300         Object newValue = "change1";
301         wrapped.setValue(newValue);
302         assertFalse(
303             "Wrapped changes do not affect the buffering state.",
304             buffer.isBuffering());
305         
306         wrapped.setValue(null);
307         assertFalse(
308             "Wrapped change to null does not affect the buffering state.",
309             buffer.isBuffering());
310     }
311     
312     /**
313      * Tests that the buffer is buffering once a value has been assigned.
314      */
315     public void testIsBufferingIfValueAssigned() {
316         BufferedValueModel buffer = createDefaultBufferedValueModel();
317         buffer.setValue("change1");
318         assertTrue(
319             "Setting a value (even the wrapped's value) turns on buffering.",
320             buffer.isBuffering());
321         
322         buffer.setValue("change2");
323         assertTrue(
324             "Changing the value doesn't affect the buffering state.",
325             buffer.isBuffering());
326         
327         buffer.setValue(wrapped.getValue());
328         assertTrue(
329             "Resetting the value to the wrapped's value should affect buffering.",
330             !buffer.isBuffering());
331     }
332     
333     /**
334      * Tests that the buffer is not buffering after a commit.
335      */
336     public void testIsNotBufferingAfterCommit() {
337         BufferedValueModel buffer = createDefaultBufferedValueModel();
338         buffer.setValue("change1");
339         commit();
340         assertFalse(
341             "The buffer does not buffer after a commit.",
342             buffer.isBuffering());
343 
344         Object newValue = "change1";
345         wrapped.setValue(newValue);
346         assertFalse(
347         "The buffer does not buffer after a commit and wrapped change1.",
348             buffer.isBuffering());
349         
350         wrapped.setValue(null);
351         assertFalse(
352         "The buffer does not buffer after a commit and wrapped change2.",
353             buffer.isBuffering());
354     }
355 
356     /**
357      * Tests that the buffer is not buffering after a flush.
358      */
359     public void testIsNotBufferingAfterFlush() {
360         BufferedValueModel buffer = createDefaultBufferedValueModel();
361         buffer.setValue("change1");
362         revert();
363         assertFalse(
364             "The buffer does not buffer after a flush.",
365             buffer.isBuffering());
366 
367         Object newValue = "change1";
368         wrapped.setValue(newValue);
369         assertFalse(
370         "The buffer does not buffer after a flush and wrapped change1.",
371             buffer.isBuffering());
372         
373         wrapped.setValue(null);
374         assertFalse(
375         "The buffer does not buffer after a flush and wrapped change2.",
376             buffer.isBuffering());
377     }
378 
379     /**
380      * Tests that changing the buffering state fires changes of 
381      * the <i>buffering</i> property.
382      */
383     public void testFiresBufferingChanges() {
384         BufferedValueModel buffer = createDefaultBufferedValueModel();
385         
386         TestPropertyChangeListener pcl = new TestPropertyChangeListener(BufferedValueModel.BUFFERING_PROPERTY);
387         buffer.addPropertyChangeListener(BufferedValueModel.BUFFERING_PROPERTY, pcl);
388         
389         assertEquals("Initial state.", 0, pcl.eventCount());
390         buffer.getValue();
391         assertEquals("Reading initial value.", 0, pcl.eventCount());
392         buffer.setCommitTrigger(null);
393         buffer.setCommitTrigger(commitTrigger);
394         assertEquals("After commit trigger change.", 0, pcl.eventCount());
395 
396         buffer.setValue("now buffering");
397         assertEquals("After setting the first value.", 1, pcl.eventCount());
398         buffer.setValue("still buffering");
399         assertEquals("After setting the second value.", 1, pcl.eventCount());
400         buffer.getValue();
401         assertEquals("Reading buffered value.", 1, pcl.eventCount());
402                 
403         wrapped.setValue(buffer.getValue());
404         assertEquals("Changing wrapped to same as buffer.", 2, pcl.eventCount());
405 
406         commit();
407         assertEquals("After committing.", 2, pcl.eventCount());
408         buffer.getValue();
409         assertEquals("Reading unbuffered value.", 2, pcl.eventCount());
410 
411         buffer.setValue("buffering again");
412         assertEquals("After second buffering switch.", 3, pcl.eventCount());
413         revert();
414         assertEquals("After flushing.", 4, pcl.eventCount());
415         buffer.getValue();
416         assertEquals("Reading unbuffered value.", 4, pcl.eventCount());
417         
418         buffer.setValue("before real commit");
419         assertEquals("With new change to be committed", 5, pcl.eventCount());
420         commit();
421         assertEquals("With new change committed", 6, pcl.eventCount());
422     }
423         
424     public void testSetValueSendsProperValueChangeEvents() {
425         Object obj1  = new Integer(1);
426         Object obj2a = new Integer(2);
427         Object obj2b = new Integer(2);
428         testSetValueSendsProperEvents(null, obj1,   true);
429         testSetValueSendsProperEvents(obj1, null,   true);
430         testSetValueSendsProperEvents(obj1, obj1,   false);
431         testSetValueSendsProperEvents(obj1, obj2a,  true);
432         testSetValueSendsProperEvents(obj2a, obj2b, false); 
433         testSetValueSendsProperEvents(null, null,   false);
434     }
435 
436     
437     public void testValueChangeSendsProperValueChangeEvents() {
438         Object obj1  = new Integer(1);
439         Object obj2a = new Integer(2);
440         Object obj2b = new Integer(2);
441         testValueChangeSendsProperEvents(null, obj1,   true);
442         testValueChangeSendsProperEvents(obj1, null,   true);
443         testValueChangeSendsProperEvents(obj1, obj1,   false);
444         testValueChangeSendsProperEvents(obj1, obj2a,  true);
445         testValueChangeSendsProperEvents(obj2a, obj2b, false); 
446         testValueChangeSendsProperEvents(null, null,   false);
447         
448         // Now replace the default value change detector with one that
449         // only uses true equivalence.
450         ValueChangeDetector oldVCD = (ValueChangeDetector)ApplicationServicesLocator.services().getService(ValueChangeDetector.class);
451         getApplicationServices().setValueChangeDetector(new StrictEquivalenceValueChangeDetector());
452         testValueChangeSendsProperEvents(null, obj1,   true);
453         testValueChangeSendsProperEvents(obj1, null,   true);
454         testValueChangeSendsProperEvents(obj1, obj1,   false);
455         testValueChangeSendsProperEvents(obj1, obj2a,  true);
456         testValueChangeSendsProperEvents(obj2a, obj2b, true); 
457         testValueChangeSendsProperEvents(null, null,   false);
458 
459         getApplicationServices().setValueChangeDetector(oldVCD);
460     }
461 
462   
463     // Commit Trigger Tests *************************************************
464 
465 
466     /**
467      * Checks that #setCommitTrigger changes the commit trigger.
468      */
469     public void testCommitTriggerChange() {
470         CommitTrigger trigger1 = new CommitTrigger();
471         CommitTrigger trigger2 = new CommitTrigger();
472         
473         BufferedValueModel buffer = new BufferedValueModel(wrapped, trigger1);
474         assertSame(
475             "Commit trigger has been changed.",
476             buffer.getCommitTrigger(),
477             trigger1);
478 
479         buffer.setCommitTrigger(trigger2);
480         assertSame(
481             "Commit trigger has been changed.",
482             buffer.getCommitTrigger(),
483             trigger2);
484         
485         buffer.setCommitTrigger(null);
486         assertSame(
487             "Commit trigger has been changed.",
488             buffer.getCommitTrigger(),
489             null);
490     }
491 
492     /**
493      * Checks and verifies that commit and flush events are driven
494      * by the current commit trigger.
495      */
496     public void testListensToCurrentCommitTrigger() {
497         CommitTrigger trigger1 = new CommitTrigger();
498         CommitTrigger trigger2 = new CommitTrigger();
499         
500         BufferedValueModel buffer = new BufferedValueModel(wrapped, trigger1);
501         buffer.setValue("change1");
502         Object wrappedValue = wrapped.getValue();
503         Object bufferedValue = buffer.getValue();
504         trigger2.commit();
505         assertEquals(
506             "Changing the unrelated trigger2 to commit has no effect on the wrapped.", 
507             wrapped.getValue(),
508             wrappedValue);
509         assertSame(
510             "Changing the unrelated trigger2 to commit has no effect on the buffer.", 
511             buffer.getValue(),
512             bufferedValue);
513         
514         trigger2.revert();
515         assertEquals(
516             "Changing the unrelated trigger2 to revert has no effect on the wrapped.", 
517             wrapped.getValue(),
518             wrappedValue);
519         assertSame(
520             "Changing the unrelated trigger2 to revert has no effect on the buffer.", 
521             buffer.getValue(),
522             bufferedValue);
523         
524         // Change the commit trigger to trigger2.
525         buffer.setCommitTrigger(trigger2);
526         assertSame(
527             "Commit trigger has been changed.",
528             buffer.getCommitTrigger(),
529             trigger2);
530 
531         trigger1.commit();
532         assertEquals(
533             "Changing the unrelated trigger1 to commit has no effect on the wrapped.", 
534             wrapped.getValue(),
535             wrappedValue);
536         assertSame(
537             "Changing the unrelated trigger1 to commit has no effect on the buffer.", 
538             buffer.getValue(),
539             bufferedValue);
540         
541         trigger1.revert();
542         assertEquals(
543             "Changing the unrelated trigger1 to revert has no effect on the wrapped.", 
544             wrapped.getValue(),
545             wrappedValue);
546         assertSame(
547             "Changing the unrelated trigger1 to revert has no effect on the buffer.", 
548             buffer.getValue(),
549             bufferedValue);
550         
551         // Commit using trigger2.
552         trigger2.commit();
553         assertEquals(
554             "Changing the current trigger2 to commit commits the buffered value.", 
555             buffer.getValue(),
556             wrapped.getValue());
557         
558         buffer.setValue("change2");
559         wrappedValue = wrapped.getValue();
560         trigger2.revert();
561         assertEquals(
562             "Changing the current trigger2 to revert flushes the buffered value.", 
563             buffer.getValue(),
564             wrapped.getValue());
565         assertEquals(
566             "Changing the current trigger2 to revert flushes the buffered value.", 
567             buffer.getValue(),
568             wrappedValue);
569     }
570 
571 
572     // Tests Proper Update Notifications **************************************
573     
574     /**
575      * Checks that wrapped changes fire value changes 
576      * if no value has been assigned.
577      */
578     public void testPropagatesWrappedChangesIfNoValueAssigned() {
579         BufferedValueModel buffer = createDefaultBufferedValueModel();
580         TestPropertyChangeListener pcl = new TestPropertyChangeListener(ValueModel.VALUE_PROPERTY);
581         buffer.addValueChangeListener(pcl);
582         
583         wrapped.setValue("change1");
584         assertEquals("Value change.", 1, pcl.eventCount());
585 
586         wrapped.setValue(null);
587         assertEquals("Value change.", 2, pcl.eventCount());
588 
589         wrapped.setValue("change2");
590         assertEquals("Value change.", 3, pcl.eventCount());
591 
592         wrapped.setValue(buffer.getValue());
593         assertEquals("No value change.", 3, pcl.eventCount());
594     }
595 
596     /**
597      * Tests that wrapped changes are  propagated once a value has
598      * been assigned, i.e. the buffer is buffering.
599      */
600     public void testIgnoresWrappedChangesIfValueAssigned() {
601         BufferedValueModel buffer = createDefaultBufferedValueModel();
602         TestPropertyChangeListener pcl = new TestPropertyChangeListener(ValueModel.VALUE_PROPERTY);
603         buffer.addValueChangeListener(pcl);
604         
605         buffer.setValue("new buffer");
606         wrapped.setValue("change1");
607         assertEquals("Value change.", 2, pcl.eventCount());
608 
609         buffer.setValue("new buffer");
610         wrapped.setValue(null);
611         assertEquals("Value change.", 4, pcl.eventCount());
612 
613         buffer.setValue("new buffer");
614         wrapped.setValue("change2");
615         assertEquals("Value change.", 6, pcl.eventCount());
616 
617         buffer.setValue("new buffer");
618         wrapped.setValue(buffer.getValue());    // won't fire event
619         assertEquals("No value change.", 7, pcl.eventCount());
620     }
621 
622     /**
623      * Checks and verifies that a commit fires no value change.
624      */
625     public void testCommitFiresNoChangeOnSameOldAndNewValues() {
626         BufferedValueModel buffer = createDefaultBufferedValueModel();
627         buffer.setValue("value1");
628         TestPropertyChangeListener pcl = new TestPropertyChangeListener(ValueModel.VALUE_PROPERTY);
629         buffer.addValueChangeListener(pcl);
630         
631         assertEquals("No initial change.", 0, pcl.eventCount());
632         commit();
633         assertEquals("First commit: no change.", 0, pcl.eventCount());
634         
635         buffer.setValue("value2");
636         assertEquals("Setting a value: a change.", 1, pcl.eventCount());
637         commit();
638         assertEquals("Second commit: no change.", 1, pcl.eventCount());
639     }
640 
641     public void testCommitFiresChangeOnDifferentOldAndNewValues() {
642         BufferedValueModel buffer = createDefaultBufferedValueModel(
643                 new ToUpperCaseStringHolder());
644         buffer.setValue("initialValue");
645         TestPropertyChangeListener pcl = new TestPropertyChangeListener(ValueModel.VALUE_PROPERTY);
646         buffer.addValueChangeListener(pcl);
647         buffer.setValue("value1");
648         assertEquals("One event fired",
649                 1,
650                 pcl.eventCount());
651         assertEquals("First value set.",
652                 "value1",
653                 pcl.lastEvent().getNewValue());
654         commit();
655         assertEquals("Commit fires if the wrapped modifies the value.",
656                 2,
657                 pcl.eventCount());
658         assertEquals("Old value is the buffered value.",
659                 "value1",
660                 pcl.lastEvent().getOldValue());
661         assertEquals("New value is the modified value.",
662                 "VALUE1",
663                 pcl.lastEvent().getNewValue());
664     }
665 
666     /**
667      * Tests that a flush event fires a value change if and only if
668      * the flushed value does not equal the buffered value.
669      */
670     public void testFlushFiresTrueValueChanges() {
671         BufferedValueModel buffer = createDefaultBufferedValueModel();
672         TestPropertyChangeListener pcl = new TestPropertyChangeListener(ValueModel.VALUE_PROPERTY);
673         
674         wrapped.setValue("new wrapped");
675         buffer.setValue("new buffer");
676         buffer.addValueChangeListener(pcl);
677         revert();
678         assertEquals("First flush changes value.", 1, pcl.eventCount());
679         
680         buffer.setValue(wrapped.getValue());
681         assertEquals("Resetting value: no change.", 1, pcl.eventCount());
682         revert();
683         assertEquals("Second flush: no change.", 1, pcl.eventCount());
684 
685         buffer.setValue("new buffer2");
686         assertEquals("Second value change.", 2, pcl.eventCount());
687         wrapped.setValue("new wrapped2");
688         assertEquals("Setting new wrapped value: no change.", 3, pcl.eventCount());
689         buffer.setValue(wrapped.getValue());
690         assertEquals("Third value change.", 3, pcl.eventCount());
691         revert();
692         assertEquals("Third flush: no change.", 3, pcl.eventCount());
693     }
694     
695     // Misc Tests *************************************************************
696  
697     /**
698      * Tests read actions on a read-only model.
699      */
700     public void testReadOnly() {
701         TestBean bean = new TestBean();
702         ValueModel readOnlyModel = new BeanPropertyAccessStrategy(bean).getPropertyValueModel("readOnly");
703         BufferedValueModel buffer = new BufferedValueModel(readOnlyModel, commitTrigger);
704         
705         assertSame(
706             "Can read values from a read-only model.", 
707             buffer.getValue(),
708             readOnlyModel.getValue());
709         
710         Object newValue1 = "new value";
711         buffer.setValue(newValue1);
712         assertSame(
713             "Can read values from a read-only model when buffering.", 
714             buffer.getValue(),
715             newValue1);
716         
717         revert();
718         assertSame(
719             "Can read values from a read-only model after a flush.", 
720             buffer.getValue(),
721             bean.getReadOnly());
722         
723         buffer.setValue("new value2");
724         try {
725             commit();
726             fail("Cannot commit to a read-only model.");
727         } catch (Exception e) {
728             // The expected behavior
729         }
730     }
731 
732     // Test Implementations ***************************************************
733     
734     private void testSetValueSendsProperEvents(Object oldValue, Object newValue, boolean eventExpected) {
735         BufferedValueModel valueModel = 
736             new BufferedValueModel(new ValueHolder(oldValue), new CommitTrigger());
737         testSendsProperEvents(valueModel, oldValue, newValue, eventExpected);
738     }
739     
740     private void testValueChangeSendsProperEvents(Object oldValue, Object newValue, boolean eventExpected) {
741         BufferedValueModel defaultModel = createDefaultBufferedValueModel();
742         defaultModel.setValue(oldValue);
743         testSendsProperEvents(defaultModel, oldValue, newValue, eventExpected);
744     }
745     
746     private void testSendsProperEvents(BufferedValueModel valueModel, Object oldValue, Object newValue, boolean eventExpected) {
747         TestPropertyChangeListener pcl = new TestPropertyChangeListener(ValueModel.VALUE_PROPERTY);
748         valueModel.addValueChangeListener(pcl);
749         int expectedEventCount = eventExpected ? 1 : 0;
750         
751         valueModel.setValue(newValue);
752         assertEquals(
753                 "Expected event count after ( " + 
754                 oldValue + " -> " + newValue + ").", 
755                 expectedEventCount, 
756                 pcl.eventCount());
757         if (eventExpected) {
758             assertEquals("Event's old value.", oldValue, pcl.lastEvent().getOldValue());
759             assertEquals("Event's new value.", newValue, pcl.lastEvent().getNewValue());
760         }
761     }
762     
763     
764     // Helper Code ************************************************************
765 
766     private void commit() {
767         commitTrigger.commit();
768     }
769 
770     private void revert() {
771         commitTrigger.revert();
772     }
773 
774     private BufferedValueModel createDefaultBufferedValueModel() {
775         wrapped.setValue(RESET_VALUE);
776         return new BufferedValueModel(wrapped, commitTrigger);
777     }
778     
779     private BufferedValueModel createDefaultBufferedValueModel(ValueModel wrapped) {
780         wrapped.setValue(RESET_VALUE);
781         return new BufferedValueModel(wrapped, commitTrigger);
782     }
783     
784     // A String typed ValueModel that modifies set values to uppercase.
785     private static class ToUpperCaseStringHolder extends AbstractValueModel {
786         
787         private String text;
788         
789         public Object getValue() {
790             return text;
791         }
792         
793         public void setValue(Object newValue) {
794             String newText = ((String) newValue).toUpperCase();
795             Object oldText = text;
796             text = newText;
797             fireValueChange(oldText, newText);
798         }
799         
800     }
801 
802     /**
803      * This class is used to test alternate value change detection methods.
804      */
805     private static class StrictEquivalenceValueChangeDetector implements ValueChangeDetector {
806         public boolean hasValueChanged(Object oldValue, Object newValue) {
807             return oldValue != newValue;
808         }
809     }
810     
811 }