diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common')
23 files changed, 4946 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/PointTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/PointTest.java new file mode 100755 index 000000000..54b6ff64c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/PointTest.java @@ -0,0 +1,123 @@ +/* + * 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.api; + +import com.android.ide.common.api.Point; + +import junit.framework.TestCase; + +public class PointTest extends TestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + public final void testPointIntInt() { + Point p = new Point(1, 2); + assertEquals(1, p.x); + assertEquals(2, p.y); + + p = new Point(-3, -4); + assertEquals(-3, p.x); + assertEquals(-4, p.y); + } + + public final void testSet() { + Point p = new Point(1, 2); + assertEquals(1, p.x); + assertEquals(2, p.y); + + p.set(-3, -4); + assertEquals(-3, p.x); + assertEquals(-4, p.y); + } + + public final void testPointPoint() { + Point p = new Point(1, 2); + Point p2 = new Point(p); + + assertNotSame(p, p2); + assertEquals(p, p2); + } + + public final void testPointPoint_Null() { + // Constructing a point with null throws an NPE + try { + new Point(null); + } catch (NullPointerException ignore) { + return; // success + } + + fail("new Point(null) failed to throew NullPointerException"); + } + + public final void testCopy() { + Point p = new Point(1, 2); + Point p2 = p.copy(); + + assertNotSame(p, p2); + assertEquals(p, p2); + } + + public final void testOffsetBy() { + Point p = new Point(1, 2); + Point p2 = p.offsetBy(3, 4); + + assertSame(p, p2); + assertEquals(1+3, p.x); + assertEquals(2+4, p.y); + } + + public final void testEquals_Null() { + Point p = new Point(1, 2); + assertFalse(p.equals(null)); + } + + public final void testEquals_UnknownObject() { + Point p = new Point(1, 2); + assertFalse(p.equals(new Object())); + } + + public final void testEquals_Point() { + Point p = new Point(1, 2); + Point p1 = new Point(1, 2); + Point p2 = new Point(-3, -4); + + assertNotSame(p1, p); + assertTrue(p.equals(p1)); + + assertFalse(p.equals(p2)); + } + + public final void testHashCode() { + Point p = new Point(1, 2); + Point p1 = new Point(1, 2); + Point p2 = new Point(-3, -4); + + assertNotSame(p1, p); + assertEquals(p1.hashCode(), p.hashCode()); + + assertFalse(p2.hashCode() == p.hashCode()); + } + + public final void testToString() { + Point p = new Point(1, 2); + assertEquals("Point [1x2]", p.toString()); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java new file mode 100755 index 000000000..2f2c59a75 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java @@ -0,0 +1,301 @@ +/* + * 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.api; + +import junit.framework.TestCase; + +public class RectTest extends TestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + public final void testRect() { + Rect r = new Rect(); + assertEquals(0, r.x); + assertEquals(0, r.y); + assertEquals(0, r.w); + assertEquals(0, r.h); + } + + public final void testRectIntIntIntInt() { + Rect r = new Rect(1, 2, 3, 4); + assertEquals(1, r.x); + assertEquals(2, r.y); + assertEquals(3, r.w); + assertEquals(4, r.h); + } + + public final void testRectRect() { + Rect r = new Rect(1, 2, 3, 4); + Rect r2 = new Rect(r); + + assertNotSame(r2, r); + assertEquals(r2, r); + } + + public final void testSetIntIntIntInt() { + Rect r = new Rect(1, 2, 3, 4); + Rect r2 = r.set(3, 4, 20, 30); + + assertSame(r2, r); + assertEquals(3, r2.x); + assertEquals(4, r2.y); + assertEquals(20, r2.w); + assertEquals(30, r2.h); + } + + public final void testSetRect() { + Rect r = new Rect(1, 2, 3, 4); + Rect r2 = new Rect(3, 4, 20, 30); + Rect r3 = r.set(r2); + + assertSame(r3, r); + assertNotSame(r3, r2); + assertEquals(3, r.x); + assertEquals(4, r.y); + assertEquals(20, r.w); + assertEquals(30, r.h); + } + + public final void testCopy() { + Rect r = new Rect(1, 2, 3, 4); + Rect r2 = r.copy(); + + assertNotSame(r2, r); + assertEquals(r2, r); + } + + public final void testIsValid() { + Rect r = new Rect(); + assertFalse(r.isValid()); + + r = new Rect(1, 2, 3, 4); + assertTrue(r.isValid()); + + // Rectangles must have a width > 0 to be valid + r = new Rect(1, 2, 0, 4); + assertFalse(r.isValid()); + r = new Rect(1, 2, -5, 4); + assertFalse(r.isValid()); + + // Rectangles must have a height > 0 to be valid + r = new Rect(1, 2, 3, 0); + assertFalse(r.isValid()); + r = new Rect(1, 2, 3, -5); + assertFalse(r.isValid()); + + r = new Rect(1, 2, 0, 0); + assertFalse(r.isValid()); + r = new Rect(1, 2, -20, -5); + assertFalse(r.isValid()); + } + + public final void testContainsIntInt() { + Rect r = new Rect(3, 4, 20, 30); + + assertTrue(r.contains(3, 4)); + assertTrue(r.contains(3+19, 4)); + assertTrue(r.contains(3+19, 4+29)); + assertTrue(r.contains(3, 4+29)); + + assertFalse(r.contains(3-1, 4)); + assertFalse(r.contains(3, 4-1)); + assertFalse(r.contains(3-1, 4-1)); + + assertFalse(r.contains(3+20, 4)); + assertFalse(r.contains(3+20, 4+30)); + assertFalse(r.contains(3, 4+30)); + } + + public final void testContainsIntInt_Invalid() { + // Invalid rects always return false + Rect r = new Rect(3, 4, -20, -30); + assertFalse(r.contains(3, 4)); + } + + public final void testContainsPoint_Null() { + // contains(null) returns false rather than an NPE + Rect r = new Rect(3, 4, -20, -30); + assertFalse(r.contains((Point) null)); + } + + public final void testContainsRect_Null() { + // contains(null) returns false rather than an NPE + Rect r = new Rect(3, 4, -20, -30); + assertFalse(r.contains((Rect) null)); + } + + public final void testContainsPoint() { + Rect r = new Rect(3, 4, 20, 30); + + assertTrue(r.contains(new Point(3, 4))); + assertTrue(r.contains(new Point(3+19, 4))); + assertTrue(r.contains(new Point(3+19, 4+29))); + assertTrue(r.contains(new Point(3, 4+29))); + + assertFalse(r.contains(new Point(3-1, 4))); + assertFalse(r.contains(new Point(3, 4-1))); + assertFalse(r.contains(new Point(3-1, 4-1))); + + assertFalse(r.contains(new Point(3+20, 4))); + assertFalse(r.contains(new Point(3+20, 4+30))); + assertFalse(r.contains(new Point(3, 4+30))); + } + + public final void testContainsRect() { + Rect r = new Rect(3, 4, 20, 30); + + assertTrue(r.contains(new Rect(3, 4, 5, 10))); + assertFalse(r.contains(new Rect(3 - 1, 4, 5, 10))); + } + + public final void testIntersects() { + Rect r1 = new Rect(0, 0, 10, 10); + Rect r2 = new Rect(1, 1, 5, 5); + Rect r3 = new Rect(10, 0, 1, 1); + Rect r4 = new Rect(5, 5, 10, 10); + Rect r5 = new Rect(-1, 0, 1, 1); + Rect r6 = new Rect(0, 10, 1, 1); + + assertTrue(r1.intersects(r2)); + assertTrue(r2.intersects(r1)); + assertTrue(r1.intersects(r4)); + assertFalse(r1.intersects(r3)); + assertFalse(r1.intersects(r5)); + assertFalse(r1.intersects(r6)); + } + + public final void testMoveTo() { + Rect r = new Rect(3, 4, 20, 30); + Rect r2 = r.moveTo(100, 200); + + assertSame(r2, r); + assertEquals(100, r.x); + assertEquals(200, r.y); + assertEquals(20, r.w); + assertEquals(30, r.h); + } + + public final void testOffsetBy() { + Rect r = new Rect(3, 4, 20, 30); + Rect r2 = r.offsetBy(100, 200); + + assertSame(r2, r); + assertEquals(103, r.x); + assertEquals(204, r.y); + assertEquals(20, r.w); + assertEquals(30, r.h); + } + + public final void testGetCenter() { + Rect r = new Rect(3, 4, 20, 30); + Point p = r.getCenter(); + + assertEquals(3+20/2, p.x); + assertEquals(4+30/2, p.y); + } + + public final void testGetTopLeft() { + Rect r = new Rect(3, 4, 20, 30); + Point p = r.getTopLeft(); + + assertEquals(3, p.x); + assertEquals(4, p.y); + } + + public final void testGetBottomLeft() { + Rect r = new Rect(3, 4, 20, 30); + Point p = r.getBottomLeft(); + + assertEquals(3, p.x); + assertEquals(4+30, p.y); + } + + public final void testGetTopRight() { + Rect r = new Rect(3, 4, 20, 30); + Point p = r.getTopRight(); + + assertEquals(3+20, p.x); + assertEquals(4, p.y); + } + + public final void testGetBottomRight() { + Rect r = new Rect(3, 4, 20, 30); + Point p = r.getBottomRight(); + + assertEquals(3+20, p.x); + assertEquals(4+30, p.y); + } + + public final void testToString() { + Rect r = new Rect(3, 4, 20, 30); + + assertEquals("Rect [(3,4)-(23,34): 20x30]", r.toString()); + } + + public final void testEqualsObject() { + Rect r = new Rect(3, 4, 20, 30); + + assertFalse(r.equals(null)); + assertFalse(r.equals(new Object())); + assertTrue(r.equals(new Rect(3, 4, 20, 30))); + } + + public final void testEqualsObject_Invalid() { + Rect r = new Rect(3, 4, 20, 30); + assertTrue(r.isValid()); + + Rect i1 = new Rect(3, 4, 0, 0); + assertFalse(i1.isValid()); + Rect i2 = new Rect(10, 20, 0, 0); + assertFalse(i2.isValid()); + + // valid rects can't be equal to invalid rects + assertFalse(r.equals(i1)); + assertFalse(r.equals(i2)); + + // invalid rects are equal to each other whatever their content is + assertEquals(i2, i1); + } + + public final void testHashCode() { + Rect r = new Rect(1, 2, 3, 4); + Rect r1 = new Rect(3, 4, 20, 30); + Rect r2 = new Rect(3, 4, 20, 30); + + assertFalse(r1.hashCode() == r.hashCode()); + assertEquals(r2.hashCode(), r1.hashCode()); + } + + + public final void testCenter() { + Rect r = new Rect(10, 20, 30, 40); + Point center = r.center(); + assertEquals(25, center.x); + assertEquals(40, center.y); + assertEquals(25, r.centerX()); + assertEquals(40, r.centerY()); + } + + public final void testX2Y2() { + Rect r = new Rect(1, 2, 3, 4); + assertEquals(4, r.x2()); + assertEquals(6, r.y2()); + } +} 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 000000000..1a14bdda4 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java @@ -0,0 +1,77 @@ +/* + * 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 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 LayoutTestBase { + // 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(30,-10,135,70)"); + + assertEquals("30dp", inserted.getStringAttr(ANDROID_URI, "layout_x")); + assertEquals("-10dp", inserted.getStringAttr(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("30dp", inserted.getStringAttr(ANDROID_URI, "layout_x")); + assertEquals("-10dp", inserted.getStringAttr(ANDROID_URI, "layout_y")); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseLayoutRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseLayoutRuleTest.java new file mode 100644 index 000000000..d176fe7d1 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseLayoutRuleTest.java @@ -0,0 +1,245 @@ +/* + * 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.ide.common.api.IDragElement; +import com.android.ide.common.api.INode; +import com.android.ide.common.api.Rect; +import com.android.ide.common.layout.BaseLayoutRule.AttributeFilter; +import com.android.utils.Pair; + +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 BaseLayoutRuleTest extends LayoutTestBase { + + /** 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 BaseLayoutRule#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 BaseLayoutRule().collectIds(idMap, elements); + assertEquals(1, ids.size()); + assertEquals("@+id/Button01", ids.keySet().iterator().next()); + } + + /** + * Test {@link BaseLayoutRule#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", ATTR_ID, + "@+id/Button01")); + + Map<String, Pair<String, String>> idMap = new HashMap<String, Pair<String, String>>(); + Map<String, Pair<String, String>> ids = new BaseLayoutRule().collectIds(idMap, elements); + assertEquals(0, ids.size()); + } + + /** + * Test {@link BaseLayoutRule#normalizeId(String)} + */ + public final void testNormalizeId() { + assertEquals("foo", new BaseLayoutRule().normalizeId("foo")); + assertEquals("@+id/name", new BaseLayoutRule().normalizeId("@id/name")); + assertEquals("@+id/name", new BaseLayoutRule().normalizeId("@+id/name")); + } + + /** + * Test {@link BaseLayoutRule#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 BaseLayoutRule().collectExistingIds(node, existing); + + assertEquals(2, existing.size()); + assertContainsSame(Arrays.asList("@+id/Button2", "@+id/Button012"), existing); + } + + /** + * Test {@link BaseLayoutRule#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 BaseLayoutRule().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 BaseLayoutRule#remapIds}: Ensure that it identifies a conflict + */ + public final void testRemapIds1() { + Map<String, Pair<String, String>> idMap = new HashMap<String, Pair<String, String>>(); + BaseLayoutRule baseLayout = new BaseLayoutRule(); + 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 BaseLayoutRule#getDropIdMap} + */ + public final void testGetDropIdMap() { + BaseLayoutRule baseLayout = new BaseLayoutRule(); + 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() { + BaseLayoutRule layout = new BaseLayoutRule(); + + // 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 + BaseLayoutRule layout = new BaseLayoutRule(); + + // 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() { + + @Override + 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() { + BaseLayoutRule baseLayout = new BaseLayoutRule(); + 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", BaseLayoutRule.DEFAULT_ATTR_FILTER.replace("myuri", "layout_alignRight", + "true")); + assertEquals(null, BaseLayoutRule.DEFAULT_ATTR_FILTER.replace(ANDROID_URI, + "layout_alignRight", "true")); + assertEquals("true", BaseLayoutRule.DEFAULT_ATTR_FILTER.replace(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>>(); + BaseLayoutRule layout = new BaseLayoutRule(); + 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/BaseViewRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseViewRuleTest.java new file mode 100644 index 000000000..6d46b1efc --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseViewRuleTest.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 BaseViewRuleTest extends TestCase { + + public final void testGetAttributeDisplayName() { + assertEquals(null, BaseViewRule.getAttributeDisplayName(null)); + assertEquals("", BaseViewRule.getAttributeDisplayName("")); + assertEquals("Foo", BaseViewRule.getAttributeDisplayName("foo")); + assertEquals("FooBar", BaseViewRule.getAttributeDisplayName("fooBar")); + assertEquals("Foo Bar", BaseViewRule.getAttributeDisplayName("foo_bar")); + // TBD: Should we also handle CamelCase properties? + // assertEquals("Foo Bar", BaseViewRule.getAttributeDisplayName("fooBar")); + } + + public final void testJoin() { + assertEquals("foo", BaseViewRule.join('|', Arrays.asList("foo"))); + assertEquals("", BaseViewRule.join('|', Collections.<String>emptyList())); + assertEquals("foo,bar", BaseViewRule.join(',', Arrays.asList("foo", "bar"))); + assertEquals("foo|bar", BaseViewRule.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 000000000..4a60566d9 --- /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 LayoutTestBase { + // 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(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/GravityHelperTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/GravityHelperTest.java new file mode 100644 index 000000000..8161babbe --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/GravityHelperTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2011 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.ide.common.layout.GravityHelper.GRAVITY_BOTTOM; +import static com.android.ide.common.layout.GravityHelper.GRAVITY_CENTER_HORIZ; +import static com.android.ide.common.layout.GravityHelper.GRAVITY_CENTER_VERT; +import static com.android.ide.common.layout.GravityHelper.GRAVITY_END; +import static com.android.ide.common.layout.GravityHelper.GRAVITY_FILL_HORIZ; +import static com.android.ide.common.layout.GravityHelper.GRAVITY_FILL_VERT; +import static com.android.ide.common.layout.GravityHelper.GRAVITY_LEFT; +import static com.android.ide.common.layout.GravityHelper.GRAVITY_RIGHT; +import static com.android.ide.common.layout.GravityHelper.GRAVITY_START; +import static com.android.ide.common.layout.GravityHelper.GRAVITY_TOP; +import static com.android.ide.common.layout.GravityHelper.getGravity; + +import com.android.utils.XmlUtils; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class GravityHelperTest extends TestCase { + public void testGravity() throws Exception { + assertEquals(GRAVITY_BOTTOM, GravityHelper.getGravity("bottom", 0)); + assertEquals(GRAVITY_BOTTOM | GRAVITY_LEFT, GravityHelper.getGravity("bottom|left", 0)); + assertEquals(GRAVITY_BOTTOM | GRAVITY_LEFT | GRAVITY_START, + GravityHelper.getGravity("bottom|left|start", 0)); + assertEquals(GRAVITY_BOTTOM | GRAVITY_LEFT | GRAVITY_START, + GravityHelper.getGravity("bottom|start|left", 0)); + assertEquals(GRAVITY_BOTTOM | GRAVITY_RIGHT | GRAVITY_END, + GravityHelper.getGravity("bottom|right|end", 0)); + assertEquals(GRAVITY_BOTTOM | GRAVITY_RIGHT | GRAVITY_END, + GravityHelper.getGravity("bottom|end|right", 0)); + assertEquals(GRAVITY_CENTER_HORIZ | GRAVITY_CENTER_VERT, + GravityHelper.getGravity("center", 0)); + } + + public void testGravityString() throws Exception { + assertEquals("left", getGravity(GRAVITY_LEFT)); + assertEquals("start", getGravity(GRAVITY_START)); + assertEquals("right", getGravity(GRAVITY_RIGHT)); + assertEquals("end", getGravity(GRAVITY_END)); + assertEquals("top", getGravity(GRAVITY_TOP)); + assertEquals("bottom", getGravity(GRAVITY_BOTTOM)); + assertEquals("center_horizontal", getGravity(GRAVITY_CENTER_HORIZ)); + assertEquals("center_vertical", getGravity(GRAVITY_CENTER_VERT)); + assertEquals("fill_horizontal", getGravity(GRAVITY_FILL_HORIZ)); + assertEquals("fill_vertical", getGravity(GRAVITY_FILL_VERT)); + + assertEquals("center", getGravity(GRAVITY_CENTER_HORIZ|GRAVITY_CENTER_VERT)); + + assertEquals("left|bottom", getGravity(GRAVITY_LEFT|GRAVITY_BOTTOM)); + assertEquals("left|start|bottom", getGravity(GRAVITY_LEFT|GRAVITY_START|GRAVITY_BOTTOM)); + assertEquals("center_horizontal|top", getGravity(GRAVITY_CENTER_HORIZ|GRAVITY_TOP)); + } + + public void testConstrained() throws Exception { + assertTrue(GravityHelper.isConstrainedHorizontally(GRAVITY_LEFT)); + assertTrue(GravityHelper.isConstrainedHorizontally(GRAVITY_START)); + assertTrue(GravityHelper.isConstrainedHorizontally(GRAVITY_RIGHT)); + assertTrue(GravityHelper.isConstrainedHorizontally(GRAVITY_END)); + assertTrue(GravityHelper.isConstrainedHorizontally(GRAVITY_CENTER_HORIZ)); + assertTrue(GravityHelper.isConstrainedHorizontally(GRAVITY_FILL_HORIZ)); + + assertFalse(GravityHelper.isConstrainedVertically(GRAVITY_LEFT)); + assertFalse(GravityHelper.isConstrainedVertically(GRAVITY_START)); + assertFalse(GravityHelper.isConstrainedVertically(GRAVITY_RIGHT)); + assertFalse(GravityHelper.isConstrainedVertically(GRAVITY_END)); + assertFalse(GravityHelper.isConstrainedVertically(GRAVITY_CENTER_HORIZ)); + assertFalse(GravityHelper.isConstrainedVertically(GRAVITY_FILL_HORIZ)); + + assertTrue(GravityHelper.isConstrainedVertically(GRAVITY_TOP)); + assertTrue(GravityHelper.isConstrainedVertically(GRAVITY_BOTTOM)); + assertTrue(GravityHelper.isConstrainedVertically(GRAVITY_CENTER_VERT)); + assertTrue(GravityHelper.isConstrainedVertically(GRAVITY_FILL_VERT)); + + assertFalse(GravityHelper.isConstrainedHorizontally(GRAVITY_TOP)); + assertFalse(GravityHelper.isConstrainedHorizontally(GRAVITY_BOTTOM)); + assertFalse(GravityHelper.isConstrainedHorizontally(GRAVITY_CENTER_VERT)); + assertFalse(GravityHelper.isConstrainedHorizontally(GRAVITY_FILL_VERT)); + } + + public void testAligned() throws Exception { + assertTrue(GravityHelper.isLeftAligned(GRAVITY_LEFT|GRAVITY_TOP)); + assertTrue(GravityHelper.isLeftAligned(GRAVITY_START|GRAVITY_TOP)); + assertTrue(GravityHelper.isLeftAligned(GRAVITY_LEFT)); + assertTrue(GravityHelper.isLeftAligned(GRAVITY_START)); + assertFalse(GravityHelper.isLeftAligned(GRAVITY_RIGHT)); + assertFalse(GravityHelper.isLeftAligned(GRAVITY_END)); + + assertTrue(GravityHelper.isTopAligned(GRAVITY_LEFT|GRAVITY_TOP)); + assertTrue(GravityHelper.isTopAligned(GRAVITY_START|GRAVITY_TOP)); + assertTrue(GravityHelper.isTopAligned(GRAVITY_TOP)); + assertFalse(GravityHelper.isTopAligned(GRAVITY_BOTTOM)); + } + + public void testElement() throws Exception { + Document document = XmlUtils.parseDocumentSilently("" + + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_gravity=\"start|center_vertical|left\"" + + " android:orientation=\"vertical\" />\n", true); + assertNotNull(document); + Element element = document.getDocumentElement(); + assertNotNull(element); + int gravity = GravityHelper.getGravity(element); + assertEquals("left|start|center_vertical", GravityHelper.getGravity(gravity)); + } +}
\ No newline at end of file diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/GridLayoutRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/GridLayoutRuleTest.java new file mode 100644 index 000000000..f0483655d --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/GridLayoutRuleTest.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2011 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; + + +public class GridLayoutRuleTest extends LayoutTestBase { + @Override + public void testDummy() { + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java new file mode 100644 index 000000000..4b4bb814a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java @@ -0,0 +1,375 @@ +/* + * 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 + } +} 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 000000000..9c4c934c0 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LinearLayoutRuleTest.java @@ -0,0 +1,466 @@ +/* + * 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 static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT; +import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH; +import static com.android.SdkConstants.ATTR_ORIENTATION; +import static com.android.SdkConstants.VALUE_HORIZONTAL; +import static com.android.SdkConstants.VALUE_VERTICAL; + +import com.android.ide.common.api.DropFeedback; +import com.android.ide.common.api.IAttributeInfo.Format; +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.Point; +import com.android.ide.common.api.Rect; +import com.android.ide.common.api.RuleAction; +import com.android.ide.common.api.RuleAction.NestedAction; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** Test the {@link LinearLayoutRule} */ +public class LinearLayoutRuleTest extends LayoutTestBase { + // 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, null/*targetView*/, 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), drawLine(1,0,1,480), " + + "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(1,0,101,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( + ANDROID_URI, 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(ANDROID_URI, + ATTR_ORIENTATION, + vertical ? VALUE_VERTICAL : 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(); + initialize(rule, "android.widget.LinearLayout"); + INode node = TestNode.create("android.widget.Button").id("@+id/Button012"); + + List<RuleAction> contextMenu = new ArrayList<RuleAction>(); + rule.addContextMenuActions(contextMenu, node); + assertEquals(6, contextMenu.size()); + assertEquals("Edit ID...", contextMenu.get(0).getTitle()); + assertTrue(contextMenu.get(1) instanceof RuleAction.Separator); + assertEquals("Layout Width", contextMenu.get(2).getTitle()); + assertEquals("Layout Height", contextMenu.get(3).getTitle()); + assertTrue(contextMenu.get(4) instanceof RuleAction.Separator); + assertEquals("Other Properties", contextMenu.get(5).getTitle()); + + RuleAction propertiesMenu = contextMenu.get(5); + assertTrue(propertiesMenu.getClass().getName(), + propertiesMenu instanceof NestedAction); + } + + public void testContextMenuCustom() { + LinearLayoutRule rule = new LinearLayoutRule(); + initialize(rule, "android.widget.LinearLayout"); + INode node = TestNode.create("android.widget.LinearLayout").id("@+id/LinearLayout") + .set(ANDROID_URI, ATTR_LAYOUT_WIDTH, "42dip") + .set(ANDROID_URI, ATTR_LAYOUT_HEIGHT, "50sp"); + + List<RuleAction> contextMenu = new ArrayList<RuleAction>(); + rule.addContextMenuActions(contextMenu, node); + assertEquals(6, contextMenu.size()); + assertEquals("Layout Width", contextMenu.get(2).getTitle()); + RuleAction menuAction = contextMenu.get(2); + assertTrue(menuAction instanceof RuleAction.Choices); + RuleAction.Choices choices = (RuleAction.Choices) menuAction; + List<String> titles = choices.getTitles(); + List<String> ids = choices.getIds(); + assertEquals("Wrap Content", titles.get(0)); + assertEquals("wrap_content", ids.get(0)); + assertEquals("Match Parent", titles.get(1)); + assertEquals("match_parent", ids.get(1)); + assertEquals("42dip", titles.get(2)); + assertEquals("42dip", ids.get(2)); + assertEquals("42dip", choices.getCurrent()); + } + + // Check that the context menu manipulates the orientation attribute + public void testOrientation() { + LinearLayoutRule rule = new LinearLayoutRule(); + initialize(rule, "android.widget.LinearLayout"); + TestNode node = TestNode.create("android.widget.LinearLayout").id("@+id/LinearLayout012"); + node.putAttributeInfo(ANDROID_URI, "orientation", + new TestAttributeInfo(ATTR_ORIENTATION, Format.ENUM_SET, + "android.widget.LinearLayout", + new String[] {"horizontal", "vertical"}, null, null)); + + assertNull(node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION)); + + List<RuleAction> contextMenu = new ArrayList<RuleAction>(); + rule.addContextMenuActions(contextMenu, node); + assertEquals(7, contextMenu.size()); + RuleAction orientationAction = contextMenu.get(1); + assertEquals("Orientation", orientationAction.getTitle()); + + assertTrue(orientationAction.getClass().getName(), + orientationAction instanceof RuleAction.Choices); + + RuleAction.Choices choices = (RuleAction.Choices) orientationAction; + IMenuCallback callback = choices.getCallback(); + callback.action(orientationAction, Collections.singletonList(node), VALUE_VERTICAL, true); + + String orientation = node.getStringAttr(ANDROID_URI, + ATTR_ORIENTATION); + assertEquals(VALUE_VERTICAL, orientation); + callback.action(orientationAction, Collections.singletonList(node), VALUE_HORIZONTAL, + true); + orientation = node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION); + assertEquals(VALUE_HORIZONTAL, orientation); + } + + // Check that the context menu manipulates the orientation attribute + public void testProperties() { + LinearLayoutRule rule = new LinearLayoutRule(); + initialize(rule, "android.widget.LinearLayout"); + TestNode node = TestNode.create("android.widget.LinearLayout").id("@+id/LinearLayout012"); + node.putAttributeInfo(ANDROID_URI, "orientation", + new TestAttributeInfo(ATTR_ORIENTATION, Format.ENUM_SET, + "android.widget.LinearLayout", + new String[] {"horizontal", "vertical"}, null, null)); + node.setAttributeSources(Arrays.asList("android.widget.LinearLayout", + "android.view.ViewGroup", "android.view.View")); + node.putAttributeInfo(ANDROID_URI, "gravity", + new TestAttributeInfo("gravity", Format.INTEGER_SET, + "android.widget.LinearLayout", null, null, null)); + + + assertNull(node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION)); + + List<RuleAction> contextMenu = new ArrayList<RuleAction>(); + rule.addContextMenuActions(contextMenu, node); + assertEquals(8, contextMenu.size()); + + assertEquals("Orientation", contextMenu.get(1).getTitle()); + assertEquals("Edit Gravity...", contextMenu.get(2).getTitle()); + + assertEquals("Other Properties", contextMenu.get(7).getTitle()); + + RuleAction propertiesMenu = contextMenu.get(7); + assertTrue(propertiesMenu.getClass().getName(), + propertiesMenu instanceof NestedAction); + NestedAction nested = (NestedAction) propertiesMenu; + List<RuleAction> nestedActions = nested.getNestedActions(node); + assertEquals(9, nestedActions.size()); + assertEquals("Recent", nestedActions.get(0).getTitle()); + assertTrue(nestedActions.get(1) instanceof RuleAction.Separator); + assertEquals("Defined by LinearLayout", nestedActions.get(2).getTitle()); + assertEquals("Inherited from ViewGroup", nestedActions.get(3).getTitle()); + assertEquals("Inherited from View", nestedActions.get(4).getTitle()); + assertTrue(nestedActions.get(5) instanceof RuleAction.Separator); + assertEquals("Layout Parameters", nestedActions.get(6).getTitle()); + assertTrue(nestedActions.get(7) instanceof RuleAction.Separator); + assertEquals("All By Name", nestedActions.get(8).getTitle()); + + BaseViewRule.editedProperty(ATTR_ORIENTATION); + + RuleAction recentAction = nestedActions.get(0); + assertTrue(recentAction instanceof NestedAction); + NestedAction recentChoices = (NestedAction) recentAction; + List<RuleAction> recentItems = recentChoices.getNestedActions(node); + + assertEquals(1, recentItems.size()); + assertEquals("Orientation", recentItems.get(0).getTitle()); + + BaseViewRule.editedProperty("gravity"); + recentItems = recentChoices.getNestedActions(node); + assertEquals(2, recentItems.size()); + assertEquals("Gravity...", recentItems.get(0).getTitle()); + assertEquals("Orientation", recentItems.get(1).getTitle()); + + BaseViewRule.editedProperty(ATTR_ORIENTATION); + recentItems = recentChoices.getNestedActions(node); + assertEquals(2, recentItems.size()); + assertEquals("Orientation", recentItems.get(0).getTitle()); + assertEquals("Gravity...", recentItems.get(1).getTitle()); + + // Lots of other properties -- flushes out properties that apply to this view + for (int i = 0; i < 30; i++) { + BaseViewRule.editedProperty("dummy_" + i); + } + recentItems = recentChoices.getNestedActions(node); + assertEquals(0, recentItems.size()); + + BaseViewRule.editedProperty("gravity"); + recentItems = recentChoices.getNestedActions(node); + assertEquals(1, recentItems.size()); + assertEquals("Gravity...", recentItems.get(0).getTitle()); + } + + 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), " + + "drawLine(0,381,240,381)", + + // Active nearest line + "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,0,240,0)", + + // Preview of the dropped rectangle + "useStyle(DROP_PREVIEW), drawRect(0,-40,105,40)"); + + // 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), drawLine(0,381,240,381), ", + + // Active nearest line + "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,381,240,381)", + + // Preview of the dropped rectangle + "useStyle(DROP_PREVIEW), drawRect(0,381,105,461)"); + + // Check without bounds too + dragInto(true, new Rect(0, 0, 105, 80), new Point(30, 500), 4, -1, + "useStyle(DROP_PREVIEW), drawRect(0,381,105,461)"); + } + + 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(0,150,105,230)"); + + // Check without bounds too + dragInto(true, new Rect(0, 0, 105, 80), new Point(0, 170), 2, -1, + "useStyle(DROP_PREVIEW), drawRect(0,150,105,230)"); + } + + 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(0,250,100,330)"); + + // 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(0,100,100,180)"); + } + + public void testDragToLastPosition() { + // Drag a button to the last position -- and confirm that the preview rectangle + // is now shown midway between the second to last and last positions, but fully + // below the drop zone line: + dragInto(true, + // Bounds of the dragged item + new Rect(0, 100, 100, 80), + // Drag point + new Point(0, 400), + // Expected insert location + 3, + // Dragging 1st item + 1, + + // Bounds rectangle + "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", + + // Drop Zones + "useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), " + + "drawLine(0,381,240,381), ", + + // Active Drop Zone + "useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,381,240,381)", + + // Drop Preview + "useStyle(DROP_PREVIEW), drawRect(0,381,100,461)"); + } + + // 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 000000000..f9747f39d --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/RelativeLayoutRuleTest.java @@ -0,0 +1,167 @@ +/* + * 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 com.android.ide.common.api.INode; +import com.android.ide.common.api.Point; +import com.android.ide.common.api.Rect; + +/** Test the {@link RelativeLayoutRule} */ +public class RelativeLayoutRuleTest extends LayoutTestBase { + // 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); + } + + /* This needs to be updated for the new interaction + 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(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(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 000000000..752d61c77 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttribute.java @@ -0,0 +1,61 @@ +/* + * 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.annotations.NonNull; +import com.android.ide.common.api.IDragElement.IDragAttribute; +import com.android.ide.common.api.INode.IAttribute; +import com.android.ide.common.xml.XmlAttributeSortOrder; + +/** 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; + } + + @Override + public @NonNull String getName() { + return mName; + } + + @Override + public @NonNull String getUri() { + return mUri; + } + + @Override + public @NonNull String getValue() { + return mValue; + } + + @Override + public String toString() { + return "TestAttribute [name=" + mName + ", uri=" + mUri + ", value=" + mValue + "]"; + } + + public int compareTo(IDragAttribute o) { + return XmlAttributeSortOrder.compareAttributes(mName, o.getName()); + } +}
\ 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 000000000..69984bdbf --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttributeInfo.java @@ -0,0 +1,81 @@ +/* + * 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.annotations.NonNull; +import com.android.ide.common.api.IAttributeInfo; + +import java.util.EnumSet; + +/** Test/mock implementation of {@link IAttributeInfo} */ +public class TestAttributeInfo implements IAttributeInfo { + private final String mName; + private final EnumSet<Format> mFormats; + private final String mDefinedBy; + private final String[] mEnumValues; + private final String[] mFlagValues; + private final String mJavadoc; + + public TestAttributeInfo(String name) { + this(name, null, null, null, null, null); + } + + public TestAttributeInfo(String name, EnumSet<Format> formats, String definedBy, + String[] enumValues, String[] flagValues, String javadoc) { + super(); + this.mName = name; + this.mFormats = formats; + this.mDefinedBy = definedBy; + this.mEnumValues = enumValues; + this.mFlagValues = flagValues; + this.mJavadoc = javadoc; + } + + @Override + public String getDeprecatedDoc() { + return null; + } + + @Override + public String[] getEnumValues() { + return mEnumValues; + } + + @Override + public String[] getFlagValues() { + return mFlagValues; + } + + @Override + public @NonNull EnumSet<Format> getFormats() { + return mFormats; + } + + @Override + public @NonNull String getJavaDoc() { + return mJavadoc; + } + + @Override + public @NonNull String getName() { + return mName; + } + + @Override + public @NonNull String getDefinedBy() { + return mDefinedBy; + } +}
\ 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 000000000..449ad5ebb --- /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 000000000..b96de60c7 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestDragElement.java @@ -0,0 +1,158 @@ +/* + * 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.IDragElement; +import com.android.ide.common.api.INode; +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(ANDROID_URI, 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 ==== + + @Override + public IDragAttribute getAttribute(@Nullable String uri, @NonNull String localName) { + if (mAttributes == null) { + return new TestAttribute(uri, localName, ""); + } + + return mAttributes.get(uri + localName); + } + + @Override + public @NonNull IDragAttribute[] getAttributes() { + return mAttributes.values().toArray(new IDragAttribute[mAttributes.size()]); + } + + @Override + public @NonNull Rect getBounds() { + return mRect; + } + + @Override + public @NonNull String getFqcn() { + return mFqcn; + } + + @Override + public @NonNull IDragElement[] getInnerElements() { + if (mChildren == null) { + return new IDragElement[0]; + } + + return mChildren.toArray(new IDragElement[mChildren.size()]); + } + + @Override + public @NonNull Rect getParentBounds() { + return mParent != null ? mParent.getBounds() : null; + } + + @Override + public String getParentFqcn() { + return mParent != null ? mParent.getFqcn() : null; + } + + @Override + public String toString() { + return "TestDragElement [fqn=" + mFqcn + ", attributes=" + mAttributes + ", bounds=" + + mRect + "]"; + } + + @Override + public boolean isSame(INode node) { + return node.getBounds().equals(getBounds()); + } +} 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 000000000..ec9321003 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestGraphics.java @@ -0,0 +1,181 @@ +/* + * 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.annotations.NonNull; +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 ==== + + @Override + public void drawBoxedStrings(int x, int y, @NonNull List<?> strings) { + mDrawn.add("drawBoxedStrings(" + x + "," + y + "," + strings + ")"); + } + + @Override + public void drawLine(int x1, int y1, int x2, int y2) { + mDrawn.add("drawLine(" + x1 + "," + y1 + "," + x2 + "," + y2 + ")"); + } + + @Override + public void drawLine(@NonNull Point p1, @NonNull Point p2) { + mDrawn.add("drawLine(" + p1 + "," + p2 + ")"); + } + + @Override + public void drawRect(int x1, int y1, int x2, int y2) { + mDrawn.add("drawRect(" + x1 + "," + y1 + "," + x2 + "," + y2 + ")"); + } + + @Override + public void drawRect(@NonNull Point p1, @NonNull Point p2) { + mDrawn.add("drawRect(" + p1 + "," + p2 + ")"); + } + + @Override + public void drawRect(@NonNull Rect r) { + mDrawn.add("drawRect(" + rectToString(r) + ")"); + } + + @Override + public void drawString(@NonNull String string, int x, int y) { + mDrawn.add("drawString(" + x + "," + y + "," + string + ")"); + } + + @Override + public void drawString(@NonNull String string, @NonNull Point topLeft) { + mDrawn.add("drawString(" + string + "," + topLeft + ")"); + } + + @Override + public void fillRect(int x1, int y1, int x2, int y2) { + mDrawn.add("fillRect(" + x1 + "," + y1 + "," + x2 + "," + y2 + ")"); + } + + @Override + public void fillRect(@NonNull Point p1, @NonNull Point p2) { + mDrawn.add("fillRect(" + p1 + "," + p2 + ")"); + } + + @Override + public void fillRect(@NonNull Rect r) { + mDrawn.add("fillRect(" + rectToString(r) + ")"); + } + + @Override + public int getAlpha() { + return mAlpha; + } + + @Override + public @NonNull IColor getBackground() { + return mBackground; + } + + @Override + public int getFontHeight() { + return 12; + } + + @Override + public @NonNull IColor getForeground() { + return mForeground; + } + + @Override + public @NonNull IColor registerColor(int rgb) { + mDrawn.add("registerColor(" + Integer.toHexString(rgb) + ")"); + return new TestColor(rgb); + } + + @Override + public void setAlpha(int alpha) { + mAlpha = alpha; + mDrawn.add("setAlpha(" + alpha + ")"); + } + + @Override + public void setBackground(@NonNull IColor color) { + mDrawn.add("setBackground(" + color + ")"); + mBackground = color; + } + + @Override + public void setForeground(@NonNull IColor color) { + mDrawn.add("setForeground(" + color + ")"); + mForeground = color; + } + + @Override + public void setLineStyle(@NonNull LineStyle style) { + mDrawn.add("setLineStyle(" + style + ")"); + } + + @Override + public void setLineWidth(int width) { + mDrawn.add("setLineWidth(" + width + ")"); + } + + @Override + public void useStyle(@NonNull DrawingStyle style) { + mDrawn.add("useStyle(" + style + ")"); + } + + @Override + public void drawArrow(int x1, int y1, int x2, int y2, int size) { + mDrawn.add("drawArrow(" + x1 + "," + y1 + "," + x2 + "," + y2 + ")"); + } + + @Override + public void drawPoint(int x, int y) { + mDrawn.add("drawPoint(" + x + "," + y + ")"); + } + + 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 000000000..30886e4fa --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestNode.java @@ -0,0 +1,477 @@ +/* + * 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.ANDROID_WIDGET_PREFIX; +import static com.android.SdkConstants.ATTR_ID; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +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.Margins; +import com.android.ide.common.api.Rect; +import com.android.ide.common.xml.XmlFormatStyle; +import com.android.ide.common.xml.XmlPrettyPrinter; +import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlFormatPreferences; +import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlPrettyPrinter; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; +import com.google.common.base.Splitter; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** Test/mock implementation of {@link INode} */ +@SuppressWarnings("javadoc") +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>(); + + private List<String> mAttributeSources; + + public TestNode(String fqcn) { + this.mFqcn = fqcn; + } + + public TestNode bounds(Rect bounds) { + this.mBounds = bounds; + + return this; + } + + public TestNode id(String id) { + return set(ANDROID_URI, 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 ==== + + @Override + public @NonNull INode appendChild(@NonNull String viewFqcn) { + return insertChildAt(viewFqcn, mChildren.size()); + } + + @Override + public void editXml(@NonNull String undoName, @NonNull INodeHandler callback) { + callback.handle(this); + } + + public void putAttributeInfo(String uri, String attrName, IAttributeInfo info) { + mAttributeInfos.put(uri + attrName, info); + } + + @Override + public IAttributeInfo getAttributeInfo(@Nullable String uri, @NonNull String attrName) { + return mAttributeInfos.get(uri + attrName); + } + + @Override + public @NonNull Rect getBounds() { + return mBounds; + } + + @Override + public @NonNull INode[] getChildren() { + return mChildren.toArray(new INode[mChildren.size()]); + } + + @Override + public @NonNull IAttributeInfo[] getDeclaredAttributes() { + return mAttributeInfos.values().toArray(new IAttributeInfo[mAttributeInfos.size()]); + } + + @Override + public @NonNull String getFqcn() { + return mFqcn; + } + + @Override + public @NonNull IAttribute[] getLiveAttributes() { + return mAttributes.values().toArray(new IAttribute[mAttributes.size()]); + } + + @Override + public INode getParent() { + return mParent; + } + + @Override + public INode getRoot() { + TestNode curr = this; + while (curr.mParent != null) { + curr = curr.mParent; + } + + return curr; + } + + @Override + public String getStringAttr(@Nullable String uri, @NonNull String attrName) { + IAttribute attr = mAttributes.get(uri + attrName); + if (attr == null) { + return null; + } + + return attr.getValue(); + } + + @Override + public @NonNull INode insertChildAt(@NonNull 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; + } + + @Override + public void removeChild(@NonNull INode node) { + int index = mChildren.indexOf(node); + if (index != -1) { + removeChild(index); + } + } + + @Override + public boolean setAttribute(@Nullable String uri, @NonNull String localName, + @Nullable String value) { + mAttributes.put(uri + localName, new TestAttribute(uri, localName, value)); + return true; + } + + @Override + public String toString() { + String id = getStringAttr(ANDROID_URI, ATTR_ID); + return "TestNode [id=" + (id != null ? id : "?") + ", fqn=" + mFqcn + ", infos=" + + mAttributeInfos + ", attributes=" + mAttributes + ", bounds=" + mBounds + "]"; + } + + @Override + public int getBaseline() { + return -1; + } + + @Override + public @NonNull Margins getMargins() { + return null; + } + + @Override + public @NonNull List<String> getAttributeSources() { + return mAttributeSources != null ? mAttributeSources : Collections.<String>emptyList(); + } + + public void setAttributeSources(List<String> attributeSources) { + mAttributeSources = attributeSources; + } + + /** Create a test node from the given XML */ + public static TestNode createFromXml(String xml) { + Document document = DomUtilities.parseDocument(xml, false); + assertNotNull(document); + assertNotNull(document.getDocumentElement()); + + return createFromNode(document.getDocumentElement()); + } + + public static String toXml(TestNode node) { + assertTrue("This method only works with nodes constructed from XML", + node instanceof TestXmlNode); + Document document = ((TestXmlNode) node).mElement.getOwnerDocument(); + // Insert new whitespace nodes etc + String xml = dumpDocument(document); + document = DomUtilities.parseDocument(xml, false); + + XmlPrettyPrinter printer = new EclipseXmlPrettyPrinter(EclipseXmlFormatPreferences.create(), + XmlFormatStyle.LAYOUT, "\n"); + StringBuilder sb = new StringBuilder(1000); + sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); + printer.prettyPrint(-1, document, null, null, sb, false); + return sb.toString(); + } + + @SuppressWarnings("deprecation") + private static String dumpDocument(Document document) { + // Diagnostics: print out the XML that we're about to render + org.apache.xml.serialize.OutputFormat outputFormat = + new org.apache.xml.serialize.OutputFormat( + "XML", "ISO-8859-1", true); //$NON-NLS-1$ //$NON-NLS-2$ + outputFormat.setIndent(2); + outputFormat.setLineWidth(100); + outputFormat.setIndenting(true); + outputFormat.setOmitXMLDeclaration(true); + outputFormat.setOmitDocumentType(true); + StringWriter stringWriter = new StringWriter(); + // Using FQN here to avoid having an import above, which will result + // in a deprecation warning, and there isn't a way to annotate a single + // import element with a SuppressWarnings. + org.apache.xml.serialize.XMLSerializer serializer = + new org.apache.xml.serialize.XMLSerializer(stringWriter, outputFormat); + serializer.setNamespaces(true); + try { + serializer.serialize(document.getDocumentElement()); + return stringWriter.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + private static TestNode createFromNode(Element element) { + String fqcn = ANDROID_WIDGET_PREFIX + element.getTagName(); + TestNode node = new TestXmlNode(fqcn, element); + + for (Element child : DomUtilities.getChildren(element)) { + node.add(createFromNode(child)); + } + + return node; + } + + @Nullable + public static TestNode findById(TestNode node, String id) { + id = BaseLayoutRule.stripIdPrefix(id); + return node.findById(id); + } + + private TestNode findById(String targetId) { + String id = getStringAttr(ANDROID_URI, ATTR_ID); + if (id != null && targetId.equals(BaseLayoutRule.stripIdPrefix(id))) { + return this; + } + + for (TestNode child : mChildren) { + TestNode result = child.findById(targetId); + if (result != null) { + return result; + } + } + + return null; + } + + private static String getTagName(String fqcn) { + return fqcn.substring(fqcn.lastIndexOf('.') + 1); + } + + private static class TestXmlNode extends TestNode { + private final Element mElement; + + public TestXmlNode(String fqcn, Element element) { + super(fqcn); + mElement = element; + } + + @Override + public @NonNull IAttribute[] getLiveAttributes() { + List<IAttribute> result = new ArrayList<IAttribute>(); + + NamedNodeMap attributes = mElement.getAttributes(); + for (int i = 0, n = attributes.getLength(); i < n; i++) { + Attr attribute = (Attr) attributes.item(i); + result.add(new TestXmlAttribute(attribute)); + } + return result.toArray(new IAttribute[result.size()]); + } + + @Override + public boolean setAttribute(String uri, String localName, String value) { + if (value == null) { + mElement.removeAttributeNS(uri, localName); + } else { + mElement.setAttributeNS(uri, localName, value); + } + return super.setAttribute(uri, localName, value); + } + + @Override + public INode appendChild(String viewFqcn) { + Element child = mElement.getOwnerDocument().createElement(getTagName(viewFqcn)); + mElement.appendChild(child); + return new TestXmlNode(viewFqcn, child); + } + + @Override + public INode insertChildAt(String viewFqcn, int index) { + if (index == -1) { + return appendChild(viewFqcn); + } + Element child = mElement.getOwnerDocument().createElement(getTagName(viewFqcn)); + List<Element> children = DomUtilities.getChildren(mElement); + if (children.size() >= index) { + Element before = children.get(index); + mElement.insertBefore(child, before); + } else { + fail("Unexpected index"); + mElement.appendChild(child); + } + return new TestXmlNode(viewFqcn, child); + } + + @Override + public String getStringAttr(String uri, String name) { + String value; + if (uri == null) { + value = mElement.getAttribute(name); + } else { + value = mElement.getAttributeNS(uri, name); + } + if (value.isEmpty()) { + value = null; + } + + return value; + } + + @Override + public void removeChild(INode node) { + assert node instanceof TestXmlNode; + mElement.removeChild(((TestXmlNode) node).mElement); + } + + @Override + public void removeChild(int index) { + List<Element> children = DomUtilities.getChildren(mElement); + assertTrue(index < children.size()); + Element oldChild = children.get(index); + mElement.removeChild(oldChild); + } + } + + public static class TestXmlAttribute implements IAttribute { + private Attr mAttribute; + + public TestXmlAttribute(Attr attribute) { + this.mAttribute = attribute; + } + + @Override + public String getUri() { + return mAttribute.getNamespaceURI(); + } + + @Override + public String getName() { + String name = mAttribute.getLocalName(); + if (name == null) { + name = mAttribute.getName(); + } + return name; + } + + @Override + public String getValue() { + return mAttribute.getValue(); + } + } + + // Recursively initialize this node with the bounds specified in the given hierarchy + // dump (from ViewHierarchy's DUMP_INFO flag + public void assignBounds(String bounds) { + Iterable<String> split = Splitter.on('\n').trimResults().split(bounds); + assignBounds(split.iterator()); + } + + private void assignBounds(Iterator<String> iterator) { + assertTrue(iterator.hasNext()); + String desc = iterator.next(); + + Pattern pattern = Pattern.compile("^\\s*(.+)\\s+\\[(.+)\\]\\s*(<.+>)?\\s*(\\S+)?\\s*$"); + Matcher matcher = pattern.matcher(desc); + assertTrue(matcher.matches()); + String fqn = matcher.group(1); + assertEquals(getFqcn(), fqn); + String boundsString = matcher.group(2); + String[] bounds = boundsString.split(","); + assertEquals(boundsString, 4, bounds.length); + try { + int left = Integer.parseInt(bounds[0]); + int top = Integer.parseInt(bounds[1]); + int right = Integer.parseInt(bounds[2]); + int bottom = Integer.parseInt(bounds[3]); + mBounds = new Rect(left, top, right - left, bottom - top); + } catch (NumberFormatException nufe) { + fail(nufe.getLocalizedMessage()); + } + String tag = matcher.group(3); + + for (INode child : getChildren()) { + assertTrue(iterator.hasNext()); + ((TestNode) child).assignBounds(iterator); + } + } +} 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 000000000..daa487ed4 --- /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 LayoutTestBase { + 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, null/*targetView*/, elements); + // Zoom controls don't respond to drags + assertNull(feedback); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/grid/GridModelTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/grid/GridModelTest.java new file mode 100644 index 000000000..c4a58c6e4 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/grid/GridModelTest.java @@ -0,0 +1,852 @@ +/* + * Copyright (C) 2011 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.grid; + +import static com.android.SdkConstants.ANDROID_URI; +import static com.android.SdkConstants.ATTR_COLUMN_COUNT; +import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN; +import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN_SPAN; +import static com.android.SdkConstants.ATTR_LAYOUT_ROW; +import static com.android.SdkConstants.FQCN_BUTTON; + +import com.android.ide.common.api.INode; +import com.android.ide.common.api.Rect; +import com.android.ide.common.layout.LayoutTestBase; +import com.android.ide.common.layout.TestNode; +import com.android.ide.common.layout.grid.GridModel.ViewData; + +import java.util.Arrays; +import java.util.Collections; + + +@SuppressWarnings("javadoc") +public class GridModelTest extends LayoutTestBase { + public void testRemoveFlag() { + assertEquals("left", GridModel.removeFlag("top", "top|left")); + assertEquals("left", GridModel.removeFlag("top", "top | left")); + assertEquals("top", GridModel.removeFlag("left", "top|left")); + assertEquals("top", GridModel.removeFlag("left", "top | left")); + assertEquals("left | center", GridModel.removeFlag("top", "top | left | center")); + assertEquals(null, GridModel.removeFlag("top", "top")); + } + + public void testReadModel1() { + TestNode targetNode = TestNode.create("android.widget.GridLayout").id("@+id/GridLayout1") + .bounds(new Rect(0, 0, 240, 480)).set(ANDROID_URI, ATTR_COLUMN_COUNT, "3"); + + GridModel model = GridModel.get(null, targetNode, null); + assertEquals(3, model.declaredColumnCount); + assertEquals(1, model.actualColumnCount); + assertEquals(1, model.actualRowCount); + + targetNode.add(TestNode.create(FQCN_BUTTON).id("@+id/Button1")); + targetNode.add(TestNode.create(FQCN_BUTTON).id("@+id/Button2")); + targetNode.add(TestNode.create(FQCN_BUTTON).id("@+id/Button3")); + targetNode.add(TestNode.create(FQCN_BUTTON).id("@+id/Button4")); + + model = GridModel.get(null, targetNode, null); + assertEquals(3, model.declaredColumnCount); + assertEquals(3, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + } + + public void testSplitColumn() { + TestNode targetNode = TestNode.create("android.widget.GridLayout").id("@+id/GridLayout1") + .bounds(new Rect(0, 0, 240, 480)).set(ANDROID_URI, ATTR_COLUMN_COUNT, "3"); + TestNode b1 = TestNode.create(FQCN_BUTTON).id("@+id/Button1"); + TestNode b2 = TestNode.create(FQCN_BUTTON).id("@+id/Button2"); + TestNode b3 = TestNode.create(FQCN_BUTTON).id("@+id/Button3"); + TestNode b4 = TestNode.create(FQCN_BUTTON).id("@+id/Button4"); + targetNode.add(b1); + targetNode.add(b2); + targetNode.add(b3); + targetNode.add(b4); + b4.setAttribute(ANDROID_URI, ATTR_LAYOUT_COLUMN_SPAN, "2"); + + GridModel model = GridModel.get(new LayoutTestBase.TestRulesEngine(targetNode.getFqcn()), + targetNode, null); + assertEquals(3, model.declaredColumnCount); + assertEquals(3, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + + model.applyPositionAttributes(); + assertEquals("0", b1.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN)); + assertEquals("0", b1.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW)); + + assertEquals("1", b2.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN)); + assertEquals("0", b2.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW)); + + assertEquals("2", b3.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN)); + assertEquals("0", b3.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW)); + + assertEquals("0", b4.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN)); + assertEquals("1", b4.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW)); + assertEquals("2", b4.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN_SPAN)); + + model.splitColumn(1, false /*insertMarginColumn*/, 100 /*columnWidthDp*/, 300 /* x */); + model.applyPositionAttributes(); + + assertEquals(4, model.declaredColumnCount); + assertEquals(4, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + + assertEquals("0", b1.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN)); + assertEquals("0", b1.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW)); + + assertEquals("1", b2.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN)); + assertEquals("2", b2.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN_SPAN)); + assertEquals("0", b2.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW)); + + assertEquals("3", b3.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN)); + assertEquals("0", b3.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW)); + + assertEquals("0", b4.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN)); + assertEquals("1", b4.getStringAttr(ANDROID_URI, ATTR_LAYOUT_ROW)); + assertEquals("3", b4.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN_SPAN)); + } + + public void testDeletion1() { + String xml = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<GridLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " android:columnCount=\"4\" >\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_column=\"1\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <TextView\n" + + " android:id=\"@+id/TextView1\"\n" + + " android:layout_column=\"3\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\"\n" + + " android:text=\"Text\" />\n" + + "\n" + + " <Space\n" + + " android:id=\"@+id/wspace1\"\n" + + " android:layout_width=\"21dp\"\n" + + " android:layout_height=\"1dp\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + "\n" + + " <Space\n" + + " android:id=\"@+id/hspace1\"\n" + + " android:layout_width=\"1dp\"\n" + + " android:layout_height=\"55dp\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + "\n" + + " <Space\n" + + " android:id=\"@+id/wspace2\"\n" + + " android:layout_width=\"10dp\"\n" + + " android:layout_height=\"1dp\"\n" + + " android:layout_column=\"2\"\n" + + " android:layout_row=\"0\" />\n" + + "\n" + + "</GridLayout>"; + + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + + TestNode button1 = TestNode.findById(targetNode, "@+id/button1"); + TestNode textView1 = TestNode.findById(targetNode, "@+id/TextView1"); + TestNode wspace1 = TestNode.findById(targetNode, "@+id/wspace1"); + TestNode wspace2 = TestNode.findById(targetNode, "@+id/wspace2"); + TestNode hspace1 = TestNode.findById(targetNode, "@+id/hspace1"); + assertNotNull(wspace1); + assertNotNull(hspace1); + assertNotNull(wspace2); + assertNotNull(button1); + assertNotNull(textView1); + + // Assign some bounds such that the model makes sense when merging spacer sizes + // TODO: MAke test utility method to automatically assign half divisions!! + button1.bounds(new Rect(90, 10, 100, 40)); + textView1.bounds(new Rect(200, 10, 100, 40)); + wspace1.bounds(new Rect(0, 0, 90, 1)); + wspace1.bounds(new Rect(190, 0, 10, 1)); + hspace1.bounds(new Rect(0, 0, 1, 10)); + + GridModel model = GridModel.get(new LayoutTestBase.TestRulesEngine(targetNode.getFqcn()), + targetNode, null); + assertEquals(4, model.declaredColumnCount); + assertEquals(4, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + + ViewData textViewData = model.getView(textView1); + assertEquals(3, textViewData.column); + + // Delete button1 + button1.getParent().removeChild(button1); + model.onDeleted(Arrays.<INode>asList(button1)); + model.applyPositionAttributes(); + + assertEquals(2, model.declaredColumnCount); + assertEquals(2, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + assertNotNull(model.getView(textView1)); + assertNull(model.getView(button1)); + + assertEquals( + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<GridLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " android:columnCount=\"2\">\n" + + "\n" + + " <TextView\n" + + " android:id=\"@+id/TextView1\"\n" + + " android:layout_column=\"1\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\"\n" + + " android:text=\"Text\">\n" + + " </TextView>\n" + + "\n" + + " <Space\n" + + " android:id=\"@+id/wspace1\"\n" + + " android:layout_width=\"66dp\"\n" + + " android:layout_height=\"1dp\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_row=\"0\">\n" + + " </Space>\n" + + "\n" + + " <Space\n" + + " android:id=\"@+id/hspace1\"\n" + + " android:layout_width=\"1dp\"\n" + + " android:layout_height=\"55dp\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_row=\"0\">\n" + + " </Space>\n" + + "\n" + + "</GridLayout>", TestNode.toXml(targetNode)); + + // Delete textView1 + + textView1.getParent().removeChild(textView1); + model.onDeleted(Arrays.<INode>asList(textView1)); + model.applyPositionAttributes(); + + assertEquals(2, model.declaredColumnCount); + assertEquals(0, model.actualColumnCount); + assertEquals(0, model.actualRowCount); + assertNull(model.getView(textView1)); + assertNull(model.getView(button1)); + + assertEquals( + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<GridLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " android:columnCount=\"0\">\n" + + "\n" + + "</GridLayout>", TestNode.toXml(targetNode)); + + } + + public void testDelete2() throws Exception { + String xml = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<GridLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " android:columnCount=\"4\"\n" + + " android:orientation=\"vertical\" >\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_columnSpan=\"2\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"0\"\n" + + " android:text=\"Button1\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button3\"\n" + + " android:layout_column=\"1\"\n" + + " android:layout_columnSpan=\"2\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\"\n" + + " android:text=\"Button2\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button2\"\n" + + " android:layout_column=\"2\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"0\"\n" + + " android:text=\"Button3\" />\n" + + "\n" + + " <Space\n" + + " android:id=\"@+id/spacer_177\"\n" + + " android:layout_width=\"46dp\"\n" + + " android:layout_height=\"1dp\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + "\n" + + "</GridLayout>"; + + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + + TestNode button1 = TestNode.findById(targetNode, "@+id/button1"); + TestNode button2 = TestNode.findById(targetNode, "@+id/button2"); + TestNode button3 = TestNode.findById(targetNode, "@+id/button3"); + TestNode hspacer = TestNode.findById(targetNode, "@+id/spacer_177"); + assertNotNull(button1); + assertNotNull(button2); + assertNotNull(button3); + assertNotNull(hspacer); + + // Assign some bounds such that the model makes sense when merging spacer sizes + // TODO: MAke test utility method to automatically assign half divisions!! + button1.bounds(new Rect(0, 0, 100, 40)); + button2.bounds(new Rect(100, 0, 100, 40)); + button3.bounds(new Rect(50, 40, 100, 40)); + hspacer.bounds(new Rect(0, 0, 50, 1)); + + GridModel model = GridModel.get(new LayoutTestBase.TestRulesEngine(targetNode.getFqcn()), + targetNode, null); + assertEquals(4, model.declaredColumnCount); + assertEquals(3, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + + ViewData buttonData = model.getView(button1); + assertEquals(0, buttonData.column); + + // Delete button1 + button1.getParent().removeChild(button1); + model.onDeleted(Arrays.<INode>asList(button1)); + model.applyPositionAttributes(); + + assertEquals(3, model.declaredColumnCount); + assertEquals(3, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + assertNull(model.getView(button1)); + + assertEquals( + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<GridLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " android:columnCount=\"3\"\n" + + " android:orientation=\"vertical\">\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button3\"\n" + + " android:layout_column=\"1\"\n" + + " android:layout_columnSpan=\"2\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\"\n" + + " android:text=\"Button2\">\n" + + " </Button>\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button2\"\n" + + " android:layout_column=\"2\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"0\"\n" + + " android:text=\"Button3\">\n" + + " </Button>\n" + + "\n" + + " <Space\n" + + " android:id=\"@+id/spacer_177\"\n" + + " android:layout_width=\"46dp\"\n" + + " android:layout_height=\"1dp\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_row=\"0\">\n" + + " </Space>\n" + + "\n" + + "</GridLayout>", TestNode.toXml(targetNode)); + } + + public void testDelete3_INCOMPLETE() throws Exception { + String xml = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<GridLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:layout_width=\"match_parent\" android:layout_height=\"match_parent\"\n" + + " android:columnCount=\"6\">\n" + + " <Button android:id=\"@+id/button1\" android:layout_column=\"1\"\n" + + " android:layout_columnSpan=\"2\" android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\" android:layout_rowSpan=\"2\" android:text=\"Button\" />\n" + + " <TextView android:id=\"@+id/TextView1\" android:layout_column=\"4\"\n" + + " android:layout_gravity=\"left|top\" android:layout_row=\"1\"\n" + + " android:text=\"Text\" />\n" + + " <Button android:id=\"@+id/button3\" android:layout_column=\"5\"\n" + + " android:layout_gravity=\"left|top\" android:layout_row=\"2\"\n" + + " android:layout_rowSpan=\"2\" android:text=\"Button\" />\n" + + " <Button android:id=\"@+id/button2\" android:layout_column=\"2\"\n" + + " android:layout_columnSpan=\"3\" android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"4\" android:text=\"Button\" />\n" + + " <Space android:id=\"@+id/wspace1\" android:layout_width=\"21dp\"\n" + + " android:layout_height=\"1dp\" android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + " <Space android:id=\"@+id/spacer_630\" android:layout_width=\"1dp\"\n" + + " android:layout_height=\"55dp\" android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + " <Space android:id=\"@+id/wspace2\" android:layout_width=\"10dp\"\n" + + " android:layout_height=\"1dp\" android:layout_column=\"3\"\n" + + " android:layout_row=\"0\" />\n" + + " <Space android:id=\"@+id/spacer_619\" android:layout_width=\"59dp\"\n" + + " android:layout_height=\"1dp\" android:layout_column=\"1\"\n" + + " android:layout_row=\"0\" />\n" + + " <Space android:id=\"@+id/spacer_102\" android:layout_width=\"1dp\"\n" + + " android:layout_height=\"30dp\" android:layout_column=\"0\"\n" + + " android:layout_row=\"1\" />\n" + + " <Space android:id=\"@+id/spacer_109\" android:layout_width=\"1dp\"\n" + + " android:layout_height=\"28dp\" android:layout_column=\"0\"\n" + + " android:layout_row=\"2\" />\n" + + " <Space android:id=\"@+id/spacer_146\" android:layout_width=\"1dp\"\n" + + " android:layout_height=\"70dp\" android:layout_column=\"0\"\n" + + " android:layout_row=\"3\" />\n" + + "</GridLayout>"; + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + targetNode.assignBounds( + "android.widget.GridLayout [0,109,480,800] <GridLayout>\n" + + " android.widget.Button [32,83,148,155] <Button> @+id/button1\n" + + " android.widget.TextView [163,83,205,109] <TextView> @+id/TextView1\n" + + " android.widget.Button [237,128,353,200] <Button> @+id/button3\n" + + " android.widget.Button [121,275,237,347] <Button> @+id/button2\n" + + " android.widget.Space [0,0,32,2] <Space> @+id/wspace1\n" + + " android.widget.Space [0,0,2,83] <Space> @+id/spacer_630\n" + + " android.widget.Space [148,0,163,2] <Space> @+id/wspace2\n" + + " android.widget.Space [32,0,121,2] <Space> @+id/spacer_619\n" + + " android.widget.Space [0,83,2,128] <Space> @+id/spacer_102\n" + + " android.widget.Space [0,128,2,170] <Space> @+id/spacer_109\n" + + " android.widget.Space [0,170,2,275] <Space> @+id/spacer_146\n"); + TestNode layout = TestNode.findById(targetNode, "@+id/GridLayout1"); + //TestNode button1 = TestNode.findById(targetNode, "@+id/button1"); + TestNode button2 = TestNode.findById(targetNode, "@+id/button2"); + TestNode button3 = TestNode.findById(targetNode, "@+id/button3"); + + GridModel model = GridModel.get(new LayoutTestBase.TestRulesEngine(targetNode.getFqcn()), + targetNode, null); + assertEquals(6, model.declaredColumnCount); + assertEquals(6, model.actualColumnCount); + assertEquals(5, model.actualRowCount); + + // TODO: Delete button2 or button3: bad stuff happens visually + fail("Finish test"); + } + + public void testDelete4_INCOMPLETE() { + String xml = "" + + "<GridLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " xmlns:tools=\"http://schemas.android.com/tools\" " + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\" android:columnCount=\"3\"\n" + + " android:gravity=\"center\" android:text=\"@string/hello_world\"\n" + + " tools:context=\".MainActivity\">\n" + + " <Button android:id=\"@+id/button2\" android:layout_column=\"1\"\n" + + " android:layout_columnSpan=\"2\" android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\" android:text=\"Button\" />\n" + + " <Button android:id=\"@+id/button1\" android:layout_column=\"1\"\n" + + " android:layout_columnSpan=\"2\" android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"3\" android:text=\"Button\" />\n" + + " <Space android:id=\"@+id/spacer_167\" android:layout_width=\"74dp\"\n" + + " android:layout_height=\"1dp\" android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + " <Space android:id=\"@+id/spacer_133\" android:layout_width=\"1dp\"\n" + + " android:layout_height=\"21dp\" android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + " <Space android:id=\"@+id/spacer_142\" android:layout_width=\"1dp\"\n" + + " android:layout_height=\"26dp\" android:layout_column=\"0\"\n" + + " android:layout_row=\"2\" />\n" + + " <Space android:id=\"@+id/spacer_673\" android:layout_width=\"43dp\"\n" + + " android:layout_height=\"1dp\" android:layout_column=\"1\"\n" + + " android:layout_row=\"0\" />\n" + + " <Space android:id=\"@+id/spacer_110\" android:layout_width=\"202dp\"\n" + + " android:layout_height=\"15dp\" android:layout_column=\"2\" />\n" + + "</GridLayout>"; + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + targetNode.assignBounds( + "android.widget.GridLayout [0,109,480,800] <GridLayout>\n" + + " android.widget.Button [111,32,227,104] <Button> @+id/button2\n" + + " android.widget.Button [111,143,227,215] <Button> @+id/button1\n" + + " android.widget.Space [0,0,111,2] <Space> @+id/spacer_167\n" + + " android.widget.Space [0,0,2,32] <Space> @+id/spacer_133\n" + + " android.widget.Space [0,104,2,143] <Space> @+id/spacer_142\n" + + " android.widget.Space [111,0,176,2] <Space> @+id/spacer_673\n" + + " android.widget.Space [176,668,479,691] <Space> @+id/spacer_110"); + + + // Remove button2; button1 shifts to the right! + + //TestNode layout = TestNode.findById(targetNode, "@+id/GridLayout1"); + //TestNode button1 = TestNode.findById(targetNode, "@+id/button1"); + TestNode button2 = TestNode.findById(targetNode, "@+id/button2"); + TestNode button3 = TestNode.findById(targetNode, "@+id/button3"); + assertEquals(new Rect(111, 32, 227 - 111, 104 - 32), button2.getBounds()); + + GridModel model = GridModel.get(new LayoutTestBase.TestRulesEngine(targetNode.getFqcn()), + targetNode, null); + assertEquals(3, model.declaredColumnCount); + assertEquals(3, model.actualColumnCount); + assertEquals(4, model.actualRowCount); + fail("Finish test"); + } + + public void testDelete5_INCOMPLETE() { + String xml = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<GridLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:id=\"@+id/GridLayout1\" android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\" android:columnCount=\"4\"\n" + + " android:orientation=\"vertical\">\n" + + " <Button android:id=\"@+id/button1\" android:layout_column=\"0\"\n" + + " android:layout_gravity=\"center_horizontal|bottom\"\n" + + " android:layout_row=\"0\" android:text=\"Button\" />\n" + + " <Space android:layout_width=\"66dp\" android:layout_height=\"1dp\"\n" + + " android:layout_column=\"0\" android:layout_row=\"0\" />\n" + + " <Button android:id=\"@+id/button3\" android:layout_column=\"2\"\n" + + " android:layout_gravity=\"left|bottom\" android:layout_row=\"0\"\n" + + " android:text=\"Button\" />\n" + + " <Button android:id=\"@+id/button2\" android:layout_column=\"3\"\n" + + " android:layout_columnSpan=\"2\" android:layout_gravity=\"left|bottom\"\n" + + " android:layout_row=\"0\" android:text=\"Button\" />\n" + + " <Space android:id=\"@+id/spacer_109\" android:layout_width=\"51dp\"\n" + + " android:layout_height=\"1dp\" android:layout_column=\"1\"\n" + + " android:layout_row=\"0\" />\n" + + " <Space android:layout_width=\"129dp\" android:layout_height=\"1dp\"\n" + + " android:layout_column=\"2\" android:layout_row=\"0\" />\n" + + " <Space android:layout_width=\"62dp\" android:layout_height=\"1dp\"\n" + + " android:layout_column=\"3\" android:layout_row=\"0\" />\n" + + " <Space android:id=\"@+id/spacer_397\" android:layout_width=\"1dp\"\n" + + " android:layout_height=\"103dp\" android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + " <Space android:layout_width=\"1dp\" android:layout_height=\"356dp\"\n" + + " android:layout_column=\"0\" android:layout_row=\"1\" />\n" + + "</GridLayout>"; + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + + targetNode.assignBounds( + "android.widget.GridLayout [0,109,480,800] <GridLayout> @+id/GridLayout1\n" + + " android.widget.Button [0,83,116,155] <Button> @+id/button1\n" + + " android.widget.Space [0,0,99,2] <Space>\n" + + " android.widget.Button [193,83,309,155] <Button> @+id/button3\n" + + " android.widget.Button [387,83,503,155] <Button> @+id/button2\n" + + " android.widget.Space [116,0,193,2] <Space> @+id/spacer_109\n" + + " android.widget.Space [193,0,387,2] <Space>\n" + + " android.widget.Space [387,0,480,2] <Space>\n" + + " android.widget.Space [0,0,2,155] <Space> @+id/spacer_397\n" + + " android.widget.Space [0,155,2,689] <Space>"); + + // Delete button3. This causes an array out of bounds exception currently. + + //TestNode layout = TestNode.findById(targetNode, "@+id/GridLayout1"); + //TestNode button1 = TestNode.findById(targetNode, "@+id/button1"); + TestNode button2 = TestNode.findById(targetNode, "@+id/button2"); + TestNode button3 = TestNode.findById(targetNode, "@+id/button3"); + assertEquals(new Rect(387, 83, 503 - 387, 155- 83), button2.getBounds()); + + GridModel model = GridModel.get(new LayoutTestBase.TestRulesEngine(targetNode.getFqcn()), + targetNode, null); + assertEquals(4, model.declaredColumnCount); + assertEquals(4, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + + model.onDeleted(Collections.<INode>singletonList(button3)); + // Exception fixed; todo: Test that the model updates are correct. + + assertEquals(3, model.declaredColumnCount); + assertEquals(3, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + + fail("Finish test"); + } + + public void testInsert1() throws Exception { + String xml = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<GridLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:id=\"@+id/GridLayout1\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " android:columnCount=\"4\"\n" + + " android:orientation=\"vertical\" >\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_columnSpan=\"4\"\n" + + " android:layout_gravity=\"center_horizontal|bottom\"\n" + + " android:layout_row=\"0\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button2\"\n" + + " android:layout_column=\"2\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button3\"\n" + + " android:layout_column=\"3\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <Space\n" + + " android:id=\"@+id/spacer_393\"\n" + + " android:layout_width=\"81dp\"\n" + + " android:layout_height=\"1dp\"\n" + + " android:layout_column=\"1\"\n" + + " android:layout_row=\"0\" />\n" + + "\n" + + " <Space\n" + + " android:id=\"@+id/spacer_397\"\n" + + " android:layout_width=\"1dp\"\n" + + " android:layout_height=\"103dp\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + "\n" + + "</GridLayout>"; + + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + + TestNode layout = TestNode.findById(targetNode, "@+id/GridLayout1"); + TestNode button1 = TestNode.findById(targetNode, "@+id/button1"); + TestNode button2 = TestNode.findById(targetNode, "@+id/button2"); + TestNode button3 = TestNode.findById(targetNode, "@+id/button3"); + TestNode hspacer = TestNode.findById(targetNode, "@+id/spacer_393"); + TestNode vspacer = TestNode.findById(targetNode, "@+id/spacer_397"); + assertNotNull(layout); + assertNotNull(button1); + assertNotNull(button2); + assertNotNull(button3); + assertNotNull(hspacer); + + // Obtained by setting ViewHierarchy.DUMP_INFO=true: + layout.bounds(new Rect(0, 109, 480, 800-109)); + button1.bounds(new Rect(182, 83, 298-182, 155-83)); + button2.bounds(new Rect(124, 155, 240-124, 227-155)); + button3.bounds(new Rect(240, 155, 356-240, 227-155)); + hspacer.bounds(new Rect(2, 0, 124-2, 2)); + vspacer.bounds(new Rect(0, 0, 2, 155)); + + GridModel model = GridModel.get(new LayoutTestBase.TestRulesEngine(targetNode.getFqcn()), + targetNode, null); + assertEquals(4, model.declaredColumnCount); + assertEquals(4, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + + + model.splitColumn(1, false, 21, 32); + int index = model.getInsertIndex(2, 1); + GridModel.ViewData next = model.getView(index); + INode newChild = targetNode.insertChildAt(FQCN_BUTTON, index); + next.applyPositionAttributes(); + model.setGridAttribute(newChild, ATTR_LAYOUT_COLUMN, 1); + model.setGridAttribute(newChild, ATTR_LAYOUT_COLUMN_SPAN, 3); + } + + public void testInsert2() throws Exception { + // Drop into a view where there is a centered view: when dropping to the right of + // it (on a row further down), ensure that the row span is increased for the + // non-left-justified centered view which does not horizontally overlap the view + String xml = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<GridLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:id=\"@+id/GridLayout1\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " android:columnCount=\"3\"\n" + + " android:orientation=\"vertical\" >\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_columnSpan=\"3\"\n" + + " android:layout_gravity=\"center_horizontal|bottom\"\n" + + " android:layout_row=\"0\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button2\"\n" + + " android:layout_column=\"1\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button3\"\n" + + " android:layout_column=\"2\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <Space\n" + + " android:id=\"@+id/spacer_393\"\n" + + " android:layout_width=\"81dp\"\n" + + " android:layout_height=\"1dp\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + "\n" + + " \n" + + " <Space\n" + + " android:id=\"@+id/spacer_397\"\n" + + " android:layout_width=\"1dp\"\n" + + " android:layout_height=\"103dp\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + "\n" + + " \n" + + "</GridLayout>"; + + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + + TestNode layout = TestNode.findById(targetNode, "@+id/GridLayout1"); + TestNode button1 = TestNode.findById(targetNode, "@+id/button1"); + TestNode button2 = TestNode.findById(targetNode, "@+id/button2"); + TestNode button3 = TestNode.findById(targetNode, "@+id/button3"); + TestNode hspacer = TestNode.findById(targetNode, "@+id/spacer_393"); + TestNode vspacer = TestNode.findById(targetNode, "@+id/spacer_397"); + assertNotNull(layout); + assertNotNull(button1); + assertNotNull(button2); + assertNotNull(button3); + assertNotNull(hspacer); + + // Obtained by setting ViewHierarchy.DUMP_INFO=true: + layout.bounds(new Rect(0, 109, 480, 800-109)); + button1.bounds(new Rect(182, 83, 298-182, 155-83)); + button2.bounds(new Rect(122, 155, 238-122, 227-155)); + button3.bounds(new Rect(238, 155, 354-238, 227-155)); + hspacer.bounds(new Rect(0, 0, 122, 2)); + vspacer.bounds(new Rect(0, 0, 2, 155)); + + GridModel model = GridModel.get(new LayoutTestBase.TestRulesEngine(targetNode.getFqcn()), + targetNode, null); + assertEquals(3, model.declaredColumnCount); + assertEquals(3, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + + ViewData view = model.getView(button1); + assertNotNull(view); + assertEquals(0, view.column); + assertEquals(3, view.columnSpan); + assertEquals("3", view.node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN_SPAN)); + + model.splitColumn(3, false, 53, 318); + assertEquals(0, view.column); + assertEquals(4, view.columnSpan); + assertEquals("4", view.node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN_SPAN)); + } + + public void testInsert3_BROKEN() throws Exception { + // Check that when we insert a new gap column near an existing column, the + // view in that new column does not get moved + String xml = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<GridLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " android:id=\"@+id/GridLayout1\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " android:columnCount=\"3\"\n" + + " android:orientation=\"vertical\" >\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_columnSpan=\"3\"\n" + + " android:layout_gravity=\"center_horizontal|bottom\"\n" + + " android:layout_row=\"0\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button2\"\n" + + " android:layout_column=\"1\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button3\"\n" + + " android:layout_column=\"2\"\n" + + " android:layout_gravity=\"left|top\"\n" + + " android:layout_row=\"1\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <Space\n" + + " android:id=\"@+id/spacer_393\"\n" + + " android:layout_width=\"81dp\"\n" + + " android:layout_height=\"1dp\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + "\n" + + " \n" + + " <Space\n" + + " android:id=\"@+id/spacer_397\"\n" + + " android:layout_width=\"1dp\"\n" + + " android:layout_height=\"103dp\"\n" + + " android:layout_column=\"0\"\n" + + " android:layout_row=\"0\" />\n" + + "\n" + + " \n" + + "</GridLayout>"; + + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + + TestNode layout = TestNode.findById(targetNode, "@+id/GridLayout1"); + TestNode button1 = TestNode.findById(targetNode, "@+id/button1"); + TestNode button2 = TestNode.findById(targetNode, "@+id/button2"); + TestNode button3 = TestNode.findById(targetNode, "@+id/button3"); + TestNode hspacer = TestNode.findById(targetNode, "@+id/spacer_393"); + TestNode vspacer = TestNode.findById(targetNode, "@+id/spacer_397"); + assertNotNull(layout); + assertNotNull(button1); + assertNotNull(button2); + assertNotNull(button3); + assertNotNull(hspacer); + + // Obtained by setting ViewHierarchy.DUMP_INFO=true: + layout.bounds(new Rect(0, 109, 480, 800-109)); + button1.bounds(new Rect(182, 83, 298-182, 155-83)); + button2.bounds(new Rect(122, 155, 238-122, 227-155)); + button3.bounds(new Rect(238, 155, 354-238, 227-155)); + hspacer.bounds(new Rect(0, 0, 122, 2)); + vspacer.bounds(new Rect(0, 0, 2, 155)); + + GridModel model = GridModel.get(new LayoutTestBase.TestRulesEngine(targetNode.getFqcn()), + targetNode, null); + assertEquals(3, model.declaredColumnCount); + assertEquals(3, model.actualColumnCount); + assertEquals(2, model.actualRowCount); + + ViewData view = model.getView(button3); + assertNotNull(view); + assertEquals(2, view.column); + assertEquals(1, view.columnSpan); + assertNull("1", view.node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN_SPAN)); + + model.splitColumn(2, true, 10, 253); + // TODO: Finish this test: Assert that the cells are in the right place + //assertEquals(4, view.column); + //assertEquals(1, view.columnSpan); + //assertEquals("4", view.node.getStringAttr(ANDROID_URI, ATTR_LAYOUT_COLUMN_SPAN)); + fail("Finish test"); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/relative/DeletionHandlerTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/relative/DeletionHandlerTest.java new file mode 100644 index 000000000..d629a3897 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/relative/DeletionHandlerTest.java @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2012 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.relative; + +import static com.android.SdkConstants.ATTR_ID; +import static com.android.SdkConstants.ANDROID_URI; + +import com.android.ide.common.api.INode; +import com.android.ide.common.layout.BaseViewRule; +import com.android.ide.common.layout.TestNode; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class DeletionHandlerTest extends TestCase { + public void testSimple() { + String xml = "" + + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " xmlns:tools=\"http://schemas.android.com/tools\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " tools:ignore=\"HardcodedText\" >\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignParentLeft=\"true\"\n" + + " android:text=\"A\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button2\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBaseline=\"@+id/button1\"\n" + + " android:layout_alignBottom=\"@+id/button1\"\n" + + " android:layout_toRightOf=\"@+id/button1\"\n" + + " android:text=\"B\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button3\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBottom=\"@+id/button2\"\n" + + " android:layout_toRightOf=\"@+id/button2\"\n" + + " android:text=\"C\" />\n" + + "\n" + + "</RelativeLayout>"; + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + + TestNode button2 = TestNode.findById(targetNode, "@+id/button2"); + + INode layout = button2.getParent(); + List<INode> deletedNodes = Collections.<INode>singletonList(button2); + List<INode> movedNodes = Collections.<INode>emptyList(); + assertSame(layout, targetNode); + layout.removeChild(button2); + + DeletionHandler handler = new DeletionHandler(deletedNodes, movedNodes, layout); + handler.updateConstraints(); + + String updated = TestNode.toXml(targetNode); + assertEquals( + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " xmlns:tools=\"http://schemas.android.com/tools\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " tools:ignore=\"HardcodedText\">\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignParentLeft=\"true\"\n" + + " android:text=\"A\">\n" + + " </Button>\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button3\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBaseline=\"@+id/button1\"\n" + + " android:layout_alignBottom=\"@+id/button1\"\n" + + " android:layout_toRightOf=\"@+id/button1\"\n" + + " android:text=\"C\">\n" + + " </Button>\n" + + "\n" + + "</RelativeLayout>", + updated); + assertFalse(updated.contains(BaseViewRule.stripIdPrefix(button2.getStringAttr(ANDROID_URI, + ATTR_ID)))); + } + + public void testTransitive() { + String xml = "" + + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " xmlns:tools=\"http://schemas.android.com/tools\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " tools:ignore=\"HardcodedText\" >\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignParentLeft=\"true\"\n" + + " android:layout_alignParentTop=\"true\"\n" + + " android:text=\"Above\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button2\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignParentLeft=\"true\"\n" + + " android:layout_below=\"@+id/button1\"\n" + + " android:text=\"A\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button3\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBaseline=\"@+id/button2\"\n" + + " android:layout_alignBottom=\"@+id/button2\"\n" + + " android:layout_toRightOf=\"@+id/button2\"\n" + + " android:text=\"B\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button4\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBottom=\"@+id/button3\"\n" + + " android:layout_toRightOf=\"@+id/button3\"\n" + + " android:text=\"C\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button5\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBaseline=\"@+id/button4\"\n" + + " android:layout_alignBottom=\"@+id/button4\"\n" + + " android:layout_toRightOf=\"@+id/button4\"\n" + + " android:text=\"D\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button6\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBottom=\"@+id/button5\"\n" + + " android:layout_toRightOf=\"@+id/button5\"\n" + + " android:text=\"E\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button7\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignLeft=\"@+id/button3\"\n" + + " android:layout_below=\"@+id/button3\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <CheckBox\n" + + " android:id=\"@+id/checkBox1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBaseline=\"@+id/button7\"\n" + + " android:layout_alignBottom=\"@+id/button7\"\n" + + " android:layout_toRightOf=\"@+id/button7\"\n" + + " android:text=\"CheckBox\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button8\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_below=\"@+id/checkBox1\"\n" + + " android:layout_toRightOf=\"@+id/checkBox1\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + "</RelativeLayout>"; + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + TestNode button7 = TestNode.findById(targetNode, "@+id/button7"); + TestNode checkBox = TestNode.findById(targetNode, "@+id/checkBox1"); + + INode layout = button7.getParent(); + List<INode> deletedNodes = Arrays.<INode>asList(button7, checkBox); + List<INode> movedNodes = Collections.<INode>emptyList(); + assertSame(layout, targetNode); + layout.removeChild(button7); + layout.removeChild(checkBox); + + DeletionHandler handler = new DeletionHandler(deletedNodes, movedNodes, layout); + handler.updateConstraints(); + + String updated = TestNode.toXml(targetNode); + assertEquals( + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " xmlns:tools=\"http://schemas.android.com/tools\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " tools:ignore=\"HardcodedText\">\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignParentLeft=\"true\"\n" + + " android:layout_alignParentTop=\"true\"\n" + + " android:text=\"Above\">\n" + + " </Button>\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button2\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignParentLeft=\"true\"\n" + + " android:layout_below=\"@+id/button1\"\n" + + " android:text=\"A\">\n" + + " </Button>\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button3\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBaseline=\"@+id/button2\"\n" + + " android:layout_alignBottom=\"@+id/button2\"\n" + + " android:layout_toRightOf=\"@+id/button2\"\n" + + " android:text=\"B\">\n" + + " </Button>\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button4\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBottom=\"@+id/button3\"\n" + + " android:layout_toRightOf=\"@+id/button3\"\n" + + " android:text=\"C\">\n" + + " </Button>\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button5\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBaseline=\"@+id/button4\"\n" + + " android:layout_alignBottom=\"@+id/button4\"\n" + + " android:layout_toRightOf=\"@+id/button4\"\n" + + " android:text=\"D\">\n" + + " </Button>\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button6\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBottom=\"@+id/button5\"\n" + + " android:layout_toRightOf=\"@+id/button5\"\n" + + " android:text=\"E\">\n" + + " </Button>\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button8\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignLeft=\"@+id/button3\"\n" + + " android:layout_below=\"@+id/button3\"\n" + + " android:text=\"Button\">\n" + + " </Button>\n" + + "\n" + + "</RelativeLayout>", + updated); + assertFalse(updated.contains(BaseViewRule.stripIdPrefix(button7.getStringAttr(ANDROID_URI, + ATTR_ID)))); + } + + public void testCenter() { + String xml = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " xmlns:tools=\"http://schemas.android.com/tools\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " tools:ignore=\"HardcodedText\" >\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_centerInParent=\"true\"\n" + + " android:text=\"Button\" />\n" + + "\n" + + " <CheckBox\n" + + " android:id=\"@+id/checkBox1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_below=\"@+id/button1\"\n" + + " android:layout_toRightOf=\"@+id/button1\"\n" + + " android:text=\"CheckBox\" />\n" + + "\n" + + "</RelativeLayout>"; + + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + TestNode button1 = TestNode.findById(targetNode, "@+id/button1"); + + INode layout = button1.getParent(); + List<INode> deletedNodes = Collections.<INode>singletonList(button1); + List<INode> movedNodes = Collections.<INode>emptyList(); + assertSame(layout, targetNode); + layout.removeChild(button1); + + DeletionHandler handler = new DeletionHandler(deletedNodes, movedNodes, layout); + handler.updateConstraints(); + + String updated = TestNode.toXml(targetNode); + assertEquals( + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " xmlns:tools=\"http://schemas.android.com/tools\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " tools:ignore=\"HardcodedText\">\n" + + "\n" + + " <CheckBox\n" + + " android:id=\"@+id/checkBox1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_centerInParent=\"true\"\n" + + " android:text=\"CheckBox\">\n" + + " </CheckBox>\n" + + "\n" + + "</RelativeLayout>", + updated); + assertFalse(updated.contains(BaseViewRule.stripIdPrefix(button1.getStringAttr(ANDROID_URI, + ATTR_ID)))); + + } + + public void testMove() { + String xml = "" + + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " xmlns:tools=\"http://schemas.android.com/tools\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " tools:ignore=\"HardcodedText\" >\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignParentLeft=\"true\"\n" + + " android:text=\"A\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button2\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBaseline=\"@+id/button1\"\n" + + " android:layout_alignBottom=\"@+id/button1\"\n" + + " android:layout_toRightOf=\"@+id/button1\"\n" + + " android:text=\"B\" />\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button3\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBottom=\"@+id/button2\"\n" + + " android:layout_toRightOf=\"@+id/button2\"\n" + + " android:text=\"C\" />\n" + + "\n" + + "</RelativeLayout>"; + TestNode targetNode = TestNode.createFromXml(xml); + assertNotNull(targetNode); + + TestNode button2 = TestNode.findById(targetNode, "@+id/button2"); + + INode layout = button2.getParent(); + List<INode> deletedNodes = Collections.<INode>singletonList(button2); + List<INode> movedNodes = Collections.<INode>singletonList(button2); + assertSame(layout, targetNode); + + DeletionHandler handler = new DeletionHandler(deletedNodes, movedNodes, layout); + handler.updateConstraints(); + + String updated = TestNode.toXml(targetNode); + assertEquals( + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + + " xmlns:tools=\"http://schemas.android.com/tools\"\n" + + " android:layout_width=\"match_parent\"\n" + + " android:layout_height=\"match_parent\"\n" + + " tools:ignore=\"HardcodedText\">\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignParentLeft=\"true\"\n" + + " android:text=\"A\">\n" + + " </Button>\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button2\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBaseline=\"@+id/button1\"\n" + + " android:layout_alignBottom=\"@+id/button1\"\n" + + " android:layout_toRightOf=\"@+id/button1\"\n" + + " android:text=\"B\">\n" + + " </Button>\n" + + "\n" + + " <Button\n" + + " android:id=\"@+id/button3\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignBottom=\"@+id/button2\"\n" + + " android:layout_toRightOf=\"@+id/button2\"\n" + + " android:text=\"C\">\n" + + " </Button>\n" + + "\n" + + "</RelativeLayout>", + updated); + assertTrue(updated.contains(BaseViewRule.stripIdPrefix(button2.getStringAttr(ANDROID_URI, + ATTR_ID)))); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttributeInfoTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttributeInfoTest.java new file mode 100644 index 000000000..b56292245 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttributeInfoTest.java @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2012 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.resources.platform; + +import static com.android.SdkConstants.ANDROID_URI; +import static com.android.SdkConstants.DOT_XML; + +import com.android.SdkConstants; +import com.android.annotations.NonNull; +import com.android.ide.common.api.IAttributeInfo.Format; +import com.android.ide.common.resources.ResourceItem; +import com.android.ide.common.resources.ResourceRepository; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities; +import com.android.ide.eclipse.mock.Mocks; +import com.android.io.IAbstractFolder; +import com.android.io.IAbstractResource; +import com.android.resources.ResourceType; +import com.android.utils.StdLogger; +import com.google.common.base.Charsets; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.common.io.Files; + +import junit.framework.TestCase; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Map; +import java.util.Set; + +@SuppressWarnings("javadoc") +public class AttributeInfoTest extends TestCase { + public void testSimple() throws Exception { + AttributeInfo info = new AttributeInfo("test", EnumSet.noneOf(Format.class)); + assertTrue(info.isValid("", null, null)); + assertTrue(info.isValid("a b c", null, null)); + assertTrue(info.isValid("@android foo bar", null, null)); + } + + public void testIsValidString() throws Exception { + AttributeInfo info = new AttributeInfo("test", Format.STRING_SET); + assertTrue(info.isValid("", null, null)); + assertTrue(info.isValid("a b c", null, null)); + assertTrue(info.isValid("@android foo bar", null, null)); + } + + public void testIsValidBoolean() throws Exception { + AttributeInfo info = new AttributeInfo("test", Format.BOOLEAN_SET); + assertTrue(info.isValid("true", null, null)); + assertTrue(info.isValid("false", null, null)); + assertFalse(info.isValid("", null, null)); + assertTrue(info.isValid("TRUE", null, null)); + assertTrue(info.isValid("True", null, null)); + assertTrue(info.isValid("FALSE", null, null)); + assertTrue(info.isValid("False", null, null)); + } + + public void testIsValidInteger() throws Exception { + AttributeInfo info = new AttributeInfo("test", Format.INTEGER_SET); + assertTrue(info.isValid("0", null, null)); + assertTrue(info.isValid("1", null, null)); + assertTrue(info.isValid("10", null, null)); + assertTrue(info.isValid("-10", null, null)); + assertTrue(info.isValid(Integer.toString(Integer.MAX_VALUE), null, null)); + + assertFalse(info.isValid("", null, null)); + assertFalse(info.isValid("a", null, null)); + assertFalse(info.isValid("a1", null, null)); + assertFalse(info.isValid("1a", null, null)); + assertFalse(info.isValid("1.0", null, null)); + } + + public void testIsValidFloat() throws Exception { + AttributeInfo info = new AttributeInfo("test", Format.FLOAT_SET); + assertTrue(info.isValid("0", null, null)); + assertTrue(info.isValid("1", null, null)); + assertTrue(info.isValid("10", null, null)); + assertTrue(info.isValid("-10", null, null)); + assertTrue(info.isValid("-10.1234", null, null)); + assertTrue(info.isValid(".1", null, null)); + assertTrue(info.isValid("-.1", null, null)); + assertTrue(info.isValid("1.5e22", null, null)); + assertTrue(info.isValid(Integer.toString(Integer.MAX_VALUE), null, null)); + + assertFalse(info.isValid("", null, null)); + assertFalse(info.isValid(".", null, null)); + assertFalse(info.isValid("-.", null, null)); + assertFalse(info.isValid("a", null, null)); + assertFalse(info.isValid("a1", null, null)); + assertFalse(info.isValid("1a", null, null)); + } + + public void testIsValidDimension() throws Exception { + AttributeInfo info = new AttributeInfo("test", Format.DIMENSION_SET); + assertTrue(info.isValid("0dp", null, null)); + assertTrue(info.isValid("1dp", null, null)); + assertTrue(info.isValid("10dip", null, null)); + assertTrue(info.isValid("-10px", null, null)); + assertTrue(info.isValid("-10.1234mm", null, null)); + assertTrue(info.isValid("14sp", null, null)); + assertTrue(info.isValid("72pt", null, null)); + + assertFalse(info.isValid("", null, null)); + assertFalse(info.isValid("5", null, null)); + assertFalse(info.isValid("50ps", null, null)); + // Since we allow resources even when not specified in format, don't assert + // this: + //assertFalse(info.isValid("@dimen/foo")); + } + + public void testIsValidColor() throws Exception { + AttributeInfo info = new AttributeInfo("test", Format.COLOR_SET); + assertTrue(info.isValid("#fff", null, null)); + assertTrue(info.isValid("#ffffff", null, null)); + assertTrue(info.isValid("#12345678", null, null)); + assertTrue(info.isValid("#abcdef00", null, null)); + + assertFalse(info.isValid("", null, null)); + assertFalse(info.isValid("#fffffffff", null, null)); + assertFalse(info.isValid("red", null, null)); + assertFalse(info.isValid("rgb(1,2,3)", null, null)); + } + + public void testIsValidFraction() throws Exception { + AttributeInfo info = new AttributeInfo("test", EnumSet.<Format>of(Format.FRACTION)); + assertTrue(info.isValid("5%", null, null)); + assertTrue(info.isValid("25%p", null, null)); + + // We don't validate fractions accurately yet + //assertFalse(info.isValid("")); + //assertFalse(info.isValid("50%%")); + //assertFalse(info.isValid("50")); + //assertFalse(info.isValid("-2%")); + } + + public void testIsValidReference() throws Exception { + AttributeInfo info = new AttributeInfo("test", Format.REFERENCE_SET); + assertTrue(info.isValid("@android:string/foo", null, null)); + assertTrue(info.isValid("@string/foo", null, null)); + assertTrue(info.isValid("@dimen/foo", null, null)); + assertTrue(info.isValid("@color/foo", null, null)); + assertTrue(info.isValid("@animator/foo", null, null)); + assertTrue(info.isValid("@anim/foo", null, null)); + assertTrue(info.isValid("?android:attr/textAppearanceMedium", null, null)); + assertTrue(info.isValid("?textAppearanceMedium", null, null)); + + assertFalse(info.isValid("", null, null)); + assertFalse(info.isValid("foo", null, null)); + assertFalse(info.isValid("3.4", null, null)); + } + + public void testIsValidEnum() throws Exception { + AttributeInfo info = new AttributeInfo("test", Format.ENUM_SET); + info.setEnumValues(new String[] { "wrap_content", "match_parent" }); + assertTrue(info.isValid("wrap_content", null, null)); + assertTrue(info.isValid("match_parent", null, null)); + assertFalse(info.isValid("", null, null)); + assertFalse(info.isValid("other", null, null)); + assertFalse(info.isValid("50", null, null)); + } + + public void testIsValidFlag() throws Exception { + AttributeInfo info = new AttributeInfo("test", Format.FLAG_SET); + info.setFlagValues(new String[] { "left", "top", "right", "bottom" }); + assertTrue(info.isValid("left", null, null)); + assertTrue(info.isValid("top", null, null)); + assertTrue(info.isValid("left|top", null, null)); + assertTrue(info.isValid("", null, null)); + + assertFalse(info.isValid("other", null, null)); + assertFalse(info.isValid("50", null, null)); + } + + public void testCombined1() throws Exception { + AttributeInfo info = new AttributeInfo("test", EnumSet.<Format>of(Format.INTEGER, + Format.REFERENCE)); + assertTrue(info.isValid("1", null, null)); + assertTrue(info.isValid("@dimen/foo", null, null)); + assertFalse(info.isValid("foo", null, null)); + } + + public void testCombined2() throws Exception { + AttributeInfo info = new AttributeInfo("test", EnumSet.<Format>of(Format.COLOR, + Format.REFERENCE)); + assertTrue(info.isValid("#ff00ff00", null, null)); + assertTrue(info.isValid("@color/foo", null, null)); + assertFalse(info.isValid("foo", null, null)); + } + + public void testCombined3() throws Exception { + AttributeInfo info = new AttributeInfo("test", EnumSet.<Format>of(Format.STRING, + Format.REFERENCE)); + assertTrue(info.isValid("test", null, null)); + assertTrue(info.isValid("@color/foo", null, null)); + } + + public void testCombined4() throws Exception { + AttributeInfo info = new AttributeInfo("test", EnumSet.<Format>of(Format.ENUM, + Format.DIMENSION)); + info.setEnumValues(new String[] { "wrap_content", "match_parent" }); + assertTrue(info.isValid("wrap_content", null, null)); + assertTrue(info.isValid("match_parent", null, null)); + assertTrue(info.isValid("50dp", null, null)); + assertFalse(info.isValid("50", null, null)); + assertFalse(info.isValid("test", null, null)); + } + + public void testResourcesExist() throws Exception { + IAbstractFolder folder = Mocks.createAbstractFolder( + SdkConstants.FD_RESOURCES, new IAbstractResource[0]); + + AttributeInfo info = new AttributeInfo("test", Format.REFERENCE_SET); + TestResourceRepository projectResources = new TestResourceRepository(folder,false); + projectResources.addResource(ResourceType.STRING, "mystring"); + projectResources.addResource(ResourceType.DIMEN, "mydimen"); + TestResourceRepository frameworkResources = new TestResourceRepository(folder, true); + frameworkResources.addResource(ResourceType.LAYOUT, "mylayout"); + + assertTrue(info.isValid("@string/mystring", null, null)); + assertTrue(info.isValid("@dimen/mydimen", null, null)); + assertTrue(info.isValid("@android:layout/mylayout", null, null)); + assertTrue(info.isValid("?android:attr/listPreferredItemHeigh", null, null)); + + assertTrue(info.isValid("@string/mystring", projectResources, frameworkResources)); + assertTrue(info.isValid("@dimen/mydimen", projectResources, frameworkResources)); + assertTrue(info.isValid("@android:layout/mylayout", projectResources, frameworkResources)); + + assertFalse(info.isValid("@android:string/mystring", projectResources, + frameworkResources)); + assertFalse(info.isValid("@android:dimen/mydimen", projectResources, frameworkResources)); + assertFalse(info.isValid("@layout/mylayout", projectResources, frameworkResources)); + assertFalse(info.isValid("@layout/foo", projectResources, frameworkResources)); + assertFalse(info.isValid("@anim/foo", projectResources, frameworkResources)); + assertFalse(info.isValid("@android:anim/foo", projectResources, frameworkResources)); + } + + private class TestResourceRepository extends ResourceRepository { + private Multimap<ResourceType, String> mResources = ArrayListMultimap.create(); + + protected TestResourceRepository(IAbstractFolder resFolder, boolean isFrameworkRepository) { + super(resFolder, isFrameworkRepository); + } + + void addResource(ResourceType type, String name) { + mResources.put(type, name); + } + + @Override + @NonNull + protected ResourceItem createResourceItem(@NonNull String name) { + fail("Not used in test"); + return null; + } + + @Override + public boolean hasResourceItem(@NonNull ResourceType type, @NonNull String name) { + Collection<String> names = mResources.get(type); + if (names != null) { + return names.contains(name); + } + + return false; + } + }; + + + public void testIsValid() throws Exception { + // This test loads the full attrs.xml file and then processes a bunch of platform + // resource file and makes sure that they're all considered valid. This helps + // make sure that isValid() closely matches what aapt accepts. + String sdkPath = System.getenv("ADT_SDK_SOURCE_PATH"); + assertNotNull("This test requires ADT_SDK_SOURCE_PATH to be set to point to the" + + "SDK git repository", sdkPath); + File sdk = new File(sdkPath); + assertNotNull("$ADT_SDK_SOURCE_PATH (" + sdk.getPath() + ") is not a directory", + sdk.isDirectory()); + File git = sdk.getParentFile(); + File attrsPath = new File(git, "frameworks" + File.separator + "base" + + File.separator + "core" + File.separator + "res" + File.separator + "res" + + File.separator + "values" + File.separator + "attrs.xml"); + assertTrue(attrsPath.getPath(), attrsPath.exists()); + AttrsXmlParser parser = new AttrsXmlParser(attrsPath.getPath(), + new StdLogger(StdLogger.Level.VERBOSE), 1100); + parser.preload(); + Map<String, AttributeInfo> attributeMap = parser.getAttributeMap(); + assertNotNull(attributeMap); + assertNotNull(attributeMap.get("layout_width")); + Set<String> seen = Sets.newHashSet(); + + checkDir(new File(git, "packages" + File.separator + "apps"), false, attributeMap, seen); + } + + private void checkDir(File dir, boolean isResourceDir, + Map<String, AttributeInfo> map, Set<String> seen) throws IOException { + assertTrue(dir.isDirectory()); + File[] list = dir.listFiles(); + if (list != null) { + for (File file : list) { + if (isResourceDir && file.isFile() && file.getPath().endsWith(DOT_XML)) { + checkXmlFile(file, map, seen); + } else if (file.isDirectory()) { + checkDir(file, isResourceDir || file.getName().equals("res"), map, seen); + } + } + } + } + + private void checkXmlFile(File file, Map<String, AttributeInfo> map, + Set<String> seen) throws IOException { + String xml = Files.toString(file, Charsets.UTF_8); + if (xml != null) { + //Document doc = DomUtilities.parseStructuredDocument(xml); + Document doc = DomUtilities.parseDocument(xml, false); + if (doc != null && doc.getDocumentElement() != null) { + checkElement(file, doc.getDocumentElement(), map, seen); + } + } + } + + private void checkElement(File file, Element element, Map<String, AttributeInfo> map, + Set<String> seen) { + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0, n = attributes.getLength(); i < n; i++) { + Attr attribute = (Attr) attributes.item(i); + + String uri = attribute.getNamespaceURI(); + String name = attribute.getLocalName(); + String value = attribute.getValue(); + if (ANDROID_URI.equals(uri)) { + AttributeInfo info = map.get(name); + if (info == null) { + System.out.println("Warning: Unknown attribute '" + name + "' in " + file); + return; + } + if (!info.isValid(value, null, null)) { + if (name.equals("duration") || name.equals("exitFadeDuration")) { + // Already known + return; + } + String message = "In file " + + file.getPath() + ":\nCould not validate value \"" + value + + "\" for attribute '" + + name + "' where the attribute info has formats " + info.getFormats() + + "\n"; + System.out.println("\n" + message); + fail(message); + } + if ((value.startsWith("@") || value.startsWith("?")) && + !info.getFormats().contains(Format.REFERENCE)) { + // Print out errors in attrs.xml + + if (!seen.contains(name)) { + seen.add(name); + System.out.println("\"" + name + "\" with formats " + info.getFormats() + + " was passed a reference (" + value + ") in file " + file); + } + } + } + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttrsXmlParserManifestTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttrsXmlParserManifestTest.java new file mode 100755 index 000000000..82d6acafb --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttrsXmlParserManifestTest.java @@ -0,0 +1,94 @@ +/* + * 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.resources.platform; + +import com.android.ide.eclipse.mock.TestLogger; +import com.android.ide.eclipse.tests.AdtTestData; + +import java.util.Arrays; +import java.util.Map; +import java.util.TreeMap; + +import junit.framework.TestCase; + +public class AttrsXmlParserManifestTest extends TestCase { + + private AttrsXmlParser mParser; + private String mFilePath; + + private static final String MOCK_DATA_PATH = + "com/android/ide/eclipse/testdata/mock_manifest_attrs.xml"; //$NON-NLS-1$ + + @Override + public void setUp() throws Exception { + mFilePath = AdtTestData.getInstance().getTestFilePath(MOCK_DATA_PATH); + mParser = new AttrsXmlParser(mFilePath, new TestLogger(), 100); + } + + @Override + public void tearDown() throws Exception { + } + + public void testGetOsAttrsXmlPath() throws Exception { + assertEquals(mFilePath, mParser.getOsAttrsXmlPath()); + } + + private Map<String, DeclareStyleableInfo> preloadAndGetStyleables() { + assertSame(mParser, mParser.preload()); + + Map<String, DeclareStyleableInfo> styleableList = mParser.getDeclareStyleableList(); + // For testing purposes, we want the strings sorted + if (!(styleableList instanceof TreeMap<?, ?>)) { + styleableList = new TreeMap<String, DeclareStyleableInfo>(styleableList); + } + return styleableList; + } + + public final void testPreload() throws Exception { + Map<String, DeclareStyleableInfo> styleableList = preloadAndGetStyleables(); + + assertEquals( + "[AndroidManifest, " + + "AndroidManifestActivityAlias, " + + "AndroidManifestApplication, " + + "AndroidManifestNewElement, " + + "AndroidManifestNewParent, " + + "AndroidManifestPermission" + + "]", + Arrays.toString(styleableList.keySet().toArray())); + } + + /** + * Tests that AndroidManifestNewParentNewElement got renamed to AndroidManifestNewElement + * and a parent named AndroidManifestNewParent was automatically created. + */ + public final void testNewParent() throws Exception { + Map<String, DeclareStyleableInfo> styleableList = preloadAndGetStyleables(); + + DeclareStyleableInfo newElement = styleableList.get("AndroidManifestNewElement"); + assertNotNull(newElement); + assertEquals("AndroidManifestNewElement", newElement.getStyleName()); + assertEquals("[AndroidManifestNewParent]", + Arrays.toString(newElement.getParents())); + + DeclareStyleableInfo newParent = styleableList.get("AndroidManifestNewParent"); + assertNotNull(newParent); + assertEquals("[AndroidManifest]", + Arrays.toString(newParent.getParents())); + + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttrsXmlParserTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttrsXmlParserTest.java new file mode 100644 index 000000000..883577b8a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttrsXmlParserTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2008 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.resources.platform; + + +import com.android.ide.common.api.IAttributeInfo.Format; +import com.android.ide.eclipse.mock.TestLogger; +import com.android.ide.eclipse.tests.AdtTestData; + +import java.util.Map; + +import junit.framework.TestCase; + +public class AttrsXmlParserTest extends TestCase { + + private AttrsXmlParser mParser; + private String mFilePath; + + private static final String MOCK_DATA_PATH = + "com/android/ide/eclipse/testdata/mock_attrs.xml"; //$NON-NLS-1$ + + @Override + public void setUp() throws Exception { + mFilePath = AdtTestData.getInstance().getTestFilePath(MOCK_DATA_PATH); + mParser = new AttrsXmlParser(mFilePath, new TestLogger(), 100); + } + + @Override + public void tearDown() throws Exception { + } + + public void testGetOsAttrsXmlPath() throws Exception { + assertEquals(mFilePath, mParser.getOsAttrsXmlPath()); + } + + public final void testPreload() throws Exception { + assertSame(mParser, mParser.preload()); + } + + + public final void testLoadViewAttributes() throws Exception { + mParser.preload(); + ViewClassInfo info = new ViewClassInfo( + false /* isLayout */, + "mock_android.something.Theme", //$NON-NLS-1$ + "Theme"); //$NON-NLS-1$ + mParser.loadViewAttributes(info); + + assertEquals("These are the standard attributes that make up a complete theme.", //$NON-NLS-1$ + info.getJavaDoc()); + AttributeInfo[] attrs = info.getAttributes(); + assertEquals(1, attrs.length); + assertEquals("scrollbarSize", info.getAttributes()[0].getName()); + assertEquals(1, info.getAttributes()[0].getFormats().size()); + assertEquals(Format.DIMENSION, info.getAttributes()[0].getFormats().iterator().next()); + } + + public final void testEnumFlagValues() throws Exception { + /* The XML being read contains: + <!-- Standard orientation constant. --> + <attr name="orientation"> + <!-- Defines an horizontal widget. --> + <enum name="horizontal" value="0" /> + <!-- Defines a vertical widget. --> + <enum name="vertical" value="1" /> + </attr> + */ + + mParser.preload(); + Map<String, Map<String, Integer>> attrMap = mParser.getEnumFlagValues(); + assertTrue(attrMap.containsKey("orientation")); + + Map<String, Integer> valueMap = attrMap.get("orientation"); + assertTrue(valueMap.containsKey("horizontal")); + assertTrue(valueMap.containsKey("vertical")); + assertEquals(Integer.valueOf(0), valueMap.get("horizontal")); + assertEquals(Integer.valueOf(1), valueMap.get("vertical")); + } + + public final void testDeprecated() throws Exception { + mParser.preload(); + + DeclareStyleableInfo dep = mParser.getDeclareStyleableList().get("DeprecatedTest"); + assertNotNull(dep); + + AttributeInfo[] attrs = dep.getAttributes(); + assertEquals(4, attrs.length); + + assertEquals("deprecated-inline", attrs[0].getName()); + assertEquals("In-line deprecated.", attrs[0].getDeprecatedDoc()); + assertEquals("Deprecated comments using delimiters.", attrs[0].getJavaDoc()); + + assertEquals("deprecated-multiline", attrs[1].getName()); + assertEquals("Multi-line version of deprecated that works till the next tag.", + attrs[1].getDeprecatedDoc()); + assertEquals("Deprecated comments on their own line.", attrs[1].getJavaDoc()); + + assertEquals("deprecated-not", attrs[2].getName()); + assertEquals(null, attrs[2].getDeprecatedDoc()); + assertEquals("This attribute is not deprecated.", attrs[2].getJavaDoc()); + + assertEquals("deprecated-no-javadoc", attrs[3].getName()); + assertEquals("There is no other javadoc here.", attrs[3].getDeprecatedDoc()); + assertEquals("", attrs[3].getJavaDoc()); + } +} |