001/* 002 * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games. 003 * Copyright (C) 2000-2010 The Gridarta Developers. 004 * 005 * This program is free software; you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation; either version 2 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License along 016 * with this program; if not, write to the Free Software Foundation, Inc., 017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 018 */ 019 020package net.sf.gridarta.model.mapcursor; 021 022import java.awt.Dimension; 023import java.awt.Point; 024import junit.framework.JUnit4TestAdapter; 025import net.sf.gridarta.model.archetype.TestArchetype; 026import net.sf.gridarta.model.direction.Direction; 027import net.sf.gridarta.model.gameobject.TestGameObject; 028import net.sf.gridarta.model.maparchobject.TestMapArchObject; 029import net.sf.gridarta.model.mapgrid.MapGrid; 030import net.sf.gridarta.model.mapgrid.SelectionMode; 031import net.sf.gridarta.model.mapmodel.MapModel; 032import net.sf.gridarta.model.mapmodel.MapSquare; 033import net.sf.gridarta.model.mapmodel.TestMapModelCreator; 034import net.sf.gridarta.utils.Size2D; 035import org.jetbrains.annotations.NotNull; 036import org.jetbrains.annotations.Nullable; 037import org.junit.Assert; 038import org.junit.Test; 039 040/** 041 * Unit test for {@link MapCursor}. 042 * @author <a href="mailto:cher@riedquat.de">Christian Hujer</a> 043 * @author <a href="mailto:dlviegas@gmail.com">Daniel Viegas</a> 044 * @author Andreas Kirschbaum 045 */ 046public class MapCursorTest { 047 048 /** 049 * The enter x coordinate of newly created maps. 050 */ 051 private static final int ENTER_X = 1; 052 053 /** 054 * The enter y coordinate of newly created maps. 055 */ 056 private static final int ENTER_Y = 2; 057 058 /** 059 * The size of the map grid for the tested cursor. 060 */ 061 @NotNull 062 private static final Size2D gridSize = new Size2D(6, 7); 063 064 /** 065 * A {@link MapCursorListener} that counts the number of callbacks. 066 */ 067 @NotNull 068 private static final TestMapCursorListener listener = new TestMapCursorListener(); 069 070 /** 071 * Creates a new {@link MapCursor} instance. No more than one instance may 072 * exist concurrently. 073 * @param grid the underlying map grid 074 * @return the new map cursor instance 075 */ 076 @NotNull 077 private static MapCursor<TestGameObject, TestMapArchObject, TestArchetype> createCursor(@NotNull final MapGrid grid) { 078 final TestMapModelCreator mapModelCreator = new TestMapModelCreator(false); 079 final Size2D gridSize = grid.getSize(); 080 final MapModel<TestGameObject, TestMapArchObject, TestArchetype> mapModel = mapModelCreator.newMapModel(gridSize.getWidth(), gridSize.getHeight()); 081 mapModel.getMapArchObject().setEnterX(ENTER_X); 082 mapModel.getMapArchObject().setEnterY(ENTER_Y); 083 final MapCursor<TestGameObject, TestMapArchObject, TestArchetype> cursor = new MapCursor<TestGameObject, TestMapArchObject, TestArchetype>(grid, mapModel); 084 cursor.addMapCursorListener(listener); 085 listener.changedPosCounter = 0; 086 listener.changedModeCounter = 0; 087 return cursor; 088 } 089 090 /** 091 * Checks that settings the cursor outside of the grid behaves as expected. 092 */ 093 @Test 094 public void setOutside() { 095 final MapGrid grid = new MapGrid(gridSize); 096 final MapCursor<TestGameObject, TestMapArchObject, TestArchetype> cursor = createCursor(grid); 097 final Point p = new Point(); 098 final int width = gridSize.getWidth(); 099 final int height = gridSize.getHeight(); 100 for (int i = -2; i < width + 2; i++) { 101 p.setLocation(i, -1); 102 cursor.setLocation(p); 103 testEvents(1, 0); 104 Assert.assertEquals(new Point(Math.max(0, Math.min(width - 1, i)), 0), cursor.getLocation()); 105 testEvents(0, 0); 106 p.setLocation(i, height); 107 cursor.setLocation(p); 108 testEvents(1, 0); 109 Assert.assertEquals(new Point(Math.max(0, Math.min(width - 1, i)), height - 1), cursor.getLocation()); 110 testEvents(0, 0); 111 } 112 for (int i = -2; i < height + 2; i++) { 113 p.setLocation(-1, i); 114 cursor.setLocation(p); 115 testEvents(1, 0); 116 Assert.assertEquals(new Point(0, Math.max(0, Math.min(height - 1, i))), cursor.getLocation()); 117 testEvents(0, 0); 118 p.setLocation(width, i); 119 cursor.setLocation(p); 120 testEvents(1, 0); 121 Assert.assertEquals(new Point(width - 1, Math.max(0, Math.min(height - 1, i))), cursor.getLocation()); 122 testEvents(0, 0); 123 } 124 } 125 126 /** 127 * Checks that settings the cursor within the map grid behaves as expected. 128 */ 129 @Test 130 public void setInside() { 131 final MapGrid grid = new MapGrid(gridSize); 132 final MapCursor<TestGameObject, TestMapArchObject, TestArchetype> cursor = createCursor(grid); 133 final Point p = new Point(); 134 for (int j = 0; j < gridSize.getHeight(); j++) { 135 for (int i = 0; i < gridSize.getWidth(); i++) { 136 p.setLocation(i, j); 137 cursor.setLocation(p); 138 testEvents(1, 0); 139 final Point res = cursor.getLocation(); 140 testEvents(0, 0); 141 Assert.assertEquals("getLocation()", p, res); 142 } 143 } 144 } 145 146 /** 147 * Checks that settings the cursor to the same location does not generate 148 * excess events. 149 */ 150 @Test 151 public void setSameLocation() { 152 final MapGrid grid = new MapGrid(gridSize); 153 final MapCursor<TestGameObject, TestMapArchObject, TestArchetype> cursor = createCursor(grid); 154 cursor.setLocation(new Point(3, 4)); 155 testEvents(1, 0); 156 cursor.setLocation(new Point(3, 4)); 157 testEvents(0, 0); 158 cursor.setLocation(new Point(1, 0)); 159 testEvents(1, 0); 160 cursor.setLocation(new Point(1, -1)); 161 testEvents(0, 0); 162 cursor.setLocation(new Point(-3, -2)); 163 testEvents(1, 0); 164 cursor.setLocation(new Point(-2, -1)); 165 testEvents(0, 0); 166 cursor.setLocation(new Point(0, 0)); 167 testEvents(0, 0); 168 } 169 170 /** 171 * Checks {@link MapCursor#setLocationSafe(Point)}. 172 */ 173 @Test 174 public void setLocationSafe() { 175 final MapGrid grid = new MapGrid(gridSize); 176 final MapCursor<TestGameObject, TestMapArchObject, TestArchetype> cursor = createCursor(grid); 177 final Point p = new Point(-1, -1); 178 Assert.assertFalse("setLocationSafe(null) should return false", cursor.setLocationSafe(null)); 179 testEvents(0, 0); 180 Assert.assertFalse("setLocationSafe(" + p + ") should return false", cursor.setLocationSafe(p)); 181 testEvents(0, 0); 182 p.setLocation(3, 4); 183 Assert.assertTrue("setLocationSafe(" + p + ") should return true", cursor.setLocationSafe(p)); 184 testEvents(1, 0); 185 Assert.assertFalse("setLocationSafe(" + p + ") should return false", cursor.setLocationSafe(p)); 186 testEvents(0, 0); 187 p.setLocation(-1, -1); 188 Assert.assertFalse("setLocationSafe(" + p + ") should return false", cursor.setLocationSafe(p)); 189 testEvents(0, 0); 190 Assert.assertFalse("setLocationSafe(null) should return false", cursor.setLocationSafe(null)); 191 testEvents(0, 0); 192 } 193 194 /** 195 * Checks {@link MapCursor#isOnGrid(Point)}. 196 */ 197 @Test 198 public void testIsOnGrid() { 199 final MapGrid grid = new MapGrid(gridSize); 200 final MapCursor<TestGameObject, TestMapArchObject, TestArchetype> cursor = createCursor(grid); 201 final Point p = new Point(); 202 for (int j = -2; j < gridSize.getHeight() + 2; j++) { 203 for (int i = -2; i < gridSize.getWidth() + 2; i++) { 204 p.setLocation(i, j); 205 if (i >= 0 && i < gridSize.getWidth() && j >= 0 && j < gridSize.getHeight()) { 206 Assert.assertTrue(p + " should be on the grid.", cursor.isOnGrid(p)); 207 } else { 208 Assert.assertFalse(p + " should not be on the grid.", cursor.isOnGrid(p)); 209 } 210 } 211 } 212 Assert.assertFalse("Null should not be on the grid.", cursor.isOnGrid(null)); 213 } 214 215 /** 216 * Checks {@link MapCursor#goTo(boolean, Direction)}. 217 */ 218 @Test 219 public void testGoTo() { 220 final MapGrid grid = new MapGrid(gridSize); 221 final MapCursor<TestGameObject, TestMapArchObject, TestArchetype> cursor = createCursor(grid); 222 final Point pStart = new Point(2, 3); 223 final Point p = new Point(pStart); 224 cursor.setLocation(p); 225 testEvents(1, 0); 226 for (final Direction dir : Direction.values()) { 227 Assert.assertTrue("go(" + dir + ") should return true. (Maybe the grid was too small.)", cursor.goTo(true, dir)); 228 testEvents(1, 0); 229 p.x += dir.getDx(); 230 p.y += dir.getDy(); 231 Assert.assertEquals("Moving cursor.", p, cursor.getLocation()); 232 } 233 Assert.assertEquals("Moving in a circle.", pStart, cursor.getLocation()); 234 } 235 236 /** 237 * Checks the dragging related functions of {@link MapCursor}. 238 */ 239 @Test 240 public void dragging() { 241 final MapGrid grid = new MapGrid(gridSize); 242 final MapCursor<TestGameObject, TestMapArchObject, TestArchetype> cursor = createCursor(grid); 243 Assert.assertFalse("MapCursor should not drag while deactivated.", cursor.isDragging()); 244 cursor.dragStart(); 245 testEvents(0, 1); 246 Assert.assertEquals(new Dimension(0, 0), cursor.getDragOffset()); 247 final Point dragStart = new Point(3, 4); 248 final Point p = new Point(dragStart); 249 final Dimension offset = new Dimension(0, 0); 250 cursor.setLocation(dragStart); 251 testEvents(1, 0); 252 cursor.dragStart(); 253 testEvents(0, 0); 254 Assert.assertTrue("MapCursor should be in drag mode.", cursor.isDragging()); 255 Assert.assertEquals("Wrong offset", offset, cursor.getDragOffset()); 256 cursor.deactivate(); 257 testEvents(0, 1); 258 Assert.assertFalse("MapCursor should not drag while deactivated.", cursor.isDragging()); 259 Assert.assertNull("Drag offset should be null", cursor.getDragOffset()); 260 cursor.setLocation(dragStart); 261 testEvents(0, 0); 262 cursor.dragStart(); 263 testEvents(0, 1); 264 Assert.assertEquals("Wrong offset", offset, cursor.getDragOffset()); 265 dragTo(cursor, grid, Direction.WEST, dragStart, p, offset); 266 dragTo(cursor, grid, Direction.EAST, dragStart, p, offset); 267 dragTo(cursor, grid, Direction.EAST, dragStart, p, offset); 268 dragTo(cursor, grid, Direction.NORTH_WEST, dragStart, p, offset); 269 dragTo(cursor, grid, Direction.SOUTH, dragStart, p, offset); 270 dragTo(cursor, grid, Direction.SOUTH, dragStart, p, offset); 271 dragTo(cursor, grid, Direction.WEST, dragStart, p, offset); 272 dragTo(cursor, grid, Direction.NORTH_EAST, dragStart, p, offset); 273 } 274 275 /** 276 * Calls {@link MapCursor#dragTo(Point)} and checks for expected results. 277 * @param cursor the map cursor to affect 278 * @param grid the associated map grid 279 * @param dir the direction to drag 280 * @param start the starting location 281 * @param p the destination location 282 * @param offset the expected dragging offset 283 */ 284 private static void dragTo(@NotNull final MapCursor<TestGameObject, TestMapArchObject, TestArchetype> cursor, @NotNull final MapGrid grid, @NotNull final Direction dir, @NotNull final Point start, @NotNull final Point p, @NotNull final Dimension offset) { 285 final Point d = new Point(dir.getDx(), dir.getDy()); 286 p.x += d.x; 287 p.y += d.y; 288 Assert.assertTrue("dragTo(" + p + ")", cursor.dragTo(p)); 289 testEvents(1, 0); 290 Assert.assertTrue("MapCursor should be in drag mode.", cursor.isDragging()); 291 Assert.assertEquals("Wrong position", p, cursor.getLocation()); 292 offset.width = p.x - start.x; 293 offset.height = p.y - start.y; 294 Assert.assertEquals("Wrong offset", offset, cursor.getDragOffset()); 295 assertPreSelection(grid, start, p); 296 } 297 298 /** 299 * Checks that a {@link MapGrid} includes a rectangle of {@link 300 * MapGrid#GRID_FLAG_SELECTING}. 301 * @param grid the map grid to check 302 * @param start one corner of the rectangle 303 * @param end the diagonally opposite corner of the rectangle 304 */ 305 private static void assertPreSelection(@NotNull final MapGrid grid, @NotNull final Point start, @NotNull final Point end) { 306 final int minX = Math.min(start.x, end.x); 307 final int maxX = Math.max(start.x, end.x); 308 final int minY = Math.min(start.y, end.y); 309 final int maxY = Math.max(start.y, end.y); 310 final int height = gridSize.getHeight(); 311 final int width = gridSize.getWidth(); 312 for (int j = 0; j < height; j++) { 313 for (int i = 0; i < width; i++) { 314 if (i < minX || i > maxX || j < minY || j > maxY) { 315 //Not preselected 316 Assert.assertFalse("Pre-selection", (grid.getFlags(i, j) & MapGrid.GRID_FLAG_SELECTING) > 0); 317 } else { 318 //Preselected 319 Assert.assertTrue("Pre-selection", (grid.getFlags(i, j) & MapGrid.GRID_FLAG_SELECTING) > 0); 320 } 321 } 322 323 } 324 } 325 326 /** 327 * Checks that a {@link MapGrid} includes a rectangle of {@link 328 * MapGrid#GRID_FLAG_SELECTION}. Squares outside the rectangle are not 329 * checked. 330 * @param grid the map grid to check 331 * @param start one corner of the rectangle 332 * @param end the diagonally opposite corner of the rectangle 333 * @param flag the expected selection state 334 */ 335 private static void assertSelection(@NotNull final MapGrid grid, @NotNull final Point start, @NotNull final Point end, final boolean flag) { 336 final int minX = Math.min(start.x, end.x); 337 final int maxX = Math.max(start.x, end.x); 338 final int minY = Math.min(start.y, end.y); 339 final int maxY = Math.max(start.y, end.y); 340 for (int j = minY; j <= maxY; j++) { 341 for (int i = minX; i <= maxX; i++) { 342 Assert.assertSame("Selection", flag, (grid.getFlags(i, j) & MapGrid.GRID_FLAG_SELECTION) > 0); 343 } 344 } 345 } 346 347 /** 348 * Checks for correct behavior of {@link MapGrid#GRID_FLAG_SELECTING} flags 349 * during selecting. 350 */ 351 @Test 352 public void selecting() { 353 final MapGrid grid = new MapGrid(gridSize); 354 final MapCursor<TestGameObject, TestMapArchObject, TestArchetype> cursor = createCursor(grid); 355 final Point start = new Point(2, 3); 356 final Point end = new Point(4, 5); 357 final Point gridMaxIndex = new Point(gridSize.getWidth() - 1, gridSize.getHeight() - 1); 358 cursor.dragRelease(); 359 testEvents(0, 0); 360 cursor.setLocation(start); 361 testEvents(1, 0); 362 cursor.dragStart(); 363 testEvents(0, 1); 364 cursor.dragTo(end); 365 testEvents(1, 0); 366 Assert.assertTrue("MapCursor should be in drag mode.", cursor.isDragging()); 367 cursor.dragRelease(); 368 testEvents(0, 1); 369 Assert.assertFalse("MapCursor should not be in drag mode.", cursor.isDragging()); 370 cursor.setLocation(start); 371 testEvents(1, 0); 372 cursor.dragStart(); 373 testEvents(0, 1); 374 cursor.dragTo(end); 375 testEvents(1, 0); 376 Assert.assertTrue("MapCursor should be in drag mode.", cursor.isDragging()); 377 cursor.dragRelease(); 378 testEvents(0, 1); 379 Assert.assertFalse("MapCursor should not be in drag mode.", cursor.isDragging()); 380 cursor.setLocation(start); 381 testEvents(1, 0); 382 cursor.dragStart(); 383 testEvents(0, 1); 384 cursor.dragTo(end); 385 testEvents(1, 0); 386 Assert.assertTrue("MapCursor should be in drag mode.", cursor.isDragging()); 387 assertSelection(grid, new Point(0, 0), gridMaxIndex, false); 388 cursor.dragSelect(SelectionMode.ADD); 389 testEvents(0, 1); 390 assertSelection(grid, start, end, true); 391 //Check if nothing is preselected 392 assertPreSelection(grid, new Point(-1, -1), new Point(-1, -1)); 393 cursor.setLocation(start); 394 testEvents(1, 0); 395 cursor.dragStart(); 396 testEvents(0, 1); 397 cursor.dragTo(end); 398 testEvents(1, 0); 399 cursor.dragSelect(SelectionMode.SUB); 400 testEvents(0, 1); 401 assertSelection(grid, new Point(0, 0), gridMaxIndex, false); 402 cursor.setLocation(start); 403 testEvents(1, 0); 404 cursor.dragStart(); 405 testEvents(0, 1); 406 cursor.dragTo(end); 407 testEvents(1, 0); 408 cursor.dragSelect(SelectionMode.FLIP); 409 testEvents(0, 1); 410 assertSelection(grid, start, end, true); 411 start.setLocation(3, 4); 412 end.setLocation(5, 1); 413 cursor.setLocation(start); 414 testEvents(1, 0); 415 cursor.dragStart(); 416 testEvents(0, 1); 417 cursor.dragTo(end); 418 testEvents(1, 0); 419 cursor.dragSelect(SelectionMode.FLIP); 420 testEvents(0, 1); 421 assertSelection(grid, start, new Point(4, 4), false); 422 assertSelection(grid, new Point(3, 2), end, true); 423 assertSelection(grid, new Point(5, 3), new Point(5, 4), true); 424 cursor.deactivate(); 425 testEvents(0, 0); 426 assertSelection(grid, new Point(0, 0), gridMaxIndex, false); 427 } 428 429 /** 430 * Checks if the number of events fired is correct. 431 * @param nPos the expected number of position events 432 * @param nMode the expected number of mode events 433 */ 434 private static void testEvents(final int nPos, final int nMode) { 435 Assert.assertEquals("Position change event", nPos, listener.changedPosCounter); 436 listener.changedPosCounter = 0; 437 Assert.assertEquals("Mode change event", nMode, listener.changedModeCounter); 438 listener.changedModeCounter = 0; 439 } 440 441 /** 442 * Returns a new test suite containing this test. 443 * @return the new test suite 444 */ 445 @NotNull 446 public static junit.framework.Test suite() { 447 return new JUnit4TestAdapter(MapCursorTest.class); 448 } 449 450 /** 451 * A {@link MapCursorListener} that counts the number of event callbacks. 452 */ 453 private static class TestMapCursorListener implements MapCursorListener<TestGameObject, TestMapArchObject, TestArchetype> { 454 455 /** 456 * The number of calls to {@link #mapCursorChangedPos(Point)}. 457 */ 458 private int changedPosCounter; 459 460 /** 461 * The number of calls to {@link #mapCursorChangedMode()}. 462 */ 463 private int changedModeCounter; 464 465 @Override 466 public void mapCursorChangedPos(@NotNull final Point location) { 467 changedPosCounter++; 468 } 469 470 @Override 471 public void mapCursorChangedMode() { 472 changedModeCounter++; 473 } 474 475 @Override 476 public void mapCursorChangedGameObject(@Nullable final MapSquare<TestGameObject, TestMapArchObject, TestArchetype> mapSquare, @Nullable final TestGameObject gameObject) { 477 // ignore 478 } 479 480 @Override 481 public void mapCursorChangedSize() { 482 // ignore 483 } 484 485 } 486 487}