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 }