1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ide.common.layout; 18 19 import static com.android.SdkConstants.ANDROID_URI; 20 import static com.android.SdkConstants.ATTR_ID; 21 22 import com.android.annotations.NonNull; 23 import com.android.annotations.Nullable; 24 import com.android.ide.common.api.DropFeedback; 25 import com.android.ide.common.api.IClientRulesEngine; 26 import com.android.ide.common.api.IDragElement; 27 import com.android.ide.common.api.INode; 28 import com.android.ide.common.api.IValidator; 29 import com.android.ide.common.api.IViewMetadata; 30 import com.android.ide.common.api.IViewRule; 31 import com.android.ide.common.api.Margins; 32 import com.android.ide.common.api.Point; 33 import com.android.ide.common.api.Rect; 34 import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository; 35 36 import java.util.ArrayList; 37 import java.util.Collection; 38 import java.util.Collections; 39 import java.util.List; 40 import java.util.Map; 41 42 import junit.framework.TestCase; 43 44 /** 45 * Common layout helpers from LayoutRule tests 46 */ 47 @SuppressWarnings("javadoc") 48 public class LayoutTestBase extends TestCase { 49 /** 50 * Helper function used by tests to drag a button into a canvas containing 51 * the given children. 52 * 53 * @param rule The rule to test on 54 * @param targetNode The target layout node to drag into 55 * @param dragBounds The (original) bounds of the dragged item 56 * @param dropPoint The drag point we should drag to and drop 57 * @param secondDropPoint An optional second drag point to drag to before 58 * drawing graphics and dropping (or null if not applicable) 59 * @param insertIndex The expected insert position we end up with after 60 * dropping at the dropPoint 61 * @param currentIndex If the dragged widget is already in the canvas this 62 * should be its child index; if not, pass in -1 63 * @param graphicsFragments This is a varargs array of String fragments 64 * we expect to see in the graphics output on the drag over 65 * event. 66 * @return The inserted node 67 */ dragInto(IViewRule rule, INode targetNode, Rect dragBounds, Point dropPoint, Point secondDropPoint, int insertIndex, int currentIndex, String... graphicsFragments)68 protected INode dragInto(IViewRule rule, INode targetNode, Rect dragBounds, Point dropPoint, 69 Point secondDropPoint, int insertIndex, int currentIndex, 70 String... graphicsFragments) { 71 72 String draggedButtonId = (currentIndex == -1) ? "@+id/DraggedButton" : targetNode 73 .getChildren()[currentIndex].getStringAttr(ANDROID_URI, ATTR_ID); 74 75 IDragElement[] elements = TestDragElement.create(TestDragElement.create( 76 "android.widget.Button", dragBounds).id(draggedButtonId)); 77 78 // Enter target 79 DropFeedback feedback = rule.onDropEnter(targetNode, null/*targetView*/, elements); 80 assertNotNull(feedback); 81 assertFalse(feedback.invalidTarget); 82 assertNotNull(feedback.painter); 83 84 if (currentIndex != -1) { 85 feedback.sameCanvas = true; 86 } 87 88 // Move near top left corner of the target 89 feedback = rule.onDropMove(targetNode, elements, feedback, dropPoint); 90 assertNotNull(feedback); 91 92 if (secondDropPoint != null) { 93 feedback = rule.onDropMove(targetNode, elements, feedback, secondDropPoint); 94 assertNotNull(feedback); 95 } 96 97 if (insertIndex == -1) { 98 assertTrue(feedback.invalidTarget); 99 } else { 100 assertFalse(feedback.invalidTarget); 101 } 102 103 // Paint feedback and make sure it's what we expect 104 TestGraphics graphics = new TestGraphics(); 105 assertNotNull(feedback.painter); 106 feedback.painter.paint(graphics, targetNode, feedback); 107 String drawn = graphics.getDrawn().toString(); 108 109 // Check that each graphics fragment is drawn 110 for (String fragment : graphicsFragments) { 111 if (!drawn.contains(fragment)) { 112 // Get drawn-output since unit test truncates message in below 113 // contains-assertion 114 System.out.println("Could not find: " + fragment); 115 System.out.println("Full graphics output: " + drawn); 116 } 117 assertTrue(fragment + " not found; full=" + drawn, drawn.contains(fragment)); 118 } 119 120 // Attempt a drop? 121 if (insertIndex == -1) { 122 // No, not expected to succeed (for example, when drop point is over an 123 // invalid region in RelativeLayout) - just return. 124 return null; 125 } 126 int childrenCountBefore = targetNode.getChildren().length; 127 rule.onDropped(targetNode, elements, feedback, dropPoint); 128 129 if (currentIndex == -1) { 130 // Inserting new from outside 131 assertEquals(childrenCountBefore+1, targetNode.getChildren().length); 132 } else { 133 // Moving from existing; must remove in old position first 134 ((TestNode) targetNode).removeChild(currentIndex); 135 136 assertEquals(childrenCountBefore, targetNode.getChildren().length); 137 } 138 // Ensure that it's inserted in the right place 139 String actualId = targetNode.getChildren()[insertIndex].getStringAttr( 140 ANDROID_URI, ATTR_ID); 141 if (!draggedButtonId.equals(actualId)) { 142 // Using assertEquals instead of fail to get nice diff view on test 143 // failure 144 List<String> childrenIds = new ArrayList<String>(); 145 for (INode child : targetNode.getChildren()) { 146 childrenIds.add(child.getStringAttr(ANDROID_URI, ATTR_ID)); 147 } 148 int index = childrenIds.indexOf(draggedButtonId); 149 String message = "Button found at index " + index + " instead of " + insertIndex 150 + " among " + childrenIds; 151 System.out.println(message); 152 assertEquals(message, draggedButtonId, actualId); 153 } 154 155 156 return targetNode.getChildren()[insertIndex]; 157 } 158 159 /** 160 * Utility method for asserting that two collections contain exactly the 161 * same elements (regardless of order) 162 * @param expected expected collection 163 * @param actual actual collection 164 */ assertContainsSame(Collection<String> expected, Collection<String> actual)165 public static void assertContainsSame(Collection<String> expected, Collection<String> actual) { 166 if (expected.size() != actual.size()) { 167 fail("Collection sizes differ; expected " + expected.size() + " but was " 168 + actual.size()); 169 } 170 171 // Sort prior to comparison to ensure we have the same elements 172 // regardless of order 173 List<String> expectedList = new ArrayList<String>(expected); 174 Collections.sort(expectedList); 175 List<String> actualList = new ArrayList<String>(actual); 176 Collections.sort(actualList); 177 // Instead of just assertEquals(expectedList, actualList); 178 // we iterate one element at a time so we can show the first 179 // -difference-. 180 for (int i = 0; i < expectedList.size(); i++) { 181 String expectedElement = expectedList.get(i); 182 String actualElement = actualList.get(i); 183 if (!expectedElement.equals(actualElement)) { 184 System.out.println("Expected items: " + expectedList); 185 System.out.println("Actual items : " + actualList); 186 } 187 assertEquals("Collections differ; first difference:", expectedElement, actualElement); 188 } 189 } 190 initialize(IViewRule rule, String fqn)191 protected void initialize(IViewRule rule, String fqn) { 192 rule.onInitialize(fqn, new TestRulesEngine(fqn)); 193 } 194 195 public static class TestRulesEngine implements IClientRulesEngine { 196 private final String mFqn; 197 TestRulesEngine(String fqn)198 public TestRulesEngine(String fqn) { 199 mFqn = fqn; 200 } 201 202 @Override debugPrintf(@onNull String msg, Object... params)203 public void debugPrintf(@NonNull String msg, Object... params) { 204 fail("Not supported in tests yet"); 205 } 206 207 @Override displayAlert(@onNull String message)208 public void displayAlert(@NonNull String message) { 209 fail("Not supported in tests yet"); 210 } 211 212 @Override displayInput(@onNull String message, @Nullable String value, @Nullable IValidator filter)213 public String displayInput(@NonNull String message, @Nullable String value, 214 @Nullable IValidator filter) { 215 fail("Not supported in tests yet"); 216 return null; 217 } 218 219 @Override getFqcn()220 public @NonNull String getFqcn() { 221 return mFqn; 222 } 223 224 @Override getMetadata(final @NonNull String fqcn)225 public @NonNull IViewMetadata getMetadata(final @NonNull String fqcn) { 226 return new IViewMetadata() { 227 @Override 228 public @NonNull String getDisplayName() { 229 // This also works when there is no "." 230 return fqcn.substring(fqcn.lastIndexOf('.') + 1); 231 } 232 233 @Override 234 public @NonNull FillPreference getFillPreference() { 235 return ViewMetadataRepository.get().getFillPreference(fqcn); 236 } 237 238 @Override 239 public @NonNull Margins getInsets() { 240 return null; 241 } 242 243 @Override 244 public @NonNull List<String> getTopAttributes() { 245 return ViewMetadataRepository.get().getTopAttributes(fqcn); 246 } 247 }; 248 } 249 250 @Override getMinApiLevel()251 public int getMinApiLevel() { 252 return 8; 253 } 254 255 @Override loadRule(@onNull String fqcn)256 public IViewRule loadRule(@NonNull String fqcn) { 257 fail("Not supported in tests yet"); 258 return null; 259 } 260 261 @Override displayReferenceInput(String currentValue)262 public String displayReferenceInput(String currentValue) { 263 fail("Not supported in tests yet"); 264 return null; 265 } 266 267 @Override getResourceValidator(String resourceTypeName, boolean uniqueInProject, boolean uniqueInLayout, boolean exists, String... allowed)268 public IValidator getResourceValidator(String resourceTypeName, boolean uniqueInProject, 269 boolean uniqueInLayout, boolean exists, String... allowed) { 270 fail("Not supported in tests yet"); 271 return null; 272 } 273 274 @Override displayResourceInput(@onNull String resourceTypeName, @Nullable String currentValue)275 public String displayResourceInput(@NonNull String resourceTypeName, 276 @Nullable String currentValue) { 277 fail("Not supported in tests yet"); 278 return null; 279 } 280 281 @Override displayMarginInput(@ullable String all, @Nullable String left, @Nullable String right, @Nullable String top, @Nullable String bottom)282 public String[] displayMarginInput(@Nullable String all, @Nullable String left, 283 @Nullable String right, @Nullable String top, @Nullable String bottom) { 284 fail("Not supported in tests yet"); 285 return null; 286 } 287 288 @Override displayIncludeSourceInput()289 public String displayIncludeSourceInput() { 290 fail("Not supported in tests yet"); 291 return null; 292 } 293 294 @Override select(@onNull Collection<INode> nodes)295 public void select(@NonNull Collection<INode> nodes) { 296 fail("Not supported in tests yet"); 297 } 298 299 @Override displayFragmentSourceInput()300 public String displayFragmentSourceInput() { 301 fail("Not supported in tests yet"); 302 return null; 303 } 304 305 @Override layout()306 public void layout() { 307 fail("Not supported in tests yet"); 308 } 309 310 @Override redraw()311 public void redraw() { 312 fail("Not supported in tests yet"); 313 } 314 315 @Override measureChildren(@onNull INode parent, @Nullable AttributeFilter filter)316 public Map<INode, Rect> measureChildren(@NonNull INode parent, 317 @Nullable AttributeFilter filter) { 318 return null; 319 } 320 321 @Override pxToDp(int px)322 public int pxToDp(int px) { 323 // Arbitrary conversion 324 return px / 3; 325 } 326 327 @Override dpToPx(int dp)328 public int dpToPx(int dp) { 329 // Arbitrary conversion 330 return 3 * dp; 331 } 332 333 @Override getUniqueId(@onNull String prefix)334 public @NonNull String getUniqueId(@NonNull String prefix) { 335 fail("Not supported in tests yet"); 336 return null; 337 } 338 339 @Override screenToLayout(int pixels)340 public int screenToLayout(int pixels) { 341 fail("Not supported in tests yet"); 342 return pixels; 343 } 344 345 @Override getAppNameSpace()346 public @NonNull String getAppNameSpace() { 347 fail("Not supported in tests yet"); 348 return null; 349 } 350 351 @Override getViewObject(@onNull INode node)352 public @Nullable Object getViewObject(@NonNull INode node) { 353 fail("Not supported in tests yet"); 354 return null; 355 } 356 357 @Override rename(INode node)358 public boolean rename(INode node) { 359 fail("Not supported in tests yet"); 360 return false; 361 } 362 363 @Override 364 @Nullable displayCustomViewClassInput()365 public String displayCustomViewClassInput() { 366 fail("Not supported in tests yet"); 367 return null; 368 } 369 } 370 371 public void testDummy() { 372 // To avoid JUnit warning that this class contains no tests, even though 373 // this is an abstract class and JUnit shouldn't try 374 } 375 } 376