001    /*
002     * Copyright 2002-2005 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.binding.value.support;
017    
018    import org.springframework.binding.support.BeanPropertyAccessStrategy;
019    import org.springframework.binding.support.TestBean;
020    import org.springframework.binding.support.TestPropertyChangeListener;
021    import org.springframework.binding.value.CommitTrigger;
022    import org.springframework.binding.value.ValueChangeDetector;
023    import org.springframework.binding.value.ValueModel;
024    import org.springframework.richclient.application.ApplicationServicesLocator;
025    import org.springframework.richclient.test.SpringRichTestCase;
026    
027    
028    /**
029     * Tests class {@link BufferedValueModel}.
030     * 
031     * @author Jeanette Winzenburg
032     * @author Karsten Lentzsch
033     * @author Oliver Hutchison
034     */
035    public final class BufferedValueModelTests extends SpringRichTestCase {
036    
037        private static final Object INITIAL_VALUE = "initial value";
038        private static final Object RESET_VALUE   = "reset value";
039    
040        private ValueModel wrapped;
041        private CommitTrigger commitTrigger;
042            
043        protected void doSetUp() throws Exception {
044            wrapped = new ValueHolder(INITIAL_VALUE);
045            commitTrigger = new CommitTrigger();
046        }
047        
048        public void testGetWrappedValueModel() {
049            BufferedValueModel buffer = createDefaultBufferedValueModel();
050            
051            assertSame(wrapped, buffer.getWrappedValueModel());
052            assertSame(wrapped, buffer.getInnerMostWrappedValueModel());
053            
054            ValueModel nestedValueModel = new AbstractValueModelWrapper(wrapped) {};
055            buffer = new BufferedValueModel(nestedValueModel);
056            assertSame(nestedValueModel, buffer.getWrappedValueModel());
057            assertSame(wrapped, buffer.getInnerMostWrappedValueModel());
058        }
059    
060        public void testReturnsWrappedValueIfNoValueAssigned() {
061            BufferedValueModel buffer = createDefaultBufferedValueModel();
062            assertEquals(
063                "Buffer value equals the wrapped value before any changes.",
064                buffer.getValue(),
065                wrapped.getValue());
066    
067            wrapped.setValue("change1");
068            assertEquals(
069                "Buffer value equals the wrapped value changes.",
070                buffer.getValue(),
071                wrapped.getValue());
072    
073            wrapped.setValue(null);
074            assertEquals(
075                "Buffer value equals the wrapped value changes.",
076                buffer.getValue(),
077                wrapped.getValue());
078    
079            wrapped.setValue("change2");
080            assertEquals(
081                "Buffer value equals the wrapped value changes.",
082                buffer.getValue(),
083                wrapped.getValue());
084        }
085    
086        /**
087         * Tests that the BufferedValueModel returns the buffered values
088         * once a value has been assigned. 
089         */
090        public void testReturnsBufferedValueIfValueAssigned() {
091            BufferedValueModel buffer = createDefaultBufferedValueModel();
092    
093            Object newValue1 = wrapped.getValue();
094            buffer.setValue(newValue1);
095            assertSame(
096                "Buffer value == new value once a value has been assigned.",
097                buffer.getValue(),
098                newValue1);
099    
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    }