/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ide.common.layout; import static com.android.SdkConstants.ANDROID_URI; import static com.android.SdkConstants.ATTR_ID; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.ide.common.api.DropFeedback; import com.android.ide.common.api.IClientRulesEngine; import com.android.ide.common.api.IDragElement; import com.android.ide.common.api.INode; import com.android.ide.common.api.IValidator; import com.android.ide.common.api.IViewMetadata; import com.android.ide.common.api.IViewRule; import com.android.ide.common.api.Margins; import com.android.ide.common.api.Point; import com.android.ide.common.api.Rect; import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import junit.framework.TestCase; /** * Common layout helpers from LayoutRule tests */ @SuppressWarnings("javadoc") public class LayoutTestBase extends TestCase { /** * Helper function used by tests to drag a button into a canvas containing * the given children. * * @param rule The rule to test on * @param targetNode The target layout node to drag into * @param dragBounds The (original) bounds of the dragged item * @param dropPoint The drag point we should drag to and drop * @param secondDropPoint An optional second drag point to drag to before * drawing graphics and dropping (or null if not applicable) * @param insertIndex The expected insert position we end up with after * dropping at the dropPoint * @param currentIndex If the dragged widget is already in the canvas this * should be its child index; if not, pass in -1 * @param graphicsFragments This is a varargs array of String fragments * we expect to see in the graphics output on the drag over * event. * @return The inserted node */ protected INode dragInto(IViewRule rule, INode targetNode, Rect dragBounds, Point dropPoint, Point secondDropPoint, int insertIndex, int currentIndex, String... graphicsFragments) { String draggedButtonId = (currentIndex == -1) ? "@+id/DraggedButton" : targetNode .getChildren()[currentIndex].getStringAttr(ANDROID_URI, ATTR_ID); IDragElement[] elements = TestDragElement.create(TestDragElement.create( "android.widget.Button", dragBounds).id(draggedButtonId)); // Enter target DropFeedback feedback = rule.onDropEnter(targetNode, null/*targetView*/, elements); assertNotNull(feedback); assertFalse(feedback.invalidTarget); assertNotNull(feedback.painter); if (currentIndex != -1) { feedback.sameCanvas = true; } // Move near top left corner of the target feedback = rule.onDropMove(targetNode, elements, feedback, dropPoint); assertNotNull(feedback); if (secondDropPoint != null) { feedback = rule.onDropMove(targetNode, elements, feedback, secondDropPoint); assertNotNull(feedback); } if (insertIndex == -1) { assertTrue(feedback.invalidTarget); } else { assertFalse(feedback.invalidTarget); } // Paint feedback and make sure it's what we expect TestGraphics graphics = new TestGraphics(); assertNotNull(feedback.painter); feedback.painter.paint(graphics, targetNode, feedback); String drawn = graphics.getDrawn().toString(); // Check that each graphics fragment is drawn for (String fragment : graphicsFragments) { if (!drawn.contains(fragment)) { // Get drawn-output since unit test truncates message in below // contains-assertion System.out.println("Could not find: " + fragment); System.out.println("Full graphics output: " + drawn); } assertTrue(fragment + " not found; full=" + drawn, drawn.contains(fragment)); } // Attempt a drop? if (insertIndex == -1) { // No, not expected to succeed (for example, when drop point is over an // invalid region in RelativeLayout) - just return. return null; } int childrenCountBefore = targetNode.getChildren().length; rule.onDropped(targetNode, elements, feedback, dropPoint); if (currentIndex == -1) { // Inserting new from outside assertEquals(childrenCountBefore+1, targetNode.getChildren().length); } else { // Moving from existing; must remove in old position first ((TestNode) targetNode).removeChild(currentIndex); assertEquals(childrenCountBefore, targetNode.getChildren().length); } // Ensure that it's inserted in the right place String actualId = targetNode.getChildren()[insertIndex].getStringAttr( ANDROID_URI, ATTR_ID); if (!draggedButtonId.equals(actualId)) { // Using assertEquals instead of fail to get nice diff view on test // failure List<String> childrenIds = new ArrayList<String>(); for (INode child : targetNode.getChildren()) { childrenIds.add(child.getStringAttr(ANDROID_URI, ATTR_ID)); } int index = childrenIds.indexOf(draggedButtonId); String message = "Button found at index " + index + " instead of " + insertIndex + " among " + childrenIds; System.out.println(message); assertEquals(message, draggedButtonId, actualId); } return targetNode.getChildren()[insertIndex]; } /** * Utility method for asserting that two collections contain exactly the * same elements (regardless of order) * @param expected expected collection * @param actual actual collection */ public static void assertContainsSame(Collection<String> expected, Collection<String> actual) { if (expected.size() != actual.size()) { fail("Collection sizes differ; expected " + expected.size() + " but was " + actual.size()); } // Sort prior to comparison to ensure we have the same elements // regardless of order List<String> expectedList = new ArrayList<String>(expected); Collections.sort(expectedList); List<String> actualList = new ArrayList<String>(actual); Collections.sort(actualList); // Instead of just assertEquals(expectedList, actualList); // we iterate one element at a time so we can show the first // -difference-. for (int i = 0; i < expectedList.size(); i++) { String expectedElement = expectedList.get(i); String actualElement = actualList.get(i); if (!expectedElement.equals(actualElement)) { System.out.println("Expected items: " + expectedList); System.out.println("Actual items : " + actualList); } assertEquals("Collections differ; first difference:", expectedElement, actualElement); } } protected void initialize(IViewRule rule, String fqn) { rule.onInitialize(fqn, new TestRulesEngine(fqn)); } public static class TestRulesEngine implements IClientRulesEngine { private final String mFqn; public TestRulesEngine(String fqn) { mFqn = fqn; } @Override public void debugPrintf(@NonNull String msg, Object... params) { fail("Not supported in tests yet"); } @Override public void displayAlert(@NonNull String message) { fail("Not supported in tests yet"); } @Override public String displayInput(@NonNull String message, @Nullable String value, @Nullable IValidator filter) { fail("Not supported in tests yet"); return null; } @Override public @NonNull String getFqcn() { return mFqn; } @Override public @NonNull IViewMetadata getMetadata(final @NonNull String fqcn) { return new IViewMetadata() { @Override public @NonNull String getDisplayName() { // This also works when there is no "." return fqcn.substring(fqcn.lastIndexOf('.') + 1); } @Override public @NonNull FillPreference getFillPreference() { return ViewMetadataRepository.get().getFillPreference(fqcn); } @Override public @NonNull Margins getInsets() { return null; } @Override public @NonNull List<String> getTopAttributes() { return ViewMetadataRepository.get().getTopAttributes(fqcn); } }; } @Override public int getMinApiLevel() { return 8; } @Override public IViewRule loadRule(@NonNull String fqcn) { fail("Not supported in tests yet"); return null; } @Override public String displayReferenceInput(String currentValue) { fail("Not supported in tests yet"); return null; } @Override public IValidator getResourceValidator(String resourceTypeName, boolean uniqueInProject, boolean uniqueInLayout, boolean exists, String... allowed) { fail("Not supported in tests yet"); return null; } @Override public String displayResourceInput(@NonNull String resourceTypeName, @Nullable String currentValue) { fail("Not supported in tests yet"); return null; } @Override public String[] displayMarginInput(@Nullable String all, @Nullable String left, @Nullable String right, @Nullable String top, @Nullable String bottom) { fail("Not supported in tests yet"); return null; } @Override public String displayIncludeSourceInput() { fail("Not supported in tests yet"); return null; } @Override public void select(@NonNull Collection<INode> nodes) { fail("Not supported in tests yet"); } @Override public String displayFragmentSourceInput() { fail("Not supported in tests yet"); return null; } @Override public void layout() { fail("Not supported in tests yet"); } @Override public void redraw() { fail("Not supported in tests yet"); } @Override public Map<INode, Rect> measureChildren(@NonNull INode parent, @Nullable AttributeFilter filter) { return null; } @Override public int pxToDp(int px) { // Arbitrary conversion return px / 3; } @Override public int dpToPx(int dp) { // Arbitrary conversion return 3 * dp; } @Override public @NonNull String getUniqueId(@NonNull String prefix) { fail("Not supported in tests yet"); return null; } @Override public int screenToLayout(int pixels) { fail("Not supported in tests yet"); return pixels; } @Override public @NonNull String getAppNameSpace() { fail("Not supported in tests yet"); return null; } @Override public @Nullable Object getViewObject(@NonNull INode node) { fail("Not supported in tests yet"); return null; } @Override public boolean rename(INode node) { fail("Not supported in tests yet"); return false; } } public void testDummy() { // To avoid JUnit warning that this class contains no tests, even though // this is an abstract class and JUnit shouldn't try } }