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 }