001    /*
002     * Copyright 2002-2004 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.richclient.table;
017    
018    import java.util.ArrayList;
019    import java.util.List;
020    
021    import javax.swing.event.TableModelEvent;
022    import javax.swing.event.TableModelListener;
023    
024    import junit.framework.Assert;
025    import junit.framework.TestCase;
026    
027    import org.easymock.EasyMock;
028    import org.easymock.IArgumentMatcher;
029    import org.springframework.util.ObjectUtils;
030    
031    
032    /**
033     * A skeleton test case for implementations of the {@link MutableTableModel} interface.  
034     *
035     * @author Kevin Stembridge
036     * @since 0.3
037     *
038     */
039    public abstract class AbstractMutableTableModelTests extends TestCase {
040    
041        /**
042         * Creates a new uninitialized {@code AbstractMutableTableModelTests}.
043         */
044        public AbstractMutableTableModelTests() {
045            super();
046        }
047        
048        /**
049         * Subclasses must implement this method to provide the {@link MutableTableModel} implementation
050         * to be tested. This method may be called often, so unnecessary repeated initialization 
051         * should be avoided.
052         *
053         * @return The implementation to be tested. Never null.
054         */
055        protected abstract MutableTableModel getTableModel();
056        
057        /**
058         * Tests the {@link MutableTableModel#addRow(Object)} method.
059         */
060        public final void testAddRow() {
061            
062            MutableTableModel model = getTableModel();
063            Object row = new Object();
064            TableModelListener listener1 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
065            TableModelListener listener2 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
066            model.addTableModelListener(listener1);
067            model.addTableModelListener(listener2);
068            
069            try {
070                model.addRow(null);
071                Assert.fail("Should have thrown an IllegalArgumentException");
072            }
073            catch (IllegalArgumentException e) {
074                //test passes
075            }
076            
077            TableModelEvent event = new TableModelEvent(model, 
078                                                        0, 
079                                                        0, 
080                                                        TableModelEvent.ALL_COLUMNS, 
081                                                        TableModelEvent.INSERT);
082            listener1.tableChanged(matchEvent(event));
083            listener2.tableChanged(matchEvent(event));
084            
085            EasyMock.replay(listener1);
086            EasyMock.replay(listener2);
087            
088            Assert.assertEquals(0, model.getRowCount());
089            model.addRow(row);
090            Assert.assertEquals(1, model.getRowCount());
091            
092            EasyMock.verify(listener1);
093            EasyMock.verify(listener2);
094            
095        }
096        
097        /**
098         * Tests the {@link MutableTableModel#addRows(java.util.List)} method.
099         */
100        public final void testAddRows() {
101            
102            MutableTableModel model = getTableModel();
103            TableModelListener listener1 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
104            TableModelListener listener2 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
105            model.addTableModelListener(listener1);
106            model.addTableModelListener(listener2);
107           
108            try {
109                model.addRows(null);
110                Assert.fail("Should have thrown an IllegalArgumentException");
111            }
112            catch (IllegalArgumentException e) {
113                //test passes
114            }
115            
116            //Passing an empty list of rows should have no effect on the model or listeners
117            
118            EasyMock.replay(listener1);
119            EasyMock.replay(listener2);
120            Assert.assertEquals(0, model.getRowCount());
121            model.addRows(new ArrayList());
122            Assert.assertEquals(0, model.getRowCount());
123            EasyMock.verify(listener1);
124            EasyMock.verify(listener2);
125            
126            //reset the mocks for the next test
127            EasyMock.reset(listener1);
128            EasyMock.reset(listener2);
129            
130            List rows = new ArrayList(2);
131            rows.add(new Object());
132            rows.add(new Object());
133    
134            TableModelEvent expectedEvent = new TableModelEvent(model, 
135                                                                0, 
136                                                                1, 
137                                                                TableModelEvent.ALL_COLUMNS, 
138                                                                TableModelEvent.INSERT);
139            
140            listener1.tableChanged(matchEvent(expectedEvent));
141            listener2.tableChanged(matchEvent(expectedEvent));
142            
143            EasyMock.replay(listener1);
144            EasyMock.replay(listener2);
145            
146            Assert.assertEquals(0, model.getRowCount());
147            model.addRows(rows);
148            Assert.assertEquals(2, model.getRowCount());
149            
150            EasyMock.verify(listener1);
151            EasyMock.verify(listener2);
152            
153        }
154    
155        /**
156         * Tests the {@link MutableTableModel#remove(int)} method.
157         */
158        public final void testRemove() {
159            
160            MutableTableModel model = getTableModel();
161            
162            //Add 2 rows to the model
163            List rows = new ArrayList(2);
164            rows.add(new Object());
165            rows.add(new Object());
166            model.addRows(rows);
167            
168            //confirm that the 2 rows were added
169            Assert.assertEquals(2, model.getRowCount());
170            
171            //confirm that an exception is thrown if the index is out of bounds
172            try {
173                model.remove(2);
174                Assert.fail("Should have thrown an IndexOutOfBoundsException");
175            }
176            catch (IndexOutOfBoundsException e) {
177                //...and that the model hasn't been altered
178                Assert.assertEquals(2, model.getRowCount());
179            }
180            
181            //Create some mock listeners and add them to the model
182            TableModelListener listener1 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
183            TableModelListener listener2 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
184            model.addTableModelListener(listener1);
185            model.addTableModelListener(listener2);
186            
187            //For the next test, remove the first row and confirm that the model now contains only one row and that the 
188            //listeners were notified correctly
189            
190            //Create the expected event
191            TableModelEvent expectedEvent = new TableModelEvent(model,
192                                                                0,
193                                                                0,
194                                                                TableModelEvent.ALL_COLUMNS,
195                                                                TableModelEvent.DELETE);
196            
197            //set the expectations on the listeners and switch them to replay mode
198            listener1.tableChanged(matchEvent(expectedEvent));
199            listener2.tableChanged(matchEvent(expectedEvent));
200            EasyMock.replay(listener1);
201            EasyMock.replay(listener2);
202            
203            //execute the test
204            model.remove(0);
205            
206            //confirm that the model now only contains one row and that the listeners were correctly 
207            //notified
208            Assert.assertEquals(1, model.getRowCount());
209            EasyMock.verify(listener1);
210            EasyMock.verify(listener2);
211         
212        }
213    
214        /**
215         * Tests the {@link MutableTableModel#remove(int, int)} method.
216         */
217        public final void testRemoveRange() {
218            
219            MutableTableModel model = getTableModel();
220            
221            //Add 4 rows to the model
222            List rows = new ArrayList(4);
223            rows.add(new Object());
224            rows.add(new Object());
225            rows.add(new Object());
226            rows.add(new Object());
227            model.addRows(rows);
228            
229            //confirm that the 4 rows were added
230            Assert.assertEquals(4, model.getRowCount());
231            
232            //confirm that an exception is thrown if lastIndex is less than firstIndex
233            try {
234                model.remove(2, 1);
235                Assert.fail("Should have thrown an IllegalArgumentException");
236            }
237            catch (IllegalArgumentException e) {
238                //test passes
239            }   
240            
241            //confirm that an exception is thrown if the index is out of bounds
242            try {
243                model.remove(1, 4);
244                Assert.fail("Should have thrown an IndexOutOfBoundsException");
245            }
246            catch (IndexOutOfBoundsException e) {
247                //...and that the model has not been altered
248                Assert.assertEquals(4, model.getRowCount());
249            }
250            
251            //Create some mock listeners and add them to the model
252            TableModelListener listener1 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
253            TableModelListener listener2 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
254            model.addTableModelListener(listener1);
255            model.addTableModelListener(listener2);
256            
257            //For the next test, remove the second and third rows and confirm that the model now 
258            //contains only two rows and that the listeners were notified correctly
259            
260            //Create the expected event
261            TableModelEvent expectedEvent = new TableModelEvent(model,
262                                                                1,
263                                                                2,
264                                                                TableModelEvent.ALL_COLUMNS,
265                                                                TableModelEvent.DELETE);
266            
267            //set the expectations on the listeners and switch them to replay mode
268            listener1.tableChanged(matchEvent(expectedEvent));
269            listener2.tableChanged(matchEvent(expectedEvent));
270            EasyMock.replay(listener1);
271            EasyMock.replay(listener2);
272            
273            //execute the test
274            model.remove(1, 2);
275            
276            //confirm that the model now only contains two rows and that the listeners were correctly 
277            //notified
278            Assert.assertEquals(2, model.getRowCount());
279            EasyMock.verify(listener1);
280            EasyMock.verify(listener2);
281         
282        }
283    
284        /**
285         * Tests the {@link MutableTableModel#remove(int[])} method.
286         */
287        public final void testRemoveIntArray() {
288            
289            MutableTableModel model = getTableModel();
290            
291            //Add 4 rows to the model
292            List rows = new ArrayList(4);
293            rows.add(new Object());
294            rows.add(new Object());
295            rows.add(new Object());
296            rows.add(new Object());
297            model.addRows(rows);
298            
299            //confirm that the 4 rows were added
300            Assert.assertEquals(4, model.getRowCount());
301            
302            //confirm that an exception is thrown if lastIndex is less than firstIndex
303            try {
304                model.remove((int[]) null);
305                Assert.fail("Should have thrown an IllegalArgumentException");
306            }
307            catch (IllegalArgumentException e) {
308                //test passes
309            }
310            
311            //confirm that an exception is thrown if the index is out of bounds
312            try {
313                model.remove(new int[] {1, 4});
314                Assert.fail("Should have thrown an IndexOutOfBoundsException");
315            }
316            catch (IndexOutOfBoundsException e) {
317                //...and that the model has not been altered
318                Assert.assertEquals(4, model.getRowCount());
319            }
320            
321            //Create some mock listeners and add them to the model
322            TableModelListener listener1 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
323            TableModelListener listener2 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
324            model.addTableModelListener(listener1);
325            model.addTableModelListener(listener2);
326            
327            // confirm that an empty array has no effect on the model
328            
329            //switch mocks to replay mode
330            EasyMock.replay(listener1);
331            EasyMock.replay(listener2);
332            
333            //execute the test
334            model.remove(new int[] {});
335            
336            //confirm that the model hasn't changed and that no listeners were invoked
337            Assert.assertEquals(4, model.getRowCount());
338            EasyMock.verify(listener1);
339            EasyMock.verify(listener2);
340            
341            
342            //For the next test, remove the second and third rows and confirm that the model now 
343            //contains only two rows
344            
345            //removing the listeners because I got no idea what's going on inside this method call
346            model.removeTableModelListener(listener1);
347            model.removeTableModelListener(listener2);
348            
349            //execute the test
350            model.remove(new int[] {1, 3});
351            
352            //confirm that the model now only contains two rows
353            Assert.assertEquals(2, model.getRowCount());
354         
355        }
356        
357        /**
358         * Tests the {@link MutableTableModel#clear()} method.
359         */
360        public final void testClear() {
361            
362            MutableTableModel model = getTableModel();
363            
364            //Add 4 rows to the model
365            List rows = new ArrayList(4);
366            rows.add(new Object());
367            rows.add(new Object());
368            rows.add(new Object());
369            rows.add(new Object());
370            model.addRows(rows);
371            
372            //confirm that the 4 rows were added
373            Assert.assertEquals(4, model.getRowCount());
374            
375            //Create some mock listeners and add them to the model
376            TableModelListener listener1 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
377            TableModelListener listener2 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
378            model.addTableModelListener(listener1);
379            model.addTableModelListener(listener2);
380            
381            //Create the expected event
382            TableModelEvent expectedEvent = new TableModelEvent(model);
383            
384            //set the expectations on the listeners and switch them to replay mode
385            listener1.tableChanged(matchEvent(expectedEvent));
386            listener2.tableChanged(matchEvent(expectedEvent));
387            EasyMock.replay(listener1);
388            EasyMock.replay(listener2);
389            
390            //execute the test
391            model.clear();
392            
393            //confirm that the model is now empty and that the listeners were correctly 
394            //notified
395            Assert.assertEquals(0, model.getRowCount());
396            EasyMock.verify(listener1);
397            EasyMock.verify(listener2);
398         
399        }
400        
401        protected TableModelEvent matchEvent(TableModelEvent event) {
402            EasyMock.reportMatcher(new TableModelEventMatcher(event));
403            return event;
404        }
405        
406        /**
407         * An argument matcher for TableModelEvents.
408         */
409        protected static class TableModelEventMatcher implements IArgumentMatcher {
410            
411            private TableModelEvent expectedEvent;
412            
413            /**
414             * Creates a new {@code TableModelEventMatcher}.
415             *
416             * @param expectedEvent
417             */
418            public TableModelEventMatcher(TableModelEvent expectedEvent) {
419                this.expectedEvent = expectedEvent;
420            }
421            
422            /**
423             * {@inheritDoc}
424             */
425            public void appendTo(StringBuffer buffer) {
426                buffer.append("(");
427                buffer.append(this.expectedEvent.getClass().getName());
428                buffer.append(" with source [");
429                buffer.append(this.expectedEvent.getSource());
430                buffer.append("])");
431            }
432    
433            /**
434             * {@inheritDoc}
435             */
436            public boolean matches(Object argument) {
437                
438                if (!(argument instanceof TableModelEvent)) {
439                    return false;
440                }
441                
442                TableModelEvent other = (TableModelEvent) argument;
443                
444                if (!ObjectUtils.nullSafeEquals(expectedEvent.getSource(), other.getSource())) {
445                    return false;
446                }
447                
448                if (expectedEvent.getFirstRow() != other.getFirstRow()) {
449                    return false;
450                }
451                
452                if (expectedEvent.getLastRow() != other.getLastRow()) {
453                    return false;
454                }
455                
456                if (expectedEvent.getColumn() != other.getColumn()) {
457                    return false;
458                }
459                
460                if (expectedEvent.getType() != other.getType()) {
461                    return false;
462                }
463                
464                return true;
465                
466            }
467            
468        }
469        
470    }