1 /*
2  * PlanControllerTest.java 31 mai 2006
3  *
4  * Copyright (c) 2006 Emmanuel PUYBARET / eTeks <info@eteks.com>. All Rights
5  * Reserved.
6  *
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19  * Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21 package com.eteks.sweethome3d.junit;
22 
23 import java.awt.EventQueue;
24 import java.lang.reflect.InvocationTargetException;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.List;
29 import java.util.Locale;
30 
31 import javax.swing.undo.UndoManager;
32 import javax.swing.undo.UndoableEditSupport;
33 
34 import junit.framework.TestCase;
35 
36 import com.eteks.sweethome3d.io.DefaultUserPreferences;
37 import com.eteks.sweethome3d.model.CatalogPieceOfFurniture;
38 import com.eteks.sweethome3d.model.CollectionEvent;
39 import com.eteks.sweethome3d.model.CollectionListener;
40 import com.eteks.sweethome3d.model.FurnitureCategory;
41 import com.eteks.sweethome3d.model.Home;
42 import com.eteks.sweethome3d.model.HomeFurnitureGroup;
43 import com.eteks.sweethome3d.model.HomePieceOfFurniture;
44 import com.eteks.sweethome3d.model.Selectable;
45 import com.eteks.sweethome3d.model.UserPreferences;
46 import com.eteks.sweethome3d.model.Wall;
47 import com.eteks.sweethome3d.swing.SwingViewFactory;
48 import com.eteks.sweethome3d.viewcontroller.PlanController;
49 import com.eteks.sweethome3d.viewcontroller.ViewFactory;
50 
51 /**
52  * Tests {@link com.eteks.sweethome3d.viewcontroller.PlanController plan controller}.
53  * @author Emmanuel Puybaret
54  */
55 public class PlanControllerTest extends TestCase {
56   /**
57    * Performs the same tests as {@link PlanComponentTest#testPlanComponent()}
58    * but with direct calls to controller in memory.
59    */
testPlanContoller()60   public void testPlanContoller() throws InterruptedException, InvocationTargetException {
61     // Run test in Event Dispatch Thread because the default view associated
62     // to plan controller instance performs some actions in EDT even if it's not displayed
63     EventQueue.invokeAndWait(new Runnable() {
64         public void run() {
65           runPlanContollerTest();
66         }
67       });
68   }
69 
runPlanContollerTest()70   private void runPlanContollerTest() {
71     // 1. Create a frame that displays a PlanComponent at its preferred size,
72     Home home = new Home();
73     Locale.setDefault(Locale.FRANCE);
74     UserPreferences preferences = new DefaultUserPreferences();
75     ViewFactory viewFactory = new SwingViewFactory();
76     UndoableEditSupport undoSupport = new UndoableEditSupport();
77     UndoManager undoManager = new UndoManager();
78     undoSupport.addUndoableEditListener(undoManager);
79     PlanController planController =
80         new PlanController(home, preferences, viewFactory, null, undoSupport);
81 
82     // Build an ordered list of walls added to home
83     final ArrayList<Wall> orderedWalls = new ArrayList<Wall>();
84     home.addWallsListener(new CollectionListener<Wall> () {
85       public void collectionChanged(CollectionEvent<Wall> ev) {
86         if (ev.getType() == CollectionEvent.Type.ADD) {
87           orderedWalls.add(ev.getItem());
88         }
89       }
90     });
91 
92     // 2. Use WALL_CREATION mode
93     planController.setMode(PlanController.Mode.WALL_CREATION);
94     // Click at (20, 20), (500, 22), (498, 300), then double click at (20, 302) in home coordinates space
95     planController.moveMouse(20, 20);
96     planController.pressMouse(20, 20, 1, false, false);
97     planController.toggleMagnetism(false);
98     planController.releaseMouse(20, 20);
99     planController.moveMouse(500, 22);
100     planController.pressMouse(500, 22, 1, false, false);
101     planController.releaseMouse(500, 22);
102     planController.moveMouse(498, 300);
103     planController.pressMouse(498, 300, 1, false, false);
104     planController.releaseMouse(498, 300);
105     planController.moveMouse(20, 302);
106     planController.pressMouse(20, 302, 1, false, false);
107     planController.releaseMouse(20, 302);
108     planController.pressMouse(20, 302, 2, false, false);
109     planController.releaseMouse(20, 302);
110     // Check 3 walls were created at (20, 20), (500, 20), (500, 300) and (20, 300) coordinates
111     Wall wall1 = orderedWalls.get(0);
112     assertCoordinatesEqualWallPoints(20, 20, 500, 20, wall1);
113     Wall wall2 = orderedWalls.get(1);
114     assertCoordinatesEqualWallPoints(500, 20, 500, 300, wall2);
115     Wall wall3 = orderedWalls.get(2);
116     assertCoordinatesEqualWallPoints(500, 300, 20, 300, wall3);
117     // Check they are joined to each other end point
118     assertWallsAreJoined(null, wall1, wall2);
119     assertWallsAreJoined(wall1, wall2, wall3);
120     assertWallsAreJoined(wall2, wall3, null);
121     // Check they are selected
122     assertSelectionContains(home, wall1, wall2, wall3);
123 
124     // 3. Click at (20, 300), then double click at (60, 60) with Alt key depressed
125     planController.moveMouse(20, 300);
126     planController.pressMouse(20, 300, 1, false, false);
127     planController.releaseMouse(20, 300);
128     planController.toggleMagnetism(true);
129     planController.moveMouse(60, 60);
130     planController.pressMouse(60, 60, 1, false, false);
131     planController.releaseMouse(60, 60);
132     planController.pressMouse(60, 60, 2, false, false);
133     planController.releaseMouse(60, 60);
134     planController.toggleMagnetism(false);
135     // Check a forth wall was created at (20, 300), (60, 60) coordinates
136     Wall wall4 = orderedWalls.get(orderedWalls.size() - 1);
137     assertCoordinatesEqualWallPoints(20, 300, 60, 60, wall4);
138     assertSelectionContains(home, wall4);
139     assertWallsAreJoined(wall3, wall4, null);
140 
141     // 4. Use SELECTION mode
142     planController.setMode(PlanController.Mode.SELECTION);
143     // Check current mode is SELECTION
144     assertEquals("Current mode isn't " + PlanController.Mode.SELECTION,
145         PlanController.Mode.SELECTION, planController.getMode());
146     // Press the delete key
147     planController.deleteSelection();
148     // Check plan contains only the first three walls
149     assertHomeContains(home, wall1, wall2, wall3);
150 
151     // 5. Use WALL_CREATION mode
152     planController.setMode(PlanController.Mode.WALL_CREATION);
153     //  Click at (22, 18), then double click at (20, 300)
154     planController.moveMouse(22, 18);
155     planController.pressMouse(22, 18, 1, false, false);
156     planController.releaseMouse(22, 18);
157     planController.moveMouse(20, 300);
158     planController.pressMouse(20, 300, 1, false, false);
159     planController.releaseMouse(20, 300);
160     planController.pressMouse(20, 300, 2, false, false);
161     planController.releaseMouse(20, 300);
162     // Check a new forth wall was created at (20, 20), (20, 300) coordinates
163     wall4 = orderedWalls.get(orderedWalls.size() - 1);
164     assertCoordinatesEqualWallPoints(20, 20, 20, 300, wall4);
165     // Check its end points are joined to the first and third wall
166     assertWallsAreJoined(wall1, wall4, wall3);
167 
168     // 6. Use SELECTION mode
169     planController.setMode(PlanController.Mode.SELECTION);
170     // Drag and drop cursor from (360, 160) to (560, 320)
171     planController.moveMouse(360, 160);
172     planController.pressMouse(360, 160, 1, false, false);
173     planController.moveMouse(560, 320);
174     planController.releaseMouse(560, 320);
175     // Check the selected walls are the second and third ones
176     assertSelectionContains(home, wall2, wall3);
177 
178     // 7. Press twice right arrow key
179     planController.moveSelection(2, 0);
180     planController.moveSelection(2, 0);
181     // Check the 4 walls coordinates are (20, 20), (504, 20), (504, 300), (24, 300)
182     assertCoordinatesEqualWallPoints(20, 20, 504, 20, wall1);
183     assertCoordinatesEqualWallPoints(504, 20, 504, 300, wall2);
184     assertCoordinatesEqualWallPoints(504, 300, 24, 300, wall3);
185     assertCoordinatesEqualWallPoints(20, 20, 24, 300, wall4);
186 
187     // 8. Click at (504, 40)
188     planController.moveMouse(504, 40);
189     planController.pressMouse(504, 40, 1, true, false);
190     planController.releaseMouse(504, 40);
191     // Check the second wall was removed from selection
192     assertSelectionContains(home, wall3);
193 
194      // 9. Drag cursor from (60, 20) to (60, 60)
195     planController.moveMouse(60, 20);
196     planController.pressMouse(60, 20, 1, false, false);
197     planController.moveMouse(60, 60);
198     // Check first wall is selected and that it moved
199     assertSelectionContains(home, wall1);
200     assertCoordinatesEqualWallPoints(20, 60, 504, 60, wall1);
201     // Lose focus
202     planController.escape();
203     // Check the wall didn't move at end
204     assertCoordinatesEqualWallPoints(20, 20, 504, 20, wall1);
205 
206     // 10. Undo 8 times
207     for (int i = 0; i < 6; i++) {
208       undoManager.undo();
209     }
210     // Check home doesn't contain any wall
211     assertHomeContains(home);
212 
213     // 11. Redo 8 times
214     for (int i = 0; i < 6; i++) {
215       undoManager.redo();
216     }
217     // Check plan contains the four wall
218     assertHomeContains(home, wall1, wall2, wall3, wall4);
219     // Check the second and the third wall are selected
220     assertSelectionContains(home, wall2, wall3);
221   }
222 
223   /**
224    * Tests how the children of a group and a subgroup are resized.
225    */
testFurnitureGroupResizing()226   public void testFurnitureGroupResizing() {
227     Home home = new Home();
228     Locale.setDefault(Locale.ENGLISH);
229     UserPreferences preferences = new DefaultUserPreferences();
230     ViewFactory viewFactory = new SwingViewFactory();
231     UndoableEditSupport undoSupport = new UndoableEditSupport();
232     UndoManager undoManager = new UndoManager();
233     undoSupport.addUndoableEditListener(undoManager);
234     PlanController planController =
235         new PlanController(home, preferences, viewFactory, null, undoSupport);
236 
237     CatalogPieceOfFurniture box = null;
238     for (FurnitureCategory category : preferences.getFurnitureCatalog().getCategories()) {
239       if ("Miscellaneous".equals(category.getName())) {
240         for (CatalogPieceOfFurniture piece : category.getFurniture()) {
241           if ("Box".equals(piece.getName())) {
242             box = piece;
243             break;
244           }
245         }
246       }
247     }
248     assertNotNull("Couldn't find box", box);
249     // 1. Add two boxes to plan
250     HomePieceOfFurniture box1 = new HomePieceOfFurniture(box);
251     home.addPieceOfFurniture(box1);
252     box1.setX(50);
253     box1.setY(50);
254     HomePieceOfFurniture box2 = new HomePieceOfFurniture(box);
255     home.addPieceOfFurniture(box2);
256     box2.setX(200);
257     box2.setY(50);
258     box2.setWidth(200);
259     // Check plan controller updates box size in plan
260     assertEquals("Box width in plan incorrect", 200f, box2.getWidthInPlan());
261 
262     // 2. Group them
263     home.setSelectedItems(Arrays.asList(box1, box2));
264     planController.groupSelectedFurniture();
265     List<HomePieceOfFurniture> furniture = home.getFurniture();
266     assertEquals("Wrong count of pieces", 1, furniture.size());
267     HomePieceOfFurniture group1 = furniture.get(0);
268     assertTrue("Group not created", group1 instanceof HomeFurnitureGroup);
269     assertEquals("Wrong X", 150f, group1.getX());
270 
271     // 3. Group the subgroup with an other box
272     HomePieceOfFurniture box3 = new HomePieceOfFurniture(box);
273     home.addPieceOfFurniture(box3);
274     box3.setX(150);
275     box3.setWidth(300);
276     box3.setY(250);
277     home.setSelectedItems(Arrays.asList(group1, box3));
278     planController.groupSelectedFurniture();
279     furniture = home.getFurniture();
280     HomePieceOfFurniture mainGroup = furniture.get(0);
281     assertTrue("Group not created", mainGroup instanceof HomeFurnitureGroup);
282     assertEquals("Wrong X", 150f, mainGroup.getX());
283     assertEquals("Wrong Y", 150f, mainGroup.getY());
284 
285     // 4. Resize group with mouse
286     planController.moveMouse(300, 300);
287     planController.pressMouse(300, 300, 1, false, false);
288     planController.moveMouse(600, 600);
289     planController.releaseMouse(600, 600);
290     assertEquals("Wrong X", 300f, mainGroup.getX());
291     assertEquals("Wrong Y", 300f, mainGroup.getY());
292     assertEquals("Wrong width in plan", 600f, group1.getWidthInPlan());
293     assertEquals("Wrong depth in plan", 200f, group1.getDepthInPlan());
294     assertEquals("Wrong width", 400f, box2.getWidth());
295     assertEquals("Wrong width in plan", 400f, box2.getWidthInPlan());
296     assertEquals("Wrong depth in plan", 200f, box2.getDepthInPlan());
297 
298     // 5. Resize group directly
299     mainGroup.setWidth(150);
300     assertEquals("Wrong width in plan", 150f, mainGroup.getWidthInPlan());
301     assertEquals("Wrong width", 150f, group1.getWidth());
302     assertEquals("Wrong width in plan", 150f, group1.getWidthInPlan());
303     assertEquals("Wrong width", 100f, box2.getWidth());
304     assertEquals("Wrong width in plan", 100f, box2.getWidthInPlan());
305   }
306 
307   /**
308    * Asserts the start point and the end point of
309    * <code>wall</code> are at (<code>xStart</code>, <code>yStart</code>), (<code>xEnd</code>, <code>yEnd</code>).
310    */
assertCoordinatesEqualWallPoints(float xStart, float yStart, float xEnd, float yEnd, Wall wall)311   private void assertCoordinatesEqualWallPoints(float xStart, float yStart, float xEnd, float yEnd, Wall wall) {
312     assertTrue("Incorrect X start " + xStart + " " + wall.getXStart(),
313         Math.abs(xStart - wall.getXStart()) < 1E-10);
314     assertTrue("Incorrect Y start " + yStart + " " + wall.getYStart(),
315         Math.abs(yStart - wall.getYStart()) < 1E-10);
316     assertTrue("Incorrect X end " + xEnd + " " + wall.getXEnd(),
317         Math.abs(xEnd - wall.getXEnd()) < 1E-10);
318     assertTrue("Incorrect Y end " + yEnd + " " + wall.getYEnd(),
319         Math.abs(yEnd - wall.getYEnd()) < 1E-10);
320   }
321 
322   /**
323    * Asserts <code>wall</code> is joined to <code>wallAtStart</code>
324    * and <code>wallAtEnd</code>.
325    */
assertWallsAreJoined(Wall wallAtStart, Wall wall, Wall wallAtEnd)326   private void assertWallsAreJoined(Wall wallAtStart, Wall wall, Wall wallAtEnd) {
327     assertSame("Incorrect wall at start", wallAtStart, wall.getWallAtStart());
328     assertSame("Incorrect wall at end", wallAtEnd, wall.getWallAtEnd());
329   }
330 
331   /**
332    * Asserts <code>home</code> contains <code>walls</code>.
333    */
assertHomeContains(Home home, Wall ... walls)334   private void assertHomeContains(Home home, Wall ... walls) {
335     Collection<Wall> planWalls = home.getWalls();
336     assertEquals("Home walls incorrect count",
337         walls.length, planWalls.size());
338     for (Wall wall : walls) {
339       assertTrue("Wall doesn't belong to plan", planWalls.contains(wall));
340     }
341   }
342 
343   /**
344    * Asserts <code>walls</code> are the current selected ones in <code>home</code>.
345    */
assertSelectionContains(Home home, Wall ... walls)346   private void assertSelectionContains(Home home,
347                                        Wall ... walls) {
348     List<Selectable> selectedItems = home.getSelectedItems();
349     assertEquals(walls.length, selectedItems.size());
350     for (Wall wall : walls) {
351       assertTrue("Wall not selected", selectedItems.contains(wall));
352     }
353   }
354 }
355