diff options
author | Tor Norbye <tnorbye@google.com> | 2010-10-24 20:57:21 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2010-10-25 14:03:30 -0700 |
commit | 63a966c7d8a08f175d1a8aee688c74d0090af888 (patch) | |
tree | 3352565b4dddf8603cad1a996cc567833543abd4 | |
parent | 56eaf5a7e8a488e32beaa1fa68cc77b04a4accb0 (diff) | |
download | sdk-63a966c7d8a08f175d1a8aee688c74d0090af888.zip sdk-63a966c7d8a08f175d1a8aee688c74d0090af888.tar.gz sdk-63a966c7d8a08f175d1a8aee688c74d0090af888.tar.bz2 |
Add layout unit tests
Add layout unit tests, and some infrastructure for testing. Also fix
some formatting errors (>100 column lines) in the previous commit.
Change-Id: I3eabf30998ab7deb84df57e4d0c10cf57ee399d5
19 files changed, 1742 insertions, 6 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java index 6d640a1..e2d64eb 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java @@ -54,7 +54,11 @@ public class AbsoluteLayoutRule extends BaseLayout { }); } - void drawFeedback(IGraphics gc, INode targetNode, IDragElement[] elements, DropFeedback feedback) { + void drawFeedback( + IGraphics gc, + INode targetNode, + IDragElement[] elements, + DropFeedback feedback) { Rect b = targetNode.getBounds(); if (!b.isValid()) { return; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseLayout.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseLayout.java index 9d8e0c2..13b335e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseLayout.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseLayout.java @@ -355,7 +355,8 @@ public class BaseLayout extends BaseView { if (value != null && value.length() > 0) { newNode.setAttribute(uri, name, value); - if (uri.equals(ANDROID_URI) && name.equals(ATTR_ID) && oldId != null && !oldId.equals(value)) { + if (uri.equals(ANDROID_URI) && name.equals(ATTR_ID) && + oldId != null && !oldId.equals(value)) { newId = value; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseView.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseView.java index 950ea2b..9bd82a7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseView.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseView.java @@ -143,7 +143,10 @@ public class BaseView implements IViewRule { IMenuCallback onChange = new IMenuCallback() { - public void action(final MenuAction action, final String valueId, final Boolean newValue) { + public void action( + final MenuAction action, + final String valueId, + final Boolean newValue) { String fullActionId = action.getId(); boolean isProp = fullActionId.startsWith("@prop@"); final String actionId = isProp ? fullActionId.substring(6) : fullActionId; @@ -460,7 +463,11 @@ public class BaseView implements IViewRule { // ignore } - public void onDropped(INode targetNode, IDragElement[] elements, DropFeedback feedback, Point p) { + public void onDropped( + INode targetNode, + IDragElement[] elements, + DropFeedback feedback, + Point p) { // ignore } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java index f3efb64..16b477b 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/FrameLayoutRule.java @@ -51,7 +51,11 @@ public class FrameLayoutRule extends BaseLayout { }); } - void drawFeedback(IGraphics gc, INode targetNode, IDragElement[] elements, DropFeedback feedback) { + void drawFeedback( + IGraphics gc, + INode targetNode, + IDragElement[] elements, + DropFeedback feedback) { Rect b = targetNode.getBounds(); if (!b.isValid()) { return; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java index 7a960b1..3e5bdcf 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java @@ -1152,7 +1152,6 @@ class LayoutCanvas extends Canvas implements ISelectionProvider { int x = mHScale.inverseTranslate(e.x); int y = mVScale.inverseTranslate(e.y); - // test, remove me if (e.button == 3) { // Right click button is used to display a context menu. // If there's an existing selection and the click is anywhere in this selection diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java new file mode 100644 index 0000000..2f4e22f --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java @@ -0,0 +1,73 @@ +/* + * 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 com.android.ide.common.api.INode; +import com.android.ide.common.api.Point; +import com.android.ide.common.api.Rect; + +/** Test the {@link AbsoluteLayoutRule} */ +public class AbsoluteLayoutRuleTest extends AbstractLayoutRuleTest { + // Utility for other tests + protected INode dragInto(Rect dragBounds, Point dragPoint, int insertIndex, int currentIndex, + String... graphicsFragments) { + INode layout = TestNode.create("android.widget.AbsoluteLayout").id("@+id/AbsoluteLayout01") + .bounds(new Rect(0, 0, 240, 480)).add( + TestNode.create("android.widget.Button").id("@+id/Button01").bounds( + new Rect(0, 0, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button02").bounds( + new Rect(0, 100, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button03").bounds( + new Rect(0, 200, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button04").bounds( + new Rect(0, 300, 100, 80))); + + return super.dragInto(new AbsoluteLayoutRule(), layout, dragBounds, dragPoint, null, + insertIndex, currentIndex, graphicsFragments); + } + + public void testDragMiddle() { + INode inserted = dragInto( + // Bounds of the dragged item + new Rect(0, 0, 105, 80), + // Drag point + new Point(30, -10), + // Expected insert location: We just append in absolute layout + 4, + // Not dragging one of the existing children + -1, + // Bounds rectangle + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + + // Drop preview + "useStyle(DROP_PREVIEW), drawRect(Rect[30,-10,105,80])"); + + assertEquals("30dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_x")); + assertEquals("-10dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_y")); + + // Without drag bounds we should just draw guide lines instead + inserted = dragInto(new Rect(0, 0, 0, 0), new Point(30, -10), 4, -1, + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + // Guideline + "useStyle(GUIDELINE), drawLine(30,0,30,480), drawLine(0,-10,240,-10)", + // Drop preview + "useStyle(DROP_PREVIEW), drawLine(30,-10,240,-10), drawLine(30,-10,30,480)"); + assertEquals("30dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_x")); + assertEquals("-10dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_y")); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbstractLayoutRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbstractLayoutRuleTest.java new file mode 100644 index 0000000..96cdbb6 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbstractLayoutRuleTest.java @@ -0,0 +1,180 @@ +/* + * 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 com.android.ide.common.api.DropFeedback; +import com.android.ide.common.api.IDragElement; +import com.android.ide.common.api.INode; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.Point; +import com.android.ide.common.api.Rect; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +/** + * Common layout helpers from LayoutRule tests + */ +public abstract class AbstractLayoutRuleTest extends TestCase { + public static String ANDROID_URI = BaseLayout.ANDROID_URI; + public static String ATTR_ID = BaseLayout.ATTR_ID; + + /** + * 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 graphicshicsFragments 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, 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) + */ + 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); + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseLayoutTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseLayoutTest.java new file mode 100644 index 0000000..dad1420 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseLayoutTest.java @@ -0,0 +1,240 @@ +/* + * 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 com.android.ide.common.api.IDragElement; +import com.android.ide.common.api.INode; +import com.android.ide.common.api.Rect; +import com.android.ide.common.layout.BaseLayout.AttributeFilter; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +// TODO: Check assertions +// TODO: Check equals() but not == strings by using new String("") to prevent interning +// TODO: Rename BaseLayout to BaseLayoutRule, and tests too of course + +public class BaseLayoutTest extends AbstractLayoutRuleTest { + + /** Provides test data used by other test cases */ + private IDragElement[] createSampleElements() { + IDragElement[] elements = TestDragElement.create(TestDragElement.create( + "android.widget.Button", new Rect(0, 0, 100, 80)).id("@+id/Button01"), + TestDragElement.create("android.widget.LinearLayout", new Rect(0, 80, 100, 280)) + .id("@+id/LinearLayout01").add( + TestDragElement.create("android.widget.Button", + new Rect(0, 80, 100, 80)).id("@+id/Button011"), + TestDragElement.create("android.widget.Button", + new Rect(0, 180, 100, 80)).id("@+id/Button012")), + TestDragElement.create("android.widget.Button", new Rect(100, 0, 100, 80)).id( + "@+id/Button02")); + return elements; + } + + /** Test {@link BaseLayout#collectIds}: Check that basic lookup of id works */ + public final void testCollectIds1() { + IDragElement[] elements = TestDragElement.create(TestDragElement.create( + "android.widget.Button", new Rect(0, 0, 100, 80)).id("@+id/Button01")); + Map<String, Pair<String, String>> idMap = new HashMap<String, Pair<String, String>>(); + Map<String, Pair<String, String>> ids = new BaseLayout().collectIds(idMap, elements); + assertEquals(1, ids.size()); + assertEquals("@+id/Button01", ids.keySet().iterator().next()); + } + + /** + * Test {@link BaseLayout#collectIds}: Check that with the wrong URI we + * don't pick up the ID + */ + public final void testCollectIds2() { + IDragElement[] elements = TestDragElement.create(TestDragElement.create( + "android.widget.Button", new Rect(0, 0, 100, 80)).set("myuri", BaseView.ATTR_ID, + "@+id/Button01")); + + Map<String, Pair<String, String>> idMap = new HashMap<String, Pair<String, String>>(); + Map<String, Pair<String, String>> ids = new BaseLayout().collectIds(idMap, elements); + assertEquals(0, ids.size()); + } + + /** + * Test {@link BaseLayout#normalizeId(String)} + */ + public final void testNormalizeId() { + assertEquals("foo", new BaseLayout().normalizeId("foo")); + assertEquals("@+id/name", new BaseLayout().normalizeId("@id/name")); + assertEquals("@+id/name", new BaseLayout().normalizeId("@+id/name")); + } + + /** + * Test {@link BaseLayout#collectExistingIds} + */ + public final void testCollectExistingIds1() { + Set<String> existing = new HashSet<String>(); + INode node = TestNode.create("android.widget.Button").id("@+id/Button012").add( + TestNode.create("android.widget.Button").id("@+id/Button2")); + + new BaseLayout().collectExistingIds(node, existing); + + assertEquals(2, existing.size()); + assertContainsSame(Arrays.asList("@+id/Button2", "@+id/Button012"), existing); + } + + /** + * Test {@link BaseLayout#collectIds}: Check that with multiple elements and + * some children we still pick up all the right id's + */ + public final void testCollectIds3() { + Map<String, Pair<String, String>> idMap = new HashMap<String, Pair<String, String>>(); + + IDragElement[] elements = createSampleElements(); + Map<String, Pair<String, String>> ids = new BaseLayout().collectIds(idMap, elements); + assertEquals(5, ids.size()); + assertContainsSame(Arrays.asList("@+id/Button01", "@+id/Button02", "@+id/Button011", + "@+id/Button012", "@+id/LinearLayout01"), ids.keySet()); + + // Make sure the Pair has the right stuff too; + // (having the id again in the pair seems redundant; see if I really + // need it in the implementation) + assertEquals(Pair.of("@+id/LinearLayout01", "android.widget.LinearLayout"), ids + .get("@+id/LinearLayout01")); + } + + /** + * Test {@link BaseLayout#remapIds}: Ensure that it identifies a conflict + */ + public final void testRemapIds1() { + Map<String, Pair<String, String>> idMap = new HashMap<String, Pair<String, String>>(); + BaseLayout baseLayout = new BaseLayout(); + IDragElement[] elements = createSampleElements(); + baseLayout.collectIds(idMap, elements); + INode node = TestNode.create("android.widget.Button").id("@+id/Button012").add( + TestNode.create("android.widget.Button").id("@+id/Button2")); + + assertEquals(5, idMap.size()); + Map<String, Pair<String, String>> remapped = baseLayout.remapIds(node, idMap); + // 4 original from the sample elements, plus overlap with one + // (Button012) - one new + // button added in + assertEquals(6, remapped.size()); + + // TODO: I'm a little confused about what exactly this method should do; + // check with Raphael. + } + + + /** + * Test {@link BaseLayout#getDropIdMap} + */ + public final void testGetDropIdMap() { + BaseLayout baseLayout = new BaseLayout(); + IDragElement[] elements = createSampleElements(); + INode node = TestNode.create("android.widget.Button").id("@+id/Button012").add( + TestNode.create("android.widget.Button").id("@+id/Button2")); + + Map<String, Pair<String, String>> idMap = baseLayout.getDropIdMap(node, elements, true); + assertContainsSame(Arrays.asList("@+id/Button01", "@+id/Button012", "@+id/Button011", + "@id/Button012", "@+id/Button02", "@+id/LinearLayout01"), idMap + .keySet()); + + // TODO: I'm a little confused about what exactly this method should do; + // check with Raphael. + } + + public final void testAddAttributes1() { + BaseLayout layout = new BaseLayout(); + + // First try with no filter + IDragElement oldElement = TestDragElement.create("a.w.B").id("@+id/foo"); + INode newNode = TestNode.create("a.w.B").id("@+id/foo").set("u", "key", "value").set("u", + "nothidden", "nothiddenvalue"); + ; + AttributeFilter filter = null; + // No references in this test case + Map<String, Pair<String, String>> idMap = null; + + layout.addAttributes(newNode, oldElement, idMap, filter); + assertEquals("value", newNode.getStringAttr("u", "key")); + assertEquals("nothiddenvalue", newNode.getStringAttr("u", "nothidden")); + } + + public final void testAddAttributes2() { + // Test filtering + BaseLayout layout = new BaseLayout(); + + // First try with no filter + IDragElement oldElement = TestDragElement.create("a.w.B").id("@+id/foo"); + INode newNode = TestNode.create("a.w.B").id("@+id/foo").set("u", "key", "value").set("u", + "hidden", "hiddenvalue"); + AttributeFilter filter = new AttributeFilter() { + + public String replace(String attributeUri, String attributeName, + String attributeValue) { + if (attributeName.equals("hidden")) { + return null; + } + + return attributeValue; + } + }; + // No references in this test case + Map<String, Pair<String, String>> idMap = null; + + layout.addAttributes(newNode, oldElement, idMap, filter); + assertEquals("value", newNode.getStringAttr("u", "key")); + } + + public final void testFindNewId() { + BaseLayout baseLayout = new BaseLayout(); + Set<String> existing = new HashSet<String>(); + assertEquals("@+id/Widget01", baseLayout.findNewId("a.w.Widget", existing)); + + existing.add("@+id/Widget01"); + assertEquals("@+id/Widget02", baseLayout.findNewId("a.w.Widget", existing)); + + existing.add("@+id/Widget02"); + assertEquals("@+id/Widget03", baseLayout.findNewId("a.w.Widget", existing)); + + existing.remove("@+id/Widget02"); + assertEquals("@+id/Widget02", baseLayout.findNewId("a.w.Widget", existing)); + } + + public final void testDefaultAttributeFilter() { + assertEquals("true", BaseLayout.DEFAULT_ATTR_FILTER.replace("myuri", "layout_alignRight", + "true")); + assertEquals(null, BaseLayout.DEFAULT_ATTR_FILTER.replace(BaseLayout.ANDROID_URI, + "layout_alignRight", "true")); + assertEquals("true", BaseLayout.DEFAULT_ATTR_FILTER.replace(BaseLayout.ANDROID_URI, + "myproperty", "true")); + } + + public final void testAddInnerElements() { + IDragElement oldElement = TestDragElement.create("root").add( + TestDragElement.create("a.w.B").id("@+id/child1") + .set("uri", "childprop1", "value1"), + TestDragElement.create("a.w.B").id("@+id/child2").set("uri", "childprop2a", + "value2a").set("uri", "childprop2b", "value2b")); + INode newNode = TestNode.create("a.w.B").id("@+id/foo"); + Map<String, Pair<String, String>> idMap = new HashMap<String, Pair<String, String>>(); + BaseLayout layout = new BaseLayout(); + layout.addInnerElements(newNode, oldElement, idMap); + assertEquals(2, newNode.getChildren().length); + + assertEquals("value2b", newNode.getChildren()[1].getStringAttr("uri", "childprop2b")); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseViewTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseViewTest.java new file mode 100644 index 0000000..3634569 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseViewTest.java @@ -0,0 +1,42 @@ +/* + * 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 java.util.Arrays; +import java.util.Collections; + +import junit.framework.TestCase; + +public class BaseViewTest extends TestCase { + public final void testPrettyName() { + assertEquals(null, BaseView.prettyName(null)); + assertEquals("", BaseView.prettyName("")); + assertEquals("Foo", BaseView.prettyName("foo")); + assertEquals("Foo bar", BaseView.prettyName("foo_bar")); + // TODO: We should check this to capitalize each initial word + // assertEquals("Foo Bar", BaseView.prettyName("foo_bar")); + // TODO: We should also handle camelcase properties + // assertEquals("Foo Bar", BaseView.prettyName("fooBar")); + } + + public final void testJoin() { + assertEquals("foo", BaseView.join('|', Arrays.asList("foo"))); + assertEquals("", BaseView.join('|', Collections.<String>emptyList())); + assertEquals("foo,bar", BaseView.join(',', Arrays.asList("foo", "bar"))); + assertEquals("foo|bar", BaseView.join('|', Arrays.asList("foo", "bar"))); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/FrameLayoutRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/FrameLayoutRuleTest.java new file mode 100644 index 0000000..2adf4f0 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/FrameLayoutRuleTest.java @@ -0,0 +1,63 @@ +/* + * 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 com.android.ide.common.api.INode; +import com.android.ide.common.api.Point; +import com.android.ide.common.api.Rect; + +/** Test the {@link FrameLayoutRule} */ +public class FrameLayoutRuleTest extends AbstractLayoutRuleTest { + // Utility for other tests + protected void dragInto(Rect dragBounds, Point dragPoint, int insertIndex, int currentIndex, + String... graphicsFragments) { + INode layout = TestNode.create("android.widget.FrameLayout").id("@+id/FrameLayout01") + .bounds(new Rect(0, 0, 240, 480)).add( + TestNode.create("android.widget.Button").id("@+id/Button01").bounds( + new Rect(0, 0, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button02").bounds( + new Rect(0, 100, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button03").bounds( + new Rect(0, 200, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button04").bounds( + new Rect(0, 300, 100, 80))); + + super.dragInto(new FrameLayoutRule(), layout, dragBounds, dragPoint, null, + insertIndex, currentIndex, graphicsFragments); + } + + public void testDragMiddle() { + dragInto( + // Bounds of the dragged item + new Rect(0, 0, 105, 80), + // Drag point + new Point(30, -10), + // Expected insert location: We just append in absolute layout + 4, + // Not dragging one of the existing children + -1, + // Bounds rectangle + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + + // Drop Preview + "useStyle(DROP_PREVIEW), drawRect(Rect[0,0,105,80])]"); + // Without drag bounds we should just draw guide lines instead + dragInto(new Rect(0, 0, 0, 0), new Point(30, -10), 4, -1, + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + "useStyle(DROP_PREVIEW), drawLine(1,0,1,480), drawLine(0,1,240,1)"); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LinearLayoutRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LinearLayoutRuleTest.java new file mode 100644 index 0000000..42dbb3c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LinearLayoutRuleTest.java @@ -0,0 +1,308 @@ +/* + * 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 com.android.ide.common.api.DropFeedback; +import com.android.ide.common.api.IDragElement; +import com.android.ide.common.api.IMenuCallback; +import com.android.ide.common.api.INode; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.MenuAction; +import com.android.ide.common.api.Point; +import com.android.ide.common.api.Rect; +import com.android.ide.common.api.MenuAction.Choices; + +import java.util.List; + +/** Test the {@link LinearLayoutRule} */ +public class LinearLayoutRuleTest extends AbstractLayoutRuleTest { + // Utility for other tests + protected void dragIntoEmpty(Rect dragBounds) { + boolean haveBounds = dragBounds.isValid(); + + IViewRule rule = new LinearLayoutRule(); + INode targetNode = TestNode.create("android.widget.LinearLayout").id( + "@+id/LinearLayout01").bounds(new Rect(0, 0, 240, 480)); + Point dropPoint = new Point(10, 5); + + IDragElement[] elements = TestDragElement.create(TestDragElement.create( + "android.widget.Button", dragBounds).id("@+id/Button01")); + + // Enter target + DropFeedback feedback = rule.onDropEnter(targetNode, elements); + assertNotNull(feedback); + assertFalse(feedback.invalidTarget); + assertNotNull(feedback.painter); + + feedback = rule.onDropMove(targetNode, elements, feedback, dropPoint); + assertNotNull(feedback); + 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); + assertEquals( + // Expect to see a recipient rectangle around the bounds of the + // LinearLayout, + // as well as a single vertical line as a drop preview located + // along the left + // edge (for this horizontal linear layout) showing insert + // position at index 0, + // and finally a rectangle for the bounds of the inserted button + // centered over + // the middle + "[useStyle(DROP_RECIPIENT), " + + + // Bounds rectangle + "drawRect(Rect[0,0,240,480]), " + "useStyle(DROP_ZONE), " + + "useStyle(DROP_ZONE_ACTIVE), " + "useStyle(DROP_PREVIEW), " + + // Insert position line + "drawLine(1,0,1,480)" + (haveBounds ? + // Outline of dragged node centered over position line + ", useStyle(DROP_PREVIEW), " + "drawRect(Rect[-49,0,100,80])" + // Nothing when we don't have bounds + : "") + "]", graphics.getDrawn().toString()); + + // Attempt a drop + assertEquals(0, targetNode.getChildren().length); + rule.onDropped(targetNode, elements, feedback, dropPoint); + assertEquals(1, targetNode.getChildren().length); + assertEquals("@+id/Button01", targetNode.getChildren()[0].getStringAttr( + BaseLayout.ANDROID_URI, BaseLayout.ATTR_ID)); + } + + // Utility for other tests + protected INode dragInto(boolean vertical, Rect dragBounds, Point dragPoint, + int insertIndex, int currentIndex, + String... graphicsFragments) { + INode linearLayout = TestNode.create("android.widget.LinearLayout").id( + "@+id/LinearLayout01").bounds(new Rect(0, 0, 240, 480)).set(BaseLayout.ANDROID_URI, + LinearLayoutRule.ATTR_ORIENTATION, + vertical ? LinearLayoutRule.VALUE_VERTICAL : LinearLayoutRule.VALUE_HORIZONTAL) + .add( + TestNode.create("android.widget.Button").id("@+id/Button01").bounds( + new Rect(0, 0, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button02").bounds( + new Rect(0, 100, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button03").bounds( + new Rect(0, 200, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button04").bounds( + new Rect(0, 300, 100, 80))); + + return super.dragInto(new LinearLayoutRule(), linearLayout, dragBounds, dragPoint, null, + insertIndex, currentIndex, graphicsFragments); + } + + // Check that the context menu registers the expected menu items + public void testContextMenu() { + LinearLayoutRule rule = new LinearLayoutRule(); + INode node = TestNode.create("android.widget.Button").id("@+id/Button012"); + + List<MenuAction> contextMenu = rule.getContextMenu(node); + assertEquals(4, contextMenu.size()); + assertEquals("Layout Width", contextMenu.get(0).getTitle()); + assertEquals("Layout Height", contextMenu.get(1).getTitle()); + assertEquals("Properties", contextMenu.get(2).getTitle()); + assertEquals("Orientation", contextMenu.get(3).getTitle()); + + MenuAction propertiesMenu = contextMenu.get(2); + assertTrue(propertiesMenu.getClass().getName(), propertiesMenu instanceof MenuAction.Group); + // TODO: Test Properties-list + } + + // Check that the context menu manipulates the orientation attribute + public void testOrientation() { + LinearLayoutRule rule = new LinearLayoutRule(); + INode node = TestNode.create("android.widget.Button").id("@+id/Button012"); + + assertNull(node.getStringAttr(BaseLayout.ANDROID_URI, LinearLayoutRule.ATTR_ORIENTATION)); + + List<MenuAction> contextMenu = rule.getContextMenu(node); + assertEquals(4, contextMenu.size()); + MenuAction orientationAction = contextMenu.get(3); + + assertTrue(orientationAction.getClass().getName(), + orientationAction instanceof MenuAction.Choices); + + MenuAction.Choices choices = (Choices) orientationAction; + IMenuCallback callback = choices.getCallback(); + callback.action(orientationAction, LinearLayoutRule.VALUE_VERTICAL, true); + + String orientation = node.getStringAttr(BaseLayout.ANDROID_URI, + LinearLayoutRule.ATTR_ORIENTATION); + assertEquals(LinearLayoutRule.VALUE_VERTICAL, orientation); + callback.action(orientationAction, LinearLayoutRule.VALUE_HORIZONTAL, true); + orientation = node.getStringAttr(BaseLayout.ANDROID_URI, LinearLayoutRule.ATTR_ORIENTATION); + assertEquals(LinearLayoutRule.VALUE_HORIZONTAL, orientation); + } + + public void testDragInEmptyWithBounds() { + dragIntoEmpty(new Rect(0, 0, 100, 80)); + } + + public void testDragInEmptyWithoutBounds() { + dragIntoEmpty(new Rect(0, 0, 0, 0)); + } + + public void testDragInVerticalTop() { + dragInto(true, + // Bounds of the dragged item + new Rect(0, 0, 105, 80), + // Drag point + new Point(30, -10), + // Expected insert location + 0, + // Not dragging one of the existing children + -1, + // Bounds rectangle + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + + // Drop zones + "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), " + + "drawLine(0,190,240,190), drawLine(0,290,240,290)", + + // Active nearest line + "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,0,240,0)", + + // Preview of the dropped rectangle + "useStyle(DROP_PREVIEW), drawRect(Rect[0,-40,105,80])"); + + // Without drag bounds it should be identical except no preview + // rectangle + dragInto(true, + new Rect(0, 0, 0, 0), // Invalid + new Point(30, -10), 0, -1, + "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,0,240,0)"); + } + + public void testDragInVerticalBottom() { + dragInto(true, + // Bounds of the dragged item + new Rect(0, 0, 105, 80), + // Drag point + new Point(30, 500), + // Expected insert location + 4, + // Not dragging one of the existing children + -1, + // Bounds rectangle + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + + // Drop zones + "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), " + + "drawLine(0,190,240,190), drawLine(0,290,240,290)", + + // Active nearest line + "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,381,240,381)", + + // Preview of the dropped rectangle + "useStyle(DROP_PREVIEW), drawRect(Rect[0,341,105,80])"); + + // Check without bounds too + dragInto(true, new Rect(0, 0, 105, 80), new Point(30, 500), 4, -1, + "useStyle(DROP_PREVIEW), drawRect(Rect[0,341,105,80])"); + } + + public void testDragInVerticalMiddle() { + dragInto(true, + // Bounds of the dragged item + new Rect(0, 0, 105, 80), + // Drag point + new Point(0, 170), + // Expected insert location + 2, + // Not dragging one of the existing children + -1, + // Bounds rectangle + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + + // Drop zones + "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), " + + "drawLine(0,190,240,190), drawLine(0,290,240,290)", + + // Active nearest line + "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,190,240,190)", + + // Preview of the dropped rectangle + "useStyle(DROP_PREVIEW), drawRect(Rect[0,150,105,80])"); + + // Check without bounds too + dragInto(true, new Rect(0, 0, 105, 80), new Point(0, 170), 2, -1, + "useStyle(DROP_PREVIEW), drawRect(Rect[0,150,105,80])"); + } + + public void testDragInVerticalMiddleSelfPos() { + // Drag the 2nd button, down to the position between 3rd and 4th + dragInto(true, + // Bounds of the dragged item + new Rect(0, 100, 100, 80), + // Drag point + new Point(0, 250), + // Expected insert location + 2, + // Dragging 1st item + 1, + // Bounds rectangle + + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + + // Drop zones - these are different because we exclude drop + // zones around the + // dragged item itself (it doesn't make sense to insert directly + // before or after + // myself + "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), " + + "drawLine(0,381,240,381)", + + // Preview line along insert axis + "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,290,240,290)", + + // Preview of dropped rectangle + "useStyle(DROP_PREVIEW), drawRect(Rect[0,250,100,80])"); + + // Test dropping on self (no position change): + dragInto(true, + // Bounds of the dragged item + new Rect(0, 100, 100, 80), + // Drag point + new Point(0, 210), + // Expected insert location + 1, + // Dragging from same pos + 1, + // Bounds rectangle + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + + // Drop zones - these are different because we exclude drop + // zones around the + // dragged item itself (it doesn't make sense to insert directly + // before or after + // myself + "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), " + + "drawLine(0,381,240,381)", + + // No active nearest line when you're over the self pos! + + // Preview of the dropped rectangle + "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawRect(Rect[0,100,100,80])"); + } + + // Left to test: + // Check inserting at last pos with multiple children + // Check inserting with no bounds rectangle for dragged element +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/RelativeLayoutRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/RelativeLayoutRuleTest.java new file mode 100644 index 0000000..cc47df1 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/RelativeLayoutRuleTest.java @@ -0,0 +1,165 @@ +/* + * 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 com.android.ide.common.api.INode; +import com.android.ide.common.api.Point; +import com.android.ide.common.api.Rect; + +import java.util.ArrayList; +import java.util.List; + +/** Test the {@link RelativeLayoutRule} */ +public class RelativeLayoutRuleTest extends AbstractLayoutRuleTest { + // Utility for other tests + protected INode dragInto(Rect dragBounds, Point dragPoint, Point secondDragPoint, + int insertIndex, int currentIndex, String... graphicsFragments) { + INode layout = TestNode.create("android.widget.RelativeLayout").id("@+id/RelativeLayout01") + .bounds(new Rect(0, 0, 240, 480)).add( + // Add centered button as the anchor + TestNode.create("android.widget.Button").id("@+id/Centered").bounds( + new Rect(70, 200, 100, 80)).set(ANDROID_URI, + "layout_centerInParent", "true"), + // Add a second button anchored to it + TestNode.create("android.widget.Button").id("@+id/Below").bounds( + new Rect(70, 280, 100, 80)).set(ANDROID_URI, "layout_below", + "@+id/Centered").set(ANDROID_URI, "layout_alignLeft", + "@+id/Centered")); + + return super.dragInto(new RelativeLayoutRule(), layout, dragBounds, dragPoint, + secondDragPoint, insertIndex, currentIndex, graphicsFragments); + } + + protected INode dragInto(Rect dragBounds, Point dragPoint, Point secondDragPoint, + int insertIndex, int currentIndex, String[] extraFragments, + String... graphicsFragments) { + + // When we switch to JDK6, use Arrays#copyOf instead + String[] combined = new String[extraFragments.length + graphicsFragments.length]; + System.arraycopy(graphicsFragments, 0, combined, 0, graphicsFragments.length); + System.arraycopy(extraFragments, 0, combined, graphicsFragments.length, + extraFragments.length); + + return dragInto(dragBounds, dragPoint, secondDragPoint, insertIndex, + currentIndex, combined); + } + + public void testDropTopEdge() { + // If we drag right into the button itself, not a valid drop position + INode inserted = dragInto( + new Rect(0, 0, 105, 80), new Point(30, -10), null, 2, -1, + // Bounds rectangle + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + + // Preview line + drop zone rectangle along the top + "useStyle(DROP_ZONE), drawRect(Rect[0,-10,240,20])", + "useStyle(DROP_ZONE_ACTIVE), fillRect(Rect[0,-10,240,20])", + "useStyle(DROP_PREVIEW), drawLine(0,0,240,0)", + + // Tip + "useStyle(HELP), drawBoxedStrings(5,15,[alignParentTop])", + + // Drop preview + "useStyle(DROP_PREVIEW), drawRect(Rect[0,0,105,80])"); + + assertEquals("true", inserted.getStringAttr(BaseLayout.ANDROID_URI, + "layout_alignParentTop")); + } + + public void testDropZones() { + List<Pair<Point,String[]>> zones = new ArrayList<Pair<Point,String[]>>(); + + zones.add(Pair.of(new Point(51+10, 181+10), + new String[] {"above=@+id/Centered", "toLeftOf=@+id/Centered"})); + zones.add(Pair.of(new Point(71+10, 181+10), + new String[] {"above=@+id/Centered", "alignLeft=@+id/Centered"})); + zones.add(Pair.of(new Point(104+10, 181+10), + new String[] {"above=@+id/Centered", "alignRight=@+id/Centered"})); + zones.add(Pair.of(new Point(137+10, 181+10), + new String[] {"above=@+id/Centered", "alignRight=@+id/Centered"})); + zones.add(Pair.of(new Point(170+10, 181+10), + new String[] {"above=@+id/Centered", "toRightOf=@+id/Centered"})); + zones.add(Pair.of(new Point(51+10, 279+10), + new String[] {"below=@+id/Centered", "toLeftOf=@+id/Centered"})); + zones.add(Pair.of(new Point(71+10, 279+10), + new String[] {"below=@+id/Centered", "alignLeft=@+id/Centered"})); + zones.add(Pair.of(new Point(104+10, 279+10), + new String[] {"below=@+id/Centered", "alignLeft=@+id/Centered"})); + zones.add(Pair.of(new Point(137+10, 279+10), + new String[] {"below=@+id/Centered", "alignRight=@+id/Centered"})); + zones.add(Pair.of(new Point(170+10, 279+10), + new String[] {"below=@+id/Centered", "toRightOf=@+id/Centered"})); + zones.add(Pair.of(new Point(51+10, 201+10), + new String[] {"toLeftOf=@+id/Centered", "alignTop=@+id/Centered"})); + zones.add(Pair.of(new Point(51+10, 227+10), + new String[] {"toLeftOf=@+id/Centered", "alignTop=@+id/Centered"})); + zones.add(Pair.of(new Point(170+10, 201+10), + new String[] {"toRightOf=@+id/Centered", "alignTop=@+id/Centered"})); + zones.add(Pair.of(new Point(51+10, 253+10), + new String[] {"toLeftOf=@+id/Centered", "alignBottom=@+id/Centered"})); + zones.add(Pair.of(new Point(170+10, 227+10), + new String[] {"toRightOf=@+id/Centered", "alignTop=@+id/Centered", + "alignBottom=@+id/Centered"})); + zones.add(Pair.of(new Point(170+10, 253+10), + new String[] {"toRightOf=@+id/Centered", "alignBottom=@+id/Centered"})); + + for (Pair<Point,String[]> zonePair : zones) { + Point dropPoint = zonePair.getFirst(); + String[] attachments = zonePair.getSecond(); + // If we drag right into the button itself, not a valid drop position + + INode inserted = dragInto( + new Rect(0, 0, 105, 80), new Point(120, 240), dropPoint, 1, -1, + attachments, + + // Bounds rectangle + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + + // Drop zones + "useStyle(DROP_ZONE), " + + "drawRect(Rect[51,181,20,20]), drawRect(Rect[71,181,33,20]), " + + "drawRect(Rect[104,181,33,20]), drawRect(Rect[137,181,33,20]), " + + "drawRect(Rect[170,181,20,20]), drawRect(Rect[51,279,20,20]), " + + "drawRect(Rect[71,279,33,20]), drawRect(Rect[104,279,33,20]), " + + "drawRect(Rect[137,279,33,20]), drawRect(Rect[170,279,20,20]), " + + "drawRect(Rect[51,201,20,26]), drawRect(Rect[51,227,20,26]), " + + "drawRect(Rect[51,253,20,26]), drawRect(Rect[170,201,20,26]), " + + "drawRect(Rect[170,227,20,26]), drawRect(Rect[170,253,20,26])"); + + for (String attachment : attachments) { + String[] elements = attachment.split("="); + String name = "layout_" + elements[0]; + String value = elements[1]; + assertEquals(value, inserted.getStringAttr(BaseLayout.ANDROID_URI, name)); + } + } + } + + + public void testDragInvalid() { + // If we drag right into the button itself, not a valid drop position + dragInto(new Rect(70, 200, 100, 80), new Point(120, 240), new Point(120, 240), -1, 0, + // Bounds rectangle + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + + // Invalid marker + "useStyle(INVALID), fillRect(Rect[70,200,100,80]), drawLine(70,200,170,280), " + + "drawLine(70,280,170,200)"); + } + + // TODO: Test error (dragging on ancestor) +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttribute.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttribute.java new file mode 100644 index 0000000..fc59ba8 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttribute.java @@ -0,0 +1,54 @@ +/* + * 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 com.android.ide.common.api.IDragElement.IDragAttribute; +import com.android.ide.common.api.INode.IAttribute; + +/** Test/mock implementation of {@link IAttribute} and {@link IDragAttribute} */ +public class TestAttribute implements IAttribute, IDragAttribute { + private String mUri; + + private String mName; + + private String mValue; + + public TestAttribute(String mUri, String mName, String mValue) { + super(); + this.mName = mName; + this.mUri = mUri; + this.mValue = mValue; + } + + public String getName() { + return mName; + } + + public String getUri() { + return mUri; + } + + public String getValue() { + return mValue; + } + + @Override + public String toString() { + return "TestAttribute [name=" + mName + ", uri=" + mUri + ", value=" + mValue + "]"; + } + + +}
\ No newline at end of file diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttributeInfo.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttributeInfo.java new file mode 100644 index 0000000..a864764 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttributeInfo.java @@ -0,0 +1,56 @@ +/* + * 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 com.android.ide.common.api.IAttributeInfo; + +/** Test/mock implementation of {@link IAttributeInfo} */ +public class TestAttributeInfo implements IAttributeInfo { + private final String mName; + + public TestAttributeInfo(String name) { + this.mName = name; + } + + public String getDeprecatedDoc() { + BaseLayoutTest.fail("Not supported yet in tests"); + return null; + } + + public String[] getEnumValues() { + BaseLayoutTest.fail("Not supported yet in tests"); + return null; + } + + public String[] getFlagValues() { + BaseLayoutTest.fail("Not supported yet in tests"); + return null; + } + + public Format[] getFormats() { + BaseLayoutTest.fail("Not supported yet in tests"); + return null; + } + + public String getJavaDoc() { + BaseLayoutTest.fail("Not supported yet in tests"); + return null; + } + + public String getName() { + return mName; + } +}
\ No newline at end of file diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestColor.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestColor.java new file mode 100644 index 0000000..449ad5e --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestColor.java @@ -0,0 +1,36 @@ +/* + * 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 com.android.ide.common.api.IColor; + +public class TestColor implements IColor { + private int mRgb; + + public TestColor(int rgb) { + this.mRgb = rgb; + } + + public int getRgb() { + return mRgb; + } + + @Override + public String toString() { + return String.format("#%6x", mRgb); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestDragElement.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestDragElement.java new file mode 100644 index 0000000..b113ced --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestDragElement.java @@ -0,0 +1,142 @@ +/* + * 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 com.android.ide.common.api.IDragElement; +import com.android.ide.common.api.Rect; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** Test/mock implementation of {@link IDragElement} */ +public class TestDragElement implements IDragElement { + private Rect mRect; + + private final String mFqcn; + + private Map<String, TestAttribute> mAttributes = new HashMap<String, TestAttribute>(); + + private List<TestDragElement> mChildren = new ArrayList<TestDragElement>(); + + private TestDragElement mParent; + + public TestDragElement(String mFqcn, Rect mRect, List<TestDragElement> mChildren, + TestDragElement mParent) { + super(); + this.mRect = mRect; + this.mFqcn = mFqcn; + this.mChildren = mChildren; + this.mParent = mParent; + } + + public TestDragElement(String fqn) { + this(fqn, null, null, null); + } + + public TestDragElement setBounds(Rect bounds) { + this.mRect = bounds; + + return this; + } + + // Builder stuff + public TestDragElement set(String uri, String name, String value) { + if (mAttributes == null) { + mAttributes = new HashMap<String, TestAttribute>(); + } + + mAttributes.put(uri + name, new TestAttribute(uri, name, value)); + + return this; + } + + public TestDragElement add(TestDragElement... children) { + if (mChildren == null) { + mChildren = new ArrayList<TestDragElement>(); + } + + for (TestDragElement child : children) { + mChildren.add(child); + child.mParent = this; + } + + return this; + } + + public TestDragElement id(String id) { + return set(BaseView.ANDROID_URI, BaseView.ATTR_ID, id); + } + + public static TestDragElement create(String fqn, Rect bounds) { + return create(fqn).setBounds(bounds); + } + + public static TestDragElement create(String fqn) { + return new TestDragElement(fqn); + } + + public static IDragElement[] create(TestDragElement... elements) { + return elements; + } + + // ==== IDragElement ==== + + public IDragAttribute getAttribute(String uri, String localName) { + if (mAttributes == null) { + return new TestAttribute(uri, localName, ""); + } + + return mAttributes.get(uri + localName); + } + + public IDragAttribute[] getAttributes() { + return mAttributes.values().toArray(new IDragAttribute[mAttributes.size()]); + } + + public Rect getBounds() { + return mRect; + } + + public String getFqcn() { + return mFqcn; + } + + public IDragElement[] getInnerElements() { + if (mChildren == null) { + return new IDragElement[0]; + } + + return mChildren.toArray(new IDragElement[mChildren.size()]); + } + + public Rect getParentBounds() { + return mParent != null ? mParent.getBounds() : null; + } + + public String getParentFqcn() { + return mParent != null ? mParent.getFqcn() : null; + } + + @Override + public String toString() { + return "TestDragElement [fqn=" + mFqcn + ", attributes=" + mAttributes + ", bounds=" + + mRect + "]"; + } + + +}
\ No newline at end of file diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestGraphics.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestGraphics.java new file mode 100644 index 0000000..b82f309 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestGraphics.java @@ -0,0 +1,148 @@ +/* + * 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 com.android.ide.common.api.DrawingStyle; +import com.android.ide.common.api.IColor; +import com.android.ide.common.api.IGraphics; +import com.android.ide.common.api.Point; +import com.android.ide.common.api.Rect; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +// TODO: Create box of ascii art + +public class TestGraphics implements IGraphics { + /** List of things we have drawn */ + private List<String> mDrawn = new ArrayList<String>(); + + private IColor mBackground = new TestColor(0x000000); + + private IColor mForeground = new TestColor(0xFFFFFF); + + private int mAlpha = 128; + + /** Return log of graphics calls */ + public List<String> getDrawn() { + return Collections.unmodifiableList(mDrawn); + } + + /** Wipe out log of graphics calls */ + public void clear() { + mDrawn.clear(); + } + + // ==== IGraphics ==== + + public void drawBoxedStrings(int x, int y, List<?> strings) { + mDrawn.add("drawBoxedStrings(" + x + "," + y + "," + strings + ")"); + } + + public void drawLine(int x1, int y1, int x2, int y2) { + mDrawn.add("drawLine(" + x1 + "," + y1 + "," + x2 + "," + y2 + ")"); + } + + public void drawLine(Point p1, Point p2) { + mDrawn.add("drawLine(" + p1 + "," + p2 + ")"); + } + + public void drawRect(int x1, int y1, int x2, int y2) { + mDrawn.add("drawRect(" + x1 + "," + y1 + "," + x2 + "," + y2 + ")"); + } + + public void drawRect(Point p1, Point p2) { + mDrawn.add("drawRect(" + p1 + "," + p2 + ")"); + } + + public void drawRect(Rect r) { + mDrawn.add("drawRect(" + rectToString(r) + ")"); + } + + public void drawString(String string, int x, int y) { + mDrawn.add("drawString(" + x + "," + y + "," + string + ")"); + } + + public void drawString(String string, Point topLeft) { + mDrawn.add("drawString(" + string + "," + topLeft + ")"); + } + + public void fillRect(int x1, int y1, int x2, int y2) { + mDrawn.add("fillRect(" + x1 + "," + y1 + "," + x2 + "," + y2 + ")"); + } + + public void fillRect(Point p1, Point p2) { + mDrawn.add("fillRect(" + p1 + "," + p2 + ")"); + } + + public void fillRect(Rect r) { + mDrawn.add("fillRect(" + rectToString(r) + ")"); + } + + public int getAlpha() { + return mAlpha; + } + + public IColor getBackground() { + return mBackground; + } + + public int getFontHeight() { + return 12; + } + + public IColor getForeground() { + return mForeground; + } + + public IColor registerColor(int rgb) { + mDrawn.add("registerColor(" + Integer.toHexString(rgb) + ")"); + return new TestColor(rgb); + } + + public void setAlpha(int alpha) { + mAlpha = alpha; + mDrawn.add("setAlpha(" + alpha + ")"); + } + + public void setBackground(IColor color) { + mDrawn.add("setBackground(" + color + ")"); + mBackground = color; + } + + public void setForeground(IColor color) { + mDrawn.add("setForeground(" + color + ")"); + mForeground = color; + } + + public void setLineStyle(LineStyle style) { + mDrawn.add("setLineStyle(" + style + ")"); + } + + public void setLineWidth(int width) { + mDrawn.add("setLineWidth(" + width + ")"); + } + + public void useStyle(DrawingStyle style) { + mDrawn.add("useStyle(" + style + ")"); + } + + private static String rectToString(Rect rect) { + return "Rect[" + rect.x + "," + rect.y + "," + rect.w + "," + rect.h + "]"; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestNode.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestNode.java new file mode 100644 index 0000000..21250de --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestNode.java @@ -0,0 +1,164 @@ +/* + * 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 com.android.ide.common.api.IAttributeInfo; +import com.android.ide.common.api.INode; +import com.android.ide.common.api.INodeHandler; +import com.android.ide.common.api.Rect; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** Test/mock implementation of {@link INode} */ +public class TestNode implements INode { + private TestNode mParent; + + private final List<TestNode> mChildren = new ArrayList<TestNode>(); + + private final String mFqcn; + + private Rect mBounds = new Rect(); // Invalid bounds initially + + private Map<String, IAttribute> mAttributes = new HashMap<String, IAttribute>(); + + private Map<String, IAttributeInfo> mAttributeInfos = new HashMap<String, IAttributeInfo>(); + + public TestNode(String fqcn) { + this.mFqcn = fqcn; + } + + public TestNode bounds(Rect bounds) { + this.mBounds = bounds; + + return this; + } + + public TestNode id(String id) { + return set(BaseView.ANDROID_URI, BaseView.ATTR_ID, id); + } + + public TestNode set(String uri, String name, String value) { + setAttribute(uri, name, value); + + return this; + } + + public TestNode add(TestNode child) { + mChildren.add(child); + child.mParent = this; + + return this; + } + + public TestNode add(TestNode... children) { + for (TestNode child : children) { + mChildren.add(child); + child.mParent = this; + } + + return this; + } + + public static TestNode create(String fcqn) { + return new TestNode(fcqn); + } + + public void removeChild(int index) { + TestNode removed = mChildren.remove(index); + removed.mParent = null; + } + + // ==== INODE ==== + + public INode appendChild(String viewFqcn) { + return insertChildAt(viewFqcn, mChildren.size()); + } + + public void editXml(String undoName, INodeHandler callback) { + callback.handle(this); + } + + public IAttributeInfo getAttributeInfo(String uri, String attrName) { + return mAttributeInfos.get(uri + attrName); + } + + public Rect getBounds() { + return mBounds; + } + + public INode[] getChildren() { + return mChildren.toArray(new INode[mChildren.size()]); + } + + public IAttributeInfo[] getDeclaredAttributes() { + return mAttributeInfos.values().toArray(new IAttributeInfo[mAttributeInfos.size()]); + } + + public String getFqcn() { + return mFqcn; + } + + public IAttribute[] getLiveAttributes() { + return mAttributes.values().toArray(new IAttribute[mAttributes.size()]); + } + + public INode getParent() { + return mParent; + } + + public INode getRoot() { + TestNode curr = this; + while (curr.mParent != null) { + curr = curr.mParent; + } + + return curr; + } + + public String getStringAttr(String uri, String attrName) { + IAttribute attr = mAttributes.get(uri + attrName); + if (attr == null) { + return null; + } + + return attr.getValue(); + } + + public INode insertChildAt(String viewFqcn, int index) { + TestNode child = new TestNode(viewFqcn); + if (index == -1) { + mChildren.add(child); + } else { + mChildren.add(index, child); + } + child.mParent = this; + return child; + } + + public boolean setAttribute(String uri, String localName, String value) { + mAttributes.put(uri + localName, new TestAttribute(uri, localName, value)); + return true; + } + + @Override + public String toString() { + return "TestNode [fqn=" + mFqcn + ", infos=" + mAttributeInfos + + ", attributes=" + mAttributes + ", bounds=" + mBounds + "]"; + } +}
\ No newline at end of file diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/ZoomControlsRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/ZoomControlsRuleTest.java new file mode 100644 index 0000000..ee08633 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/ZoomControlsRuleTest.java @@ -0,0 +1,50 @@ +/* + * 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 com.android.ide.common.api.DropFeedback; +import com.android.ide.common.api.IDragElement; +import com.android.ide.common.api.INode; +import com.android.ide.common.api.Rect; + +/** Test the {@link ZoomControlsRule} */ +public class ZoomControlsRuleTest extends AbstractLayoutRuleTest { + public void testDoNothing() { + String draggedButtonId = "@+id/DraggedButton"; + + IDragElement[] elements = TestDragElement.create(TestDragElement.create( + "android.widget.Button").id(draggedButtonId)); + + INode layout = TestNode.create("android.widget.ZoomControls").id("@+id/ZoomControls01") + .bounds(new Rect(0, 0, 240, 480)).add( + TestNode.create("android.widget.Button").id("@+id/Button01").bounds( + new Rect(0, 0, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button02").bounds( + new Rect(0, 100, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button03").bounds( + new Rect(0, 200, 100, 80)), + TestNode.create("android.widget.Button").id("@+id/Button04").bounds( + new Rect(0, 300, 100, 80))); + + ZoomControlsRule rule = new ZoomControlsRule(); + + // Enter target + DropFeedback feedback = rule.onDropEnter(layout, elements); + // Zoom controls don't respond to drags + assertNull(feedback); + } +} |