1   /*
2    * Copyright 2002-2004 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.richclient.table;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import javax.swing.event.TableModelEvent;
22  import javax.swing.event.TableModelListener;
23  
24  import junit.framework.Assert;
25  import junit.framework.TestCase;
26  
27  import org.easymock.EasyMock;
28  import org.easymock.IArgumentMatcher;
29  import org.springframework.util.ObjectUtils;
30  
31  
32  /**
33   * A skeleton test case for implementations of the {@link MutableTableModel} interface.  
34   *
35   * @author Kevin Stembridge
36   * @since 0.3
37   *
38   */
39  public abstract class AbstractMutableTableModelTests extends TestCase {
40  
41      /**
42       * Creates a new uninitialized {@code AbstractMutableTableModelTests}.
43       */
44      public AbstractMutableTableModelTests() {
45          super();
46      }
47      
48      /**
49       * Subclasses must implement this method to provide the {@link MutableTableModel} implementation
50       * to be tested. This method may be called often, so unnecessary repeated initialization 
51       * should be avoided.
52       *
53       * @return The implementation to be tested. Never null.
54       */
55      protected abstract MutableTableModel getTableModel();
56      
57      /**
58       * Tests the {@link MutableTableModel#addRow(Object)} method.
59       */
60      public final void testAddRow() {
61          
62          MutableTableModel model = getTableModel();
63          Object row = new Object();
64          TableModelListener listener1 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
65          TableModelListener listener2 = (TableModelListener) EasyMock.createMock(TableModelListener.class);
66          model.addTableModelListener(listener1);
67          model.addTableModelListener(listener2);
68          
69          try {
70              model.addRow(null);
71              Assert.fail("Should have thrown an IllegalArgumentException");
72          }
73          catch (IllegalArgumentException e) {
74              //test passes
75          }
76          
77          TableModelEvent event = new TableModelEvent(model, 
78                                                      0, 
79                                                      0, 
80                                                      TableModelEvent.ALL_COLUMNS, 
81                                                      TableModelEvent.INSERT);
82          listener1.tableChanged(matchEvent(event));
83          listener2.tableChanged(matchEvent(event));
84          
85          EasyMock.replay(listener1);
86          EasyMock.replay(listener2);
87          
88          Assert.assertEquals(0, model.getRowCount());
89          model.addRow(row);
90          Assert.assertEquals(1, model.getRowCount());
91          
92          EasyMock.verify(listener1);
93          EasyMock.verify(listener2);
94          
95      }
96      
97      /**
98       * Tests the {@link MutableTableModel#addRows(java.util.List)} method.
99       */
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 }