| /* |
| * 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; |
| } |
| |
| @Override |
| @Nullable |
| public String displayCustomViewClassInput() { |
| fail("Not supported in tests yet"); |
| return null; |
| } |
| } |
| |
| 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 |
| } |
| } |