diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.tests/unittests')
106 files changed, 14925 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()); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtPluginTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtPluginTest.java new file mode 100644 index 000000000..e198c6e4b --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtPluginTest.java @@ -0,0 +1,49 @@ +/* + * 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.eclipse.adt; + +import java.io.File; +import java.io.StringReader; + +import junit.framework.TestCase; + +public class AdtPluginTest extends TestCase { + public void testReaderContains() throws Exception { + String input = "this is a test"; + assertFalse(AdtPlugin.streamContains(new StringReader(input), "hello")); + assertTrue(AdtPlugin.streamContains(new StringReader(input), "this")); + assertFalse(AdtPlugin.streamContains(new StringReader(input), "thiss")); + assertTrue(AdtPlugin.streamContains(new StringReader(input), "is a")); + assertTrue(AdtPlugin.streamContains(new StringReader("ABC ABCDAB ABCDABCDABDE"), + "ABCDABD")); + assertFalse(AdtPlugin.streamContains(new StringReader("ABC ABCDAB ABCDABCDABDE"), + "ABCEABD")); + } + + public void testReadStream() throws Exception { + String input = "this is a test"; + String contents = AdtPlugin.readFile(new StringReader(input)); + assertEquals(input, contents); + } + + public void testReadWriteFile() throws Exception { + File temp = File.createTempFile("test", ".txt"); + String myContent = "this is\na test"; + AdtPlugin.writeFile(temp, myContent); + String readBack = AdtPlugin.readFile(temp); + assertEquals(myContent, readBack); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java new file mode 100644 index 000000000..c1e94ac69 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.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.eclipse.adt; + +import junit.framework.TestCase; + +import java.util.Locale; + +@SuppressWarnings("javadoc") +public class AdtUtilsTest extends TestCase { + public void testExtractClassName() { + assertEquals("Foo", AdtUtils.extractClassName("foo")); + assertEquals("Foobar", AdtUtils.extractClassName("foo bar")); + assertEquals("JavasTypeSystem", AdtUtils.extractClassName("Java's Type System")); + assertEquals("Foo", AdtUtils.extractClassName("1foo ")); + } + + public void testStripAllExtensions() { + assertEquals("", AdtUtils.stripAllExtensions("")); + assertEquals("foobar", AdtUtils.stripAllExtensions("foobar")); + assertEquals("foobar", AdtUtils.stripAllExtensions("foobar.png")); + assertEquals("foobar", AdtUtils.stripAllExtensions("foobar.9.png")); + assertEquals(".profile", AdtUtils.stripAllExtensions(".profile")); + } + + public void testStripLastExtension() { + assertEquals("", AdtUtils.stripLastExtension("")); + assertEquals("foobar", AdtUtils.stripLastExtension("foobar")); + assertEquals("foobar", AdtUtils.stripLastExtension("foobar.png")); + assertEquals("foobar.9", AdtUtils.stripLastExtension("foobar.9.png")); + assertEquals(".profile", AdtUtils.stripLastExtension(".profile")); + } + + public void testCapitalize() { + assertEquals("UPPER", AdtUtils.capitalize("UPPER")); + assertEquals("Lower", AdtUtils.capitalize("lower")); + assertEquals("Capital", AdtUtils.capitalize("Capital")); + assertEquals("CamelCase", AdtUtils.capitalize("camelCase")); + assertEquals("", AdtUtils.capitalize("")); + assertSame("Foo", AdtUtils.capitalize("Foo")); + assertNull(null, AdtUtils.capitalize(null)); + } + + public void testCamelCaseToUnderlines() { + assertEquals("", AdtUtils.camelCaseToUnderlines("")); + assertEquals("foo", AdtUtils.camelCaseToUnderlines("foo")); + assertEquals("foo", AdtUtils.camelCaseToUnderlines("Foo")); + assertEquals("foo_bar", AdtUtils.camelCaseToUnderlines("FooBar")); + assertEquals("test_xml", AdtUtils.camelCaseToUnderlines("testXML")); + assertEquals("test_foo", AdtUtils.camelCaseToUnderlines("testFoo")); + } + + public void testUnderlinesToCamelCase() { + assertEquals("", AdtUtils.underlinesToCamelCase("")); + assertEquals("", AdtUtils.underlinesToCamelCase("_")); + assertEquals("Foo", AdtUtils.underlinesToCamelCase("foo")); + assertEquals("FooBar", AdtUtils.underlinesToCamelCase("foo_bar")); + assertEquals("FooBar", AdtUtils.underlinesToCamelCase("foo__bar")); + assertEquals("Foo", AdtUtils.underlinesToCamelCase("foo_")); + } + + public void testStripSuffix() { + assertEquals("Foo", AdtUtils.stripSuffix("Foo", "")); + assertEquals("Fo", AdtUtils.stripSuffix("Foo", "o")); + assertEquals("F", AdtUtils.stripSuffix("Fo", "o")); + assertEquals("", AdtUtils.stripSuffix("Foo", "Foo")); + assertEquals("LinearLayout_Layout", + AdtUtils.stripSuffix("LinearLayout_LayoutParams", "Params")); + assertEquals("Foo", AdtUtils.stripSuffix("Foo", "Bar")); + } + + public void testFormatFloatValue() throws Exception { + assertEquals("1", AdtUtils.formatFloatAttribute(1.0f)); + assertEquals("2", AdtUtils.formatFloatAttribute(2.0f)); + assertEquals("1.50", AdtUtils.formatFloatAttribute(1.5f)); + assertEquals("1.50", AdtUtils.formatFloatAttribute(1.50f)); + assertEquals("1.51", AdtUtils.formatFloatAttribute(1.51f)); + assertEquals("1.51", AdtUtils.formatFloatAttribute(1.514542f)); + assertEquals("1.52", AdtUtils.formatFloatAttribute(1.516542f)); + assertEquals("-1.51", AdtUtils.formatFloatAttribute(-1.51f)); + assertEquals("-1", AdtUtils.formatFloatAttribute(-1f)); + } + + public void testFormatFloatValueLocale() throws Exception { + // Ensure that the layout float values aren't affected by + // locale settings, like using commas instead of of periods + Locale originalDefaultLocale = Locale.getDefault(); + + try { + Locale.setDefault(Locale.FRENCH); + + // Ensure that this is a locale which uses a comma instead of a period: + assertEquals("5,24", String.format("%.2f", 5.236f)); + + // Ensure that the formatFloatAttribute is immune + assertEquals("1.50", AdtUtils.formatFloatAttribute(1.5f)); + } finally { + Locale.setDefault(originalDefaultLocale); + } + } + + public void testEscapeUnicodeChars() throws Exception { + assertEquals("", AdtUtils.replaceUnicodeEscapes("")); + assertEquals("foo bar", AdtUtils.replaceUnicodeEscapes("foo bar")); + assertEquals("\u25C0", AdtUtils.replaceUnicodeEscapes("\\u25C0")); + assertEquals("!\u25C0\u25C1!", AdtUtils.replaceUnicodeEscapes("!\\u25C0\\u25C1!")); + assertEquals("\u1234\\", AdtUtils.replaceUnicodeEscapes("\\u1234\\")); + + assertEquals("\\U25C0", AdtUtils.replaceUnicodeEscapes("\\U25C0")); // no unicode expand + assertEquals("\\u25C", AdtUtils.replaceUnicodeEscapes("\\u25C")); // no unicode expand + assertEquals("\\\\u25C0", AdtUtils.replaceUnicodeEscapes("\\\\u25C0")); // escaped + assertEquals("\\u123\\", AdtUtils.replaceUnicodeEscapes("\\u123\\")); // broken + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/build/BaseBuilderTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/build/BaseBuilderTest.java new file mode 100644 index 000000000..a1d658b97 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/build/BaseBuilderTest.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2007 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.eclipse.adt.internal.build; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import junit.framework.TestCase; + +public class BaseBuilderTest extends TestCase { + + public void testParseAaptOutput() { + Pattern p = Pattern.compile( "^(.+):(\\d+):\\s(.+)$"); //$NON-NLS-1$ + String s = "C:\\java\\workspace-android\\AndroidApp\\res\\values\\strings.xml:11: WARNING: empty 'some warning text"; + + Matcher m = p.matcher(s); + assertEquals(true, m.matches()); + assertEquals("C:\\java\\workspace-android\\AndroidApp\\res\\values\\strings.xml", m.group(1)); + assertEquals("11", m.group(2)); + assertEquals("WARNING: empty 'some warning text", m.group(3)); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/AndroidDoubleClickStrategyTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/AndroidDoubleClickStrategyTest.java new file mode 100644 index 000000000..3c0805a34 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/AndroidDoubleClickStrategyTest.java @@ -0,0 +1,71 @@ +/* + * 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.eclipse.adt.internal.editors; + +import org.eclipse.swt.graphics.Point; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class AndroidDoubleClickStrategyTest extends TestCase { + public void test_getWord_plain() throws Exception { + checkWord("^foo", "[foo]"); + checkWord("'fo^o'", "'[foo]'"); + checkWord("\"foo^\"", "\"[foo]\""); + } + + public void test_getWord_resources() throws Exception { + checkWord("'@and^roid:string/ok'", "'[@android:string/ok]'"); + checkWord("'@android^:string/ok'", "'[@android:string/ok]'"); + checkWord("'^@android:string/ok'", "'[@android:string/ok]'"); + checkWord("'@android:^string/ok'", "'[@android:string/ok]'"); + checkWord("'@android:string^/ok'", "'[@android:string/ok]'"); + checkWord("'@android:string/^ok'", "'@android:string/[ok]'"); + checkWord("'@android:string/o^k'", "'@android:string/[ok]'"); + checkWord("'@android:string/ok^'", "'@android:string/[ok]'"); + checkWord("'@string/ok^'", "'@string/[ok]'"); + checkWord("'@str^ing/ok'", "'[@string/ok]'"); + } + + public void test_getWord_classnames() throws Exception { + checkWord("\"co^m.example.templatetest1\"", "\"[com.example.templatetest1]\""); + checkWord("\"com.exam^ple.templatetest1\"", "\"[com.example.templatetest1]\""); + checkWord("\"com.example^.templatetest1\"", "\"[com.example.templatetest1]\""); + checkWord("\"com.example.templat^etest1\"", "\"com.example.[templatetest1]\""); + checkWord("\"com.example.^templatetest1\"", "\"com.example.[templatetest1]\""); + checkWord("\"com.example.templatetest1^\"", "\"com.example.[templatetest1]\""); + checkWord("\"...^\"", "\"[...]\""); + checkWord("\"..^.\"", "\"[...]\""); + } + + private void checkWord(String before, String expected) throws Exception { + AndroidDoubleClickStrategy strategy = new AndroidDoubleClickStrategy(); + int cursor = before.indexOf('^'); + assertTrue("Must set cursor position with ^ in " + before, cursor != -1); + before = before.substring(0, cursor) + before.substring(cursor + 1); + assertEquals(-1, before.indexOf('^')); + assertEquals(-1, before.indexOf('[')); + assertEquals(-1, before.indexOf(']')); + + Point positions = strategy.getWord(before, cursor); + assertNotNull(positions); + assertTrue(positions.y >= positions.x); + String after = before.substring(0, positions.x) + '[' + + before.substring(positions.x, positions.y) + ']' + + before.substring(positions.y); + assertEquals(expected, after); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java new file mode 100644 index 000000000..03db18956 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java @@ -0,0 +1,297 @@ +/* + * 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.eclipse.adt.internal.editors.descriptors; + +import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; + +import junit.framework.TestCase; + +/** + * Unit tests for DescriptorsUtils in the editors plugin + */ +@SuppressWarnings("javadoc") +public class DescriptorsUtilsTest extends TestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testPrettyAttributeUiName() { + assertEquals("", DescriptorsUtils.prettyAttributeUiName("")); + + assertEquals("Max width for view", + DescriptorsUtils.prettyAttributeUiName("maxWidthForView")); + + assertEquals("Layout width", + DescriptorsUtils.prettyAttributeUiName("layout_width")); + + // X Y and Z are capitalized when used as single words (so "T" becomes "t") + assertEquals("Axis X", DescriptorsUtils.prettyAttributeUiName("axisX")); + assertEquals("Axis Y", DescriptorsUtils.prettyAttributeUiName("axisY")); + assertEquals("Axis Z", DescriptorsUtils.prettyAttributeUiName("axisZ")); + assertEquals("Axis t", DescriptorsUtils.prettyAttributeUiName("axisT")); + + assertEquals("The X axis", DescriptorsUtils.prettyAttributeUiName("theXAxis")); + assertEquals("The Y axis", DescriptorsUtils.prettyAttributeUiName("theYAxis")); + assertEquals("The Z axis", DescriptorsUtils.prettyAttributeUiName("theZAxis")); + assertEquals("The t axis", DescriptorsUtils.prettyAttributeUiName("theTAxis")); + + // Special cases for "uri" and "sdk" etc + assertEquals("Grant URI permission", + DescriptorsUtils.prettyAttributeUiName("grantUriPermission")); + assertEquals("URI permission", + DescriptorsUtils.prettyAttributeUiName("uriPermission")); + assertEquals("Min SDK version", DescriptorsUtils.prettyAttributeUiName("minSdkVersion")); + assertEquals("SDK version", DescriptorsUtils.prettyAttributeUiName("sdkVersion")); + assertEquals("IME action method", + DescriptorsUtils.prettyAttributeUiName("imeActionMethod")); + assertEquals("VM safe mode", DescriptorsUtils.prettyAttributeUiName("vmSafeMode")); + assertEquals("UI options", DescriptorsUtils.prettyAttributeUiName("uiOptions")); + } + + public void testCapitalize() { + assertEquals("", DescriptorsUtils.capitalize("")); + + assertEquals("Max Width For View", + DescriptorsUtils.capitalize("maxWidthForView")); + + assertEquals("Layout Width", + DescriptorsUtils.capitalize("layout_width")); + + assertEquals("Axis X", DescriptorsUtils.capitalize("axisX")); + assertEquals("Axis Y", DescriptorsUtils.capitalize("axisY")); + assertEquals("Axis Z", DescriptorsUtils.capitalize("axisZ")); + assertEquals("Axis T", DescriptorsUtils.capitalize("axisT")); + + assertEquals("The X Axis", DescriptorsUtils.capitalize("theXAxis")); + assertEquals("The Y Axis", DescriptorsUtils.capitalize("theYAxis")); + assertEquals("The Z Axis", DescriptorsUtils.capitalize("theZAxis")); + assertEquals("The T Axis", DescriptorsUtils.capitalize("theTAxis")); + + // Special cases for "uri" and "sdk" etc + assertEquals("Grant URI Permission", DescriptorsUtils.capitalize("grantUriPermission")); + assertEquals("Min SDK Version", DescriptorsUtils.capitalize("minSdkVersion")); + assertEquals("IME Action Method", DescriptorsUtils.capitalize("imeActionMethod")); + assertEquals("URI Permission", DescriptorsUtils.capitalize("uriPermission")); + assertEquals("SDK Version", DescriptorsUtils.capitalize("sdkVersion")); + assertEquals("Grant IME", DescriptorsUtils.capitalize("GrantIme")); + assertEquals("VM Safe Mode", DescriptorsUtils.capitalize("vmSafeMode")); + assertEquals("UI Options", DescriptorsUtils.capitalize("uiOptions")); + } + + public void testFormatTooltip() { + assertEquals("", DescriptorsUtils.formatTooltip("")); + + assertEquals("\"application\"", + DescriptorsUtils.formatTooltip( + "<code>application</code>")); + + assertEquals("android.content.Intent", + DescriptorsUtils.formatTooltip( + "{@link android.content.Intent}")); + + assertEquals("FLAG_ACTIVITY_SINGLE_TOP", + DescriptorsUtils.formatTooltip( + "{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}")); + + assertEquals("activity-alias", + DescriptorsUtils.formatTooltip( + "{@link \t #AndroidManifestActivityAlias \tactivity-alias }")); + + assertEquals("\"permission\"", + DescriptorsUtils.formatTooltip( + "{@link #AndroidManifestPermission <permission>}")); + + assertEquals("and etc.", + DescriptorsUtils.formatTooltip( + "{@link #IntentCategory <category> and etc. }")); + + assertEquals("Activity.onNewIntent()", + DescriptorsUtils.formatTooltip( + "{@link android.app.Activity#onNewIntent Activity.onNewIntent()}")); + } + + public void testFormatFormText() { + ElementDescriptor desc = new ElementDescriptor("application"); + desc.setSdkUrl(DescriptorsUtils.MANIFEST_SDK_URL + "TagApplication"); + String docBaseUrl = "http://base"; + assertEquals("<form><li style=\"image\" value=\"image\"></li></form>", DescriptorsUtils.formatFormText("", desc, docBaseUrl)); + + assertEquals("<form><li style=\"image\" value=\"image\"><a href=\"http://base/reference/android/R.styleable.html#TagApplication\">application</a></li></form>", + DescriptorsUtils.formatFormText( + "<code>application</code>", + desc, docBaseUrl)); + + assertEquals("<form><li style=\"image\" value=\"image\"><b>android.content.Intent</b></li></form>", + DescriptorsUtils.formatFormText( + "{@link android.content.Intent}", + desc, docBaseUrl)); + + assertEquals("<form><li style=\"image\" value=\"image\"><a href=\"http://base/reference/android/R.styleable.html#AndroidManifestPermission\">AndroidManifestPermission</a></li></form>", + DescriptorsUtils.formatFormText( + "{@link #AndroidManifestPermission}", + desc, docBaseUrl)); + + assertEquals("<form><li style=\"image\" value=\"image\"><a href=\"http://base/reference/android/R.styleable.html#AndroidManifestPermission\">\"permission\"</a></li></form>", + DescriptorsUtils.formatFormText( + "{@link #AndroidManifestPermission <permission>}", + desc, docBaseUrl)); + } + + public void testGetFreeWidgetId() throws Exception { + DocumentDescriptor documentDescriptor = + new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$ + UiDocumentNode model = new UiDocumentNode(documentDescriptor); + UiElementNode uiRoot = model.getUiRoot(); + + assertEquals("@+id/button1", DescriptorsUtils.getFreeWidgetId(uiRoot, "Button")); + assertEquals("@+id/linearLayout1", + DescriptorsUtils.getFreeWidgetId(uiRoot, "LinearLayout")); + } + + public void testNeedsDefaultId() throws Exception { + assertTrue(DescriptorsUtils.needsDefaultId(new ElementDescriptor("Button"))); + assertTrue(DescriptorsUtils.needsDefaultId(new ElementDescriptor("EditText"))); + assertTrue(DescriptorsUtils.needsDefaultId(new ElementDescriptor("TextView"))); + + assertFalse(DescriptorsUtils.needsDefaultId(new ElementDescriptor("LinearLayout"))); + assertFalse(DescriptorsUtils.needsDefaultId(new ElementDescriptor("GridLayout"))); + assertFalse(DescriptorsUtils.needsDefaultId(new ElementDescriptor("RelativeLayout"))); + assertFalse(DescriptorsUtils.needsDefaultId(new ElementDescriptor("include"))); + assertFalse(DescriptorsUtils.needsDefaultId(new ElementDescriptor("merge"))); + assertFalse(DescriptorsUtils.needsDefaultId(new ElementDescriptor("fragment"))); + assertFalse(DescriptorsUtils.needsDefaultId(new ElementDescriptor("Space"))); + } + + private static ViewElementDescriptor createDesc(String name, String fqn, boolean hasChildren) { + if (hasChildren) { + return new ViewElementDescriptor(name, name, fqn, "", "", new AttributeDescriptor[0], + new AttributeDescriptor[0], new ElementDescriptor[1], false); + } else { + return new ViewElementDescriptor(name, fqn); + } + } + + public void testCanInsertChildren() throws Exception { + assertFalse(DescriptorsUtils.canInsertChildren(createDesc("android:Button", + "android.widget.Button", false), null)); + assertTrue(DescriptorsUtils.canInsertChildren(createDesc("android:LinearLayout", + "android.view.LinearLayout", true), null)); + assertFalse(DescriptorsUtils.canInsertChildren(createDesc("android:ListView", + "android.widget.ListView", true), null)); + assertFalse(DescriptorsUtils.canInsertChildren(createDesc("android:ExpandableListView", + "android.widget.ExpandableListView", true), null)); + assertFalse(DescriptorsUtils.canInsertChildren(createDesc("android:Gallery", + "android.widget.Gallery", true), null)); + assertFalse(DescriptorsUtils.canInsertChildren(createDesc("android:GridView", + "android.widget.GridView", true), null)); + + // This isn't the Android one (missing android: namespace prefix): + // This test is disabled since I had to remove the namespace enforcement + // (see namespace-related comment in canInsertChildren) + //assertTrue(DescriptorsUtils.canInsertChildren(createDesc("mynamespace:ListView", + // "android.widget.ListView", true), null)); + + // Custom view without known view object + assertTrue(DescriptorsUtils.canInsertChildren(createDesc("MyView", + "foo.bar.MyView", true), null)); + + // Custom view with known view object that extends AdapterView + Object view = new MyClassLoader().findClass("foo.bar.MyView").newInstance(); + assertFalse(DescriptorsUtils.canInsertChildren(createDesc("MyView", + "foo.bar.MyView", true), view)); + } + + /** Test class loader which finds foo.bar.MyView extends android.widget.AdapterView */ + private static class MyClassLoader extends ClassLoader { + public MyClassLoader() { + super(null); + } + + @Override + protected Class<?> findClass(String name) throws ClassNotFoundException { + if (name.equals("foo.bar.MyView")) { + // Simple class stub compiled by javac and dumped as bytes: + //package foo.bar; + //public class MyView extends android.widget.AdapterView { + // public MyView() { + // super(null); + // } + //} + byte[] classData = new byte[] { + -54,-2,-70,-66,0,0,0,49,0,17,10,0,3,0,13,7,0,14,7,0,15,1,0,6,60,105,110, + 105,116,62,1,0,3,40,41,86,1,0,4,67,111,100,101,1,0,15,76,105,110,101,78, + 117,109,98,101,114,84,97,98,108,101,1,0,18,76,111,99,97,108,86,97,114, + 105,97,98,108,101,84,97,98,108,101,1,0,4,116,104,105,115,1,0,16,76,102, + 111,111,47,98,97,114,47,77,121,86,105,101,119,59,1,0,10,83,111,117,114, + 99,101,70,105,108,101,1,0,11,77,121,86,105,101,119,46,106,97,118,97,12, + 0,4,0,16,1,0,14,102,111,111,47,98,97,114,47,77,121,86,105,101,119,1,0, + 26,97,110,100,114,111,105,100,47,119,105,100,103,101,116,47,65,100,97, + 112,116,101,114,86,105,101,119,1,0,28,40,76,97,110,100,114,111,105,100, + 47,99,111,110,116,101,110,116,47,67,111,110,116,101,120,116,59,41,86,0, + 33,0,2,0,3,0,0,0,0,0,1,0,1,0,4,0,5,0,1,0,6,0,0,0,52,0,2,0,1,0,0,0,6,42, + 1,-73,0,1,-79,0,0,0,2,0,7,0,0,0,10,0,2,0,0,0,9,0,5,0,10,0,8,0,0,0,12,0, + 1,0,0,0,6,0,9,0,10,0,0,0,1,0,11,0,0,0,2,0,12 + }; + return defineClass("foo.bar.MyView", classData, 0, classData.length); + } + if (name.equals("android.widget.AdapterView")) { + // Simple class stub compiled by javac and dumped as bytes: + //package android.widget; + //public class AdapterView { + // public AdapterView(android.content.Context context) { } + //} + byte[] classData = new byte[] { + -54,-2,-70,-66,0,0,0,49,0,19,10,0,3,0,15,7,0,16,7,0,17,1,0,6,60,105,110, + 105,116,62,1,0,28,40,76,97,110,100,114,111,105,100,47,99,111,110,116,101, + 110,116,47,67,111,110,116,101,120,116,59,41,86,1,0,4,67,111,100,101,1,0, + 15,76,105,110,101,78,117,109,98,101,114,84,97,98,108,101,1,0,18,76,111, + 99,97,108,86,97,114,105,97,98,108,101,84,97,98,108,101,1,0,4,116,104,105, + 115,1,0,28,76,97,110,100,114,111,105,100,47,119,105,100,103,101,116,47, + 65,100,97,112,116,101,114,86,105,101,119,59,1,0,7,99,111,110,116,101,120, + 116,1,0,25,76,97,110,100,114,111,105,100,47,99,111,110,116,101,110,116, + 47,67,111,110,116,101,120,116,59,1,0,10,83,111,117,114,99,101,70,105,108, + 101,1,0,16,65,100,97,112,116,101,114,86,105,101,119,46,106,97,118,97,12, + 0,4,0,18,1,0,26,97,110,100,114,111,105,100,47,119,105,100,103,101,116, + 47,65,100,97,112,116,101,114,86,105,101,119,1,0,16,106,97,118,97,47,108, + 97,110,103,47,79,98,106,101,99,116,1,0,3,40,41,86,0,33,0,2,0,3,0,0,0,0,0, + 1,0,1,0,4,0,5,0,1,0,6,0,0,0,57,0,1,0,2,0,0,0,5,42,-73,0,1,-79,0,0,0,2,0, + 7,0,0,0,6,0,1,0,0,0,8,0,8,0,0,0,22,0,2,0,0,0,5,0,9,0,10,0,0,0,0,0,5,0,11, + 0,12,0,1,0,1,0,13,0,0,0,2,0,14 + }; + return defineClass("android.widget.AdapterView", classData, 0, classData.length); + } + + return super.findClass(name); + } + } + + public void testGetBasename() { + assertEquals("Foo", DescriptorsUtils.getBasename("Foo")); + assertEquals("Foo", DescriptorsUtils.getBasename("foo.Foo")); + assertEquals("String", DescriptorsUtils.getBasename("java.util.String")); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/draw9patch/graphics/GraphicsUtilitiesTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/draw9patch/graphics/GraphicsUtilitiesTest.java new file mode 100644 index 000000000..4f00097bc --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/draw9patch/graphics/GraphicsUtilitiesTest.java @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.eclipse.adt.internal.editors.draw9patch.graphics; + +import java.util.Arrays; + +import junit.framework.TestCase; + +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.widgets.Display; + +public class GraphicsUtilitiesTest extends TestCase { + private static final int MASK_ALPHA = 0xFF000000; + + private static final String DIR = "/com/android/ide/eclipse/testdata/draw9patch/"; + + public void testConvertToNinePatchNull() throws Exception { + ImageData result = GraphicsUtilities.convertToNinePatch(null); + assertNull(result); + } + + public void testConvertToNinePatch() throws Exception { + String fileName = DIR + "no-patched.png"; + Image image = new Image(Display.getDefault(), + getClass().getResourceAsStream(fileName)); + ImageData baseData = image.getImageData(); + + ImageData result = GraphicsUtilities.convertToNinePatch(baseData); + + assertEquals(baseData.width + 2, result.width); + assertEquals(baseData.height + 2, result.height); + + // horizontal + for (int x = 0; x < result.width; x++) { + + // top row + assertEquals(0x0, result.getPixel(x, 0) & MASK_ALPHA); + + // bottom row + assertEquals(0x0, result.getPixel(x, result.height - 1) & MASK_ALPHA); + } + + // vertical + for (int y = 0; y < result.height; y++) { + + // left column + assertEquals(0x0, result.getPixel(0, y) & MASK_ALPHA); + + // right column + assertEquals(0x0, result.getPixel(result.width - 1, y) & MASK_ALPHA); + } + } + + public void testClearImageDataNull() throws Exception { + try { + GraphicsUtilities.clearImageData(null); + fail(); + } catch (IllegalArgumentException e) { + } + } + + public void testClearImageData() throws Exception { + String fileName = DIR + "no-patched.png"; + Image image = new Image(Display.getDefault(), + getClass().getResourceAsStream(fileName)); + + ImageData baseData = image.getImageData(); + GraphicsUtilities.clearImageData(baseData); + for (int y = 0; y < baseData.height; y++) { + for (int x = 0; x < baseData.width; x++) { + assertEquals(0x000000, baseData.getPixel(x, y)); + assertEquals(0x00, baseData.getAlpha(x, y)); + } + } + + } + + public void testCopyNull() throws Exception { + ImageData result = GraphicsUtilities.copy(null); + assertNull(result); + } + + public void testCopy() throws Exception { + String fileName = DIR + "no-patched.png"; + Image image = new Image(Display.getDefault(), + getClass().getResourceAsStream(fileName)); + + ImageData baseData = image.getImageData(); + ImageData copiedData = GraphicsUtilities.copy(baseData); + + assertEquals(baseData.width, copiedData.width); + assertEquals(baseData.height, copiedData.height); + assertEquals(baseData.depth, copiedData.depth); + assertEquals(baseData.transparentPixel, copiedData.transparentPixel); + assertEquals(baseData.alpha, copiedData.alpha); + assertTrue(baseData.palette.equals(copiedData.palette)); + + final int[] baseColors = new int[baseData.width]; + final byte[] baseAlpha = new byte[baseData.width]; + + final int[] copiedColors = new int[copiedData.width]; + final byte[] copiedAlpha = new byte[copiedData.width]; + + for (int y = 0; y < baseData.height; y++) { + + baseData.getPixels(0, y, baseData.width, baseColors, 0); + baseData.getPixels(0, y, baseData.width, copiedColors, 0); + assertTrue(Arrays.equals(baseColors, copiedColors)); + + baseData.getAlphas(0, y, baseData.width, baseAlpha, 0); + baseData.getAlphas(0, y, baseData.width, copiedAlpha, 0); + assertTrue(Arrays.equals(baseAlpha, copiedAlpha)); + + } + } + + public void testGetVerticalPixelsIllegalArgument() throws Exception { + String fileName = DIR + "no-patched.png"; + Image image = new Image(Display.getDefault(), + getClass().getResourceAsStream(fileName)); + + ImageData baseData = image.getImageData(); + int[] temp = new int[baseData.width]; + + // data must not be null + try { + GraphicsUtilities.getVerticalPixels(null, 0, 0, 1, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // out must not be null + try { + GraphicsUtilities.getVerticalPixels(baseData, 0, 0, 1, null); + fail(); + } catch (IllegalArgumentException e) { + } + + // out length must be > height + try { + GraphicsUtilities.getVerticalPixels(baseData, 0, 0, 1, new int[0]); + fail(); + } catch (IllegalArgumentException e) { + } + + // x must be > 0 + try { + GraphicsUtilities.getVerticalPixels(baseData, -1, 0, 1, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // y must be > 0 + try { + GraphicsUtilities.getVerticalPixels(baseData, 0, -1, 1, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // height must be >= 0 + try { + GraphicsUtilities.getVerticalPixels(baseData, 0, 0, 0, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // argument x must be < data.width + try { + GraphicsUtilities.getVerticalPixels(baseData, baseData.width, 0, baseData.height, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // argument y must be < data.height + try { + GraphicsUtilities + .getVerticalPixels(baseData, 0, baseData.height, baseData.height, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // argument height must be > (y + data.height) + try { + GraphicsUtilities.getVerticalPixels(baseData, 0, 1, baseData.height, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + } + + public void testGetVerticalPixels() throws Exception { + String fileName = DIR + "no-patched.png"; + Image image = new Image(Display.getDefault(), + getClass().getResourceAsStream(fileName)); + + ImageData baseData = image.getImageData(); + int[] temp = new int[baseData.width]; + + GraphicsUtilities.getVerticalPixels(baseData, 0, 0, baseData.height, temp); + + int height = baseData.height; + for (int y = 0; y < height; y++) { + assertEquals(baseData.getPixel(0, y), temp[y]); + } + } + + public void testGetHorizontalPixelsIllegalArgument() throws Exception { + String fileName = DIR + "no-patched.png"; + Image image = new Image(Display.getDefault(), + getClass().getResourceAsStream(fileName)); + + ImageData baseData = image.getImageData(); + int[] temp = new int[baseData.width]; + + // data must not be null + try { + GraphicsUtilities.getHorizontalPixels(null, 0, 0, 1, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // out must not be null + try { + GraphicsUtilities.getHorizontalPixels(baseData, 0, 0, 1, null); + fail(); + } catch (IllegalArgumentException e) { + } + + // out length must be > width + try { + GraphicsUtilities.getHorizontalPixels(baseData, 0, 0, 1, new int[0]); + fail(); + } catch (IllegalArgumentException e) { + } + + // x must be > 0 + try { + GraphicsUtilities.getHorizontalPixels(baseData, -1, 0, 1, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // y must be > 0 + try { + GraphicsUtilities.getHorizontalPixels(baseData, 0, -1, 1, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // width must be >= 0 + try { + GraphicsUtilities.getHorizontalPixels(baseData, 0, 0, 0, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // argument x must be < data.width + try { + GraphicsUtilities + .getHorizontalPixels(baseData, baseData.width, 0, baseData.width, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // argument y must be < data.height + try { + GraphicsUtilities + .getHorizontalPixels(baseData, 0, baseData.height, baseData.width, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + // argument width must be > (x + data.width) + try { + GraphicsUtilities.getHorizontalPixels(baseData, 1, 0, baseData.width, temp); + fail(); + } catch (IllegalArgumentException e) { + } + + } + + public void testGetHorizontalPixels() throws Exception { + String fileName = DIR + "no-patched.png"; + Image image = new Image(Display.getDefault(), + getClass().getResourceAsStream(fileName)); + + ImageData baseData = image.getImageData(); + int[] temp = new int[baseData.width]; + + GraphicsUtilities.getHorizontalPixels(baseData, 0, 0, baseData.width, temp); + + int width = baseData.width; + for (int x = 0; x < width; x++) { + assertEquals(baseData.getPixel(x, 0), temp[x]); + } + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/draw9patch/graphics/NinePatchedImageTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/draw9patch/graphics/NinePatchedImageTest.java new file mode 100644 index 000000000..72c929618 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/draw9patch/graphics/NinePatchedImageTest.java @@ -0,0 +1,914 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.eclipse.adt.internal.editors.draw9patch.graphics; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; + +import com.android.ide.eclipse.adt.internal.editors.draw9patch.graphics.NinePatchedImage.Chunk; +import com.android.ide.eclipse.adt.internal.editors.draw9patch.graphics.NinePatchedImage.Projection; +import com.android.ide.eclipse.adt.internal.editors.draw9patch.graphics.NinePatchedImage.Tick; + +public class NinePatchedImageTest extends TestCase { + + private static final String DIR = "/com/android/ide/eclipse/testdata/draw9patch/"; + + public void testReadNoPatchedImage() throws Exception { + String fileName = DIR + "no-patched.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + ImageData data = image.getImageData(); + int width = data.width; + int height = data.height; + + assertEquals(72, width); + assertEquals(50, height); + + assertFalse(image.hasNinePatchExtension()); + } + + public void testReadNoPatchedInteraceImage() throws Exception { + String fileName = DIR + "no-patched-interlace.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + ImageData data = image.getImageData(); + int width = data.width; + int height = data.height; + + assertEquals(72, width); + assertEquals(50, height); + + assertFalse(image.hasNinePatchExtension()); + } + + public void testConvert9PatchedImage() throws Exception { + String fileName = DIR + "no-patched.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + ImageData data = image.getImageData(); + int width = data.width; + int height = data.height; + + assertEquals(72, width); + assertEquals(50, height); + + assertFalse(image.hasNinePatchExtension()); + + image.convertToNinePatch(); + + data = image.getImageData(); + width = data.width; + height = data.height; + + // increased patch size + assertEquals(72 + 2, width); + assertEquals(50 + 2, height); + + assertTrue(image.hasNinePatchExtension()); + assertFalse(image.isDirty()); + + // initialized patches + List<Tick> horizontalPatches = image.getHorizontalPatches(); + List<Tick> verticalPatches = image.getVerticalPatches(); + assertEquals(1, horizontalPatches.size()); + assertEquals(1, verticalPatches.size()); + + // initialized contents area + List<Tick> horizontalContentsArea = image.getHorizontalContents(); + List<Tick> verticalContentsArea = image.getVerticalContents(); + assertEquals(1, horizontalContentsArea.size()); + assertEquals(1, verticalContentsArea.size()); + + // content area rectangle + Rectangle contentsArea = image.getContentArea(); + assertEquals(new Rectangle(1, 1, width - 2, height - 2), contentsArea); + } + + public void testReadInvalidPatchedImageCorners() throws Exception { + + // top-left + String fileName = DIR + "invalid-patched1.9.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + assertTrue(image.hasNinePatchExtension()); + assertFalse(image.ensure9Patch()); + + // top-right + fileName = DIR + "invalid-patched2.9.png"; + image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + assertTrue(image.hasNinePatchExtension()); + assertFalse(image.ensure9Patch()); + + // bottom-left + fileName = DIR + "invalid-patched3.9.png"; + image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + assertTrue(image.hasNinePatchExtension()); + assertFalse(image.ensure9Patch()); + + // bottom-right + fileName = DIR + "invalid-patched4.9.png"; + image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + assertTrue(image.hasNinePatchExtension()); + assertFalse(image.ensure9Patch()); + } + + public void testReadInvalidPatchedImageLine() throws Exception { + + // top + String fileName = DIR + "invalid-patched5.9.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + assertTrue(image.hasNinePatchExtension()); + assertFalse(image.ensure9Patch()); + + // right + fileName = DIR + "invalid-patched6.9.png"; + image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + assertTrue(image.hasNinePatchExtension()); + assertFalse(image.ensure9Patch()); + + // bottom + fileName = DIR + "invalid-patched7.9.png"; + image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + assertTrue(image.hasNinePatchExtension()); + assertFalse(image.ensure9Patch()); + + // left + fileName = DIR + "invalid-patched8.9.png"; + image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + assertTrue(image.hasNinePatchExtension()); + assertFalse(image.ensure9Patch()); + } + + public void testEnsure9PatchIgnoreInvalidPixels() throws Exception { + // top + String fileName = DIR + "invalid-patched5.9.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + // invalid pixel + int invalidPixel = image.getImageData().getPixel(33, 0); + assertTrue(0x0 != invalidPixel); + + assertTrue(image.hasNinePatchExtension()); + assertFalse(image.ensure9Patch()); + + // ensure9path() ignored invalid pixels + int invalidPixelAlpha = image.getImageData().getAlpha(33, 0); + assertEquals(0x00, invalidPixelAlpha); + } + + public void test9Patch1() throws Exception { + String fileName = DIR + "patched1.9.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + assertTrue(image.hasNinePatchExtension()); + assertTrue(image.ensure9Patch()); + + // patches + List<Tick> horizontalPatches = image.getHorizontalPatches(); + List<Tick> verticalPatches = image.getVerticalPatches(); + assertEquals(3, horizontalPatches.size()); + assertEquals(3, verticalPatches.size()); + + Chunk[][] chunks = null; + chunks = image.getChunks(chunks); + + // vertical chunk size + assertEquals(3, chunks.length); + + // horizontal chunk size + for (int i = 0; i < chunks.length; i++) { + assertEquals(3, chunks[i].length); + } + + Chunk c = null; + Rectangle rect = null; + + // Row 1 + c = chunks[0][0]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(1, rect.x); + assertEquals(1, rect.y); + assertEquals(1, rect.width); + assertEquals(1, rect.height); + + c = chunks[0][1]; + rect = c.rect; + assertEquals(Chunk.TYPE_VERTICAL, c.type); + assertEquals(2, rect.x); + assertEquals(1, rect.y); + assertEquals(70, rect.width); + assertEquals(1, rect.height); + + c = chunks[0][2]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(72, rect.x); + assertEquals(1, rect.y); + assertEquals(1, rect.width); + assertEquals(1, rect.height); + + // Row 2 + c = chunks[1][0]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL, c.type); + assertEquals(1, rect.x); + assertEquals(2, rect.y); + assertEquals(1, rect.width); + assertEquals(48, rect.height); + + c = chunks[1][1]; + rect = c.rect; + assertEquals(Chunk.TYPE_FIXED, c.type); + assertEquals(2, rect.x); + assertEquals(2, rect.y); + assertEquals(70, rect.width); + assertEquals(48, rect.height); + + c = chunks[1][2]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL, c.type); + assertEquals(72, rect.x); + assertEquals(2, rect.y); + assertEquals(1, rect.width); + assertEquals(48, rect.height); + + // Row 3 + c = chunks[2][0]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(1, rect.x); + assertEquals(50, rect.y); + assertEquals(1, rect.width); + assertEquals(1, rect.height); + + c = chunks[2][1]; + rect = c.rect; + assertEquals(Chunk.TYPE_VERTICAL, c.type); + assertEquals(2, rect.x); + assertEquals(50, rect.y); + assertEquals(70, rect.width); + assertEquals(1, rect.height); + + c = chunks[2][2]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(72, rect.x); + assertEquals(50, rect.y); + assertEquals(1, rect.width); + assertEquals(1, rect.height); + } + + public void test9Patch2() throws Exception { + String fileName = DIR + "patched2.9.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + assertTrue(image.hasNinePatchExtension()); + assertTrue(image.ensure9Patch()); + + // patches + List<Tick> horizontalPatches = image.getHorizontalPatches(); + List<Tick> verticalPatches = image.getVerticalPatches(); + assertEquals(5, horizontalPatches.size()); + assertEquals(7, verticalPatches.size()); + + NinePatchedImage.Chunk[][] chunks = null; + chunks = image.getChunks(chunks); + + // vertical chunk size + assertEquals(7, chunks.length); + + // horizontal chunk size + for (int i = 0; i < chunks.length; i++) { + assertEquals(5, chunks[i].length); + } + + NinePatchedImage.Chunk c = null; + Rectangle rect = null; + + // Row 1 + c = chunks[0][0]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(1, rect.x); + assertEquals(1, rect.y); + assertEquals(1, rect.width); + assertEquals(1, rect.height); + + c = chunks[0][1]; + rect = c.rect; + assertEquals(Chunk.TYPE_VERTICAL, c.type); + assertEquals(2, rect.x); + assertEquals(1, rect.y); + assertEquals(34, rect.width); + assertEquals(1, rect.height); + + c = chunks[0][2]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(36, rect.x); + assertEquals(1, rect.y); + assertEquals(1, rect.width); + assertEquals(1, rect.height); + + c = chunks[0][3]; + rect = c.rect; + assertEquals(Chunk.TYPE_VERTICAL, c.type); + assertEquals(37, rect.x); + assertEquals(1, rect.y); + assertEquals(35, rect.width); + assertEquals(1, rect.height); + + c = chunks[0][4]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(72, rect.x); + assertEquals(1, rect.y); + assertEquals(1, rect.width); + assertEquals(1, rect.height); + + // Row 2 + c = chunks[1][0]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL, c.type); + assertEquals(1, rect.x); + assertEquals(2, rect.y); + assertEquals(1, rect.width); + assertEquals(7, rect.height); + + c = chunks[1][1]; + rect = c.rect; + assertEquals(Chunk.TYPE_FIXED, c.type); + assertEquals(2, rect.x); + assertEquals(2, rect.y); + assertEquals(34, rect.width); + assertEquals(7, rect.height); + + c = chunks[1][2]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL, c.type); + assertEquals(36, rect.x); + assertEquals(2, rect.y); + assertEquals(1, rect.width); + assertEquals(7, rect.height); + + c = chunks[1][3]; + rect = c.rect; + assertEquals(Chunk.TYPE_FIXED, c.type); + assertEquals(37, rect.x); + assertEquals(2, rect.y); + assertEquals(35, rect.width); + assertEquals(7, rect.height); + + c = chunks[1][4]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL, c.type); + assertEquals(72, rect.x); + assertEquals(2, rect.y); + assertEquals(1, rect.width); + assertEquals(7, rect.height); + + // Row 3 + c = chunks[2][0]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(1, rect.x); + assertEquals(9, rect.y); + assertEquals(1, rect.width); + assertEquals(4, rect.height); + + c = chunks[2][1]; + rect = c.rect; + assertEquals(Chunk.TYPE_VERTICAL, c.type); + assertEquals(2, rect.x); + assertEquals(9, rect.y); + assertEquals(34, rect.width); + assertEquals(4, rect.height); + + c = chunks[2][2]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(36, rect.x); + assertEquals(9, rect.y); + assertEquals(1, rect.width); + assertEquals(4, rect.height); + + c = chunks[2][3]; + rect = c.rect; + assertEquals(Chunk.TYPE_VERTICAL, c.type); + assertEquals(37, rect.x); + assertEquals(9, rect.y); + assertEquals(35, rect.width); + assertEquals(4, rect.height); + + c = chunks[2][4]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(72, rect.x); + assertEquals(9, rect.y); + assertEquals(1, rect.width); + assertEquals(4, rect.height); + + // Row 4 + c = chunks[3][0]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL, c.type); + assertEquals(1, rect.x); + assertEquals(13, rect.y); + assertEquals(1, rect.width); + assertEquals(13, rect.height); + + c = chunks[3][1]; + rect = c.rect; + assertEquals(Chunk.TYPE_FIXED, c.type); + assertEquals(2, rect.x); + assertEquals(13, rect.y); + assertEquals(34, rect.width); + assertEquals(13, rect.height); + + c = chunks[3][2]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL, c.type); + assertEquals(36, rect.x); + assertEquals(13, rect.y); + assertEquals(1, rect.width); + assertEquals(13, rect.height); + + c = chunks[3][3]; + rect = c.rect; + assertEquals(Chunk.TYPE_FIXED, c.type); + assertEquals(37, rect.x); + assertEquals(13, rect.y); + assertEquals(35, rect.width); + assertEquals(13, rect.height); + + c = chunks[3][4]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL, c.type); + assertEquals(72, rect.x); + assertEquals(13, rect.y); + assertEquals(1, rect.width); + assertEquals(13, rect.height); + + // Row 5 + c = chunks[4][0]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(1, rect.x); + assertEquals(26, rect.y); + assertEquals(1, rect.width); + assertEquals(12, rect.height); + + c = chunks[4][1]; + rect = c.rect; + assertEquals(Chunk.TYPE_VERTICAL, c.type); + assertEquals(2, rect.x); + assertEquals(26, rect.y); + assertEquals(34, rect.width); + assertEquals(12, rect.height); + + c = chunks[4][2]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(36, rect.x); + assertEquals(26, rect.y); + assertEquals(1, rect.width); + assertEquals(12, rect.height); + + c = chunks[4][3]; + rect = c.rect; + assertEquals(Chunk.TYPE_VERTICAL, c.type); + assertEquals(37, rect.x); + assertEquals(26, rect.y); + assertEquals(35, rect.width); + assertEquals(12, rect.height); + + c = chunks[4][4]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(72, rect.x); + assertEquals(26, rect.y); + assertEquals(1, rect.width); + assertEquals(12, rect.height); + + // Row 6 + c = chunks[5][0]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL, c.type); + assertEquals(1, rect.x); + assertEquals(38, rect.y); + assertEquals(1, rect.width); + assertEquals(12, rect.height); + + c = chunks[5][1]; + rect = c.rect; + assertEquals(Chunk.TYPE_FIXED, c.type); + assertEquals(2, rect.x); + assertEquals(38, rect.y); + assertEquals(34, rect.width); + assertEquals(12, rect.height); + + c = chunks[5][2]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL, c.type); + assertEquals(36, rect.x); + assertEquals(38, rect.y); + assertEquals(1, rect.width); + assertEquals(12, rect.height); + + c = chunks[5][3]; + rect = c.rect; + assertEquals(Chunk.TYPE_FIXED, c.type); + assertEquals(37, rect.x); + assertEquals(38, rect.y); + assertEquals(35, rect.width); + assertEquals(12, rect.height); + + c = chunks[5][4]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL, c.type); + assertEquals(72, rect.x); + assertEquals(38, rect.y); + assertEquals(1, rect.width); + assertEquals(12, rect.height); + + // Row 7 + c = chunks[6][0]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(1, rect.x); + assertEquals(50, rect.y); + assertEquals(1, rect.width); + assertEquals(1, rect.height); + + c = chunks[6][1]; + rect = c.rect; + assertEquals(Chunk.TYPE_VERTICAL, c.type); + assertEquals(2, rect.x); + assertEquals(50, rect.y); + assertEquals(34, rect.width); + assertEquals(1, rect.height); + + c = chunks[6][2]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(36, rect.x); + assertEquals(50, rect.y); + assertEquals(1, rect.width); + assertEquals(1, rect.height); + + c = chunks[6][3]; + rect = c.rect; + assertEquals(Chunk.TYPE_VERTICAL, c.type); + assertEquals(37, rect.x); + assertEquals(50, rect.y); + assertEquals(35, rect.width); + assertEquals(1, rect.height); + + c = chunks[6][4]; + rect = c.rect; + assertEquals(Chunk.TYPE_HORIZONTAL | Chunk.TYPE_VERTICAL, c.type); + assertEquals(72, rect.x); + assertEquals(50, rect.y); + assertEquals(1, rect.width); + assertEquals(1, rect.height); + } + + public void testContentArea() throws Exception { + String fileName = DIR + "content-area.9.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + assertTrue(image.hasNinePatchExtension()); + assertTrue(image.ensure9Patch()); + + // contents area + List<Tick> horizontalContentsArea = image.getHorizontalContents(); + List<Tick> verticalContentsArea = image.getVerticalContents(); + assertEquals(3, horizontalContentsArea.size()); + assertEquals(3, verticalContentsArea.size()); + + // content area rectangle + Rectangle contentsArea = image.getContentArea(); + assertEquals(new Rectangle(19, 13, 35, 25), contentsArea); + } + + public void testContentAreaOneDot() throws Exception { + String fileName = DIR + "content-area-one-dot.9.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + assertTrue(image.hasNinePatchExtension()); + assertTrue(image.ensure9Patch()); + + // contents area + List<Tick> horizontalContentsArea = image.getHorizontalContents(); + List<Tick> verticalContentsArea = image.getVerticalContents(); + assertEquals(3, horizontalContentsArea.size()); + assertEquals(3, verticalContentsArea.size()); + + // content area rectangle + Rectangle contentsArea = image.getContentArea(); + assertEquals(new Rectangle(19, 13, 1, 1), contentsArea); + } + + public void testContentAreaTwoDots() throws Exception { + String fileName = DIR + "content-area-two-dots.9.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + assertTrue(image.hasNinePatchExtension()); + assertTrue(image.ensure9Patch()); + + // contents area + List<Tick> horizontalContentsArea = image.getHorizontalContents(); + List<Tick> verticalContentsArea = image.getVerticalContents(); + assertEquals(5, horizontalContentsArea.size()); + assertEquals(5, verticalContentsArea.size()); + + // content area rectangle + Rectangle contentsArea = image.getContentArea(); + assertEquals(new Rectangle(19, 13, 35, 25), contentsArea); + + String fileName2 = DIR + "content-area.9.png"; + NinePatchedImage image2 = new NinePatchedImage(getClass() + .getResourceAsStream(fileName2), fileName2); + assertNotNull(image2); + + assertTrue(image2.hasNinePatchExtension()); + assertTrue(image2.ensure9Patch()); + + // content area rectangle + Rectangle contentsArea2 = image2.getContentArea(); + assertEquals(contentsArea2, contentsArea); + } + + public void testBadPatches() throws Exception { + String fileName = DIR + "patched-with-badpatches.9.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + assertTrue(image.hasNinePatchExtension()); + assertTrue(image.ensure9Patch()); + + Chunk[][] chunks = null; + chunks = image.getChunks(chunks); + + // vertical chunk size + assertEquals(5, chunks.length); + + // horizontal chunk size + for (int i = 0; i < chunks.length; i++) { + assertEquals(7, chunks[i].length); + } + + chunks = image.getCorruptedChunks(chunks); + + Chunk c = null; + + // collect bad patches + List<Point> badPatches = new ArrayList<Point>(5 * 7); + for (int y = 0; y < chunks.length; y++) { + for (int x = 0; x < chunks[0].length; x++) { + c = chunks[y][x]; + if ((c.type & Chunk.TYPE_CORRUPT) != 0x0) { + badPatches.add(new Point(y, x)); + } + } + } + + assertEquals(15, badPatches.size()); + + assertTrue(badPatches.contains(new Point(0, 3))); + + assertTrue(badPatches.contains(new Point(1, 1))); + assertTrue(badPatches.contains(new Point(1, 2))); + assertTrue(badPatches.contains(new Point(1, 3))); + assertTrue(badPatches.contains(new Point(1, 4))); + assertTrue(badPatches.contains(new Point(1, 5))); + + assertTrue(badPatches.contains(new Point(2, 1))); + assertTrue(badPatches.contains(new Point(2, 5))); + + assertTrue(badPatches.contains(new Point(3, 0))); + assertTrue(badPatches.contains(new Point(3, 1))); + assertTrue(badPatches.contains(new Point(3, 5))); + assertTrue(badPatches.contains(new Point(3, 6))); + + assertTrue(badPatches.contains(new Point(4, 1))); + assertTrue(badPatches.contains(new Point(4, 3))); + assertTrue(badPatches.contains(new Point(4, 5))); + } + + public void testProjection() throws Exception { + // top + String fileName = DIR + "patched3.9.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + assertTrue(image.hasNinePatchExtension()); + assertTrue(image.ensure9Patch()); + + ImageData data = image.getImageData(); + assertEquals(72 + 2, data.width); + assertEquals(50 + 2, data.height); + + int width = 72 * 2; + int height = 50 * 2; + + Chunk[][] chunks = null; + chunks = image.getChunks(chunks); + + Projection[][] projections = null; + projections = image.getProjections(width, height, projections); + + assertEquals(chunks.length, projections.length); + for (int i = 0; i < chunks.length; i++) { + assertEquals(chunks[i].length, projections[i].length); + } + + for (int y = 0; y < projections.length; y++) { + for (int x = 0; x < projections[y].length; x++) { + assertEquals(projections[y][x].src, chunks[y][x].rect); + + // If chunk type is FIXED. Same projection size as original + // chunk. + if (projections[y][x].chunk.type == Chunk.TYPE_FIXED) { + assertEquals(projections[y][x].dest.width, chunks[y][x].rect.width); + assertEquals(projections[y][x].dest.height, chunks[y][x].rect.height); + } + } + } + + Projection p = null; + Rectangle rect = null; + + // Check start position + p = projections[0][0]; + + // src position start from 1, 9-patch row and column included. + assertEquals(1, p.src.x); + assertEquals(1, p.src.y); + + // dest position start from 0, 9-patch row and column ignored. + assertEquals(0, p.dest.x); + assertEquals(0, p.dest.y); + + // row 1 + p = projections[0][0]; + rect = p.dest; + assertEquals(0, rect.x); + assertEquals(0, rect.y); + assertEquals(74, rect.width); + assertEquals(5, rect.height); + + p = projections[0][1]; + rect = p.dest; + assertEquals(74, rect.x); + assertEquals(0, rect.y); + assertEquals(62, rect.width); + assertEquals(5, rect.height); + + p = projections[0][2]; + rect = p.dest; + assertEquals(136, rect.x); + assertEquals(0, rect.y); + assertEquals(8, rect.width); + assertEquals(5, rect.height); + + // row 2 + p = projections[1][0]; + rect = p.dest; + assertEquals(0, rect.x); + assertEquals(5, rect.y); + assertEquals(74, rect.width); + assertEquals(24, rect.height); + + p = projections[1][1]; + rect = p.dest; + assertEquals(74, rect.x); + assertEquals(5, rect.y); + assertEquals(62, rect.width); + assertEquals(24, rect.height); + + p = projections[1][2]; + rect = p.dest; + assertEquals(136, rect.x); + assertEquals(5, rect.y); + assertEquals(8, rect.width); + assertEquals(24, rect.height); + + // row 3 + p = projections[2][0]; + rect = p.dest; + assertEquals(0, rect.x); + assertEquals(29, rect.y); + assertEquals(74, rect.width); + assertEquals(58, rect.height); + + p = projections[2][1]; + rect = p.dest; + assertEquals(74, rect.x); + assertEquals(29, rect.y); + assertEquals(62, rect.width); + assertEquals(58, rect.height); + + p = projections[2][2]; + rect = p.dest; + assertEquals(136, rect.x); + assertEquals(29, rect.y); + assertEquals(8, rect.width); + assertEquals(58, rect.height); + + // row 4 + p = projections[3][0]; + rect = p.dest; + assertEquals(0, rect.x); + assertEquals(87, rect.y); + assertEquals(74, rect.width); + assertEquals(13, rect.height); + + p = projections[3][1]; + rect = p.dest; + assertEquals(74, rect.x); + assertEquals(87, rect.y); + assertEquals(62, rect.width); + assertEquals(13, rect.height); + + p = projections[3][2]; + rect = p.dest; + assertEquals(136, rect.x); + assertEquals(87, rect.y); + assertEquals(8, rect.width); + assertEquals(13, rect.height); + } + + public void testReadLayoutBoundsOnlyImage() throws Exception { + String fileName = DIR + "layout-bounds-only.9.png"; + NinePatchedImage image = new NinePatchedImage(getClass() + .getResourceAsStream(fileName), fileName); + assertNotNull(image); + + ImageData data = image.getImageData(); + int width = data.width; + int height = data.height; + + assertEquals(74, width); + assertEquals(52, height); + + assertTrue(image.hasNinePatchExtension()); + assertTrue(image.ensure9Patch()); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategyTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategyTest.java new file mode 100644 index 000000000..9c7e25d4b --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategyTest.java @@ -0,0 +1,335 @@ +/* + * 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.eclipse.adt.internal.editors.formatting; + +import com.android.ide.common.xml.XmlFormatPreferences; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Document; +import org.eclipse.text.edits.MalformedTreeException; +import org.eclipse.text.edits.ReplaceEdit; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class AndroidXmlFormattingStrategyTest extends TestCase { + // In the given before document, replace in the range replaceStart to replaceEnd + // the formatted string, and assert that it's identical to the given after string + private void check(String before, int replaceStart, int replaceEnd, String formatted, + String expected, XmlFormatPreferences prefs) + throws MalformedTreeException, BadLocationException { + Document document = new Document(); + document.set(before); + ReplaceEdit edit = AndroidXmlFormattingStrategy.createReplaceEdit(document, replaceStart, + replaceEnd, formatted, prefs); + assertNotNull(edit); + edit.apply(document); + String contents = document.get(); + // Ensure that we don't have any mangled CRLFs + char prev = 0; + boolean haveCrlf = false; + for (int i = 0, n = contents.length(); i < n; i++) { + char c = contents.charAt(i); + if (c == '\r') { + haveCrlf = true; + } + if (!(c != '\r' || prev != '\r')) { + fail("Mangled document: Found adjacent \\r's starting at " + i + + ": " + contents.substring(i - 1, Math.min(contents.length(), i + 10)) + + "..."); + } + if (haveCrlf && c == '\n' && prev != '\r') { + fail("Mangled document: In a CRLF document, found \\n without preceeding \\r"); + } + + prev = c; + } + + assertEquals(expected, contents); + } + + // In the given before document, replace the range indicated by [ and ] with the given + // formatted string, and assert that it's identical to the given after string + private void check( + String before, String insert, String expected, + XmlFormatPreferences prefs) + throws MalformedTreeException, BadLocationException { + int replaceStart = before.indexOf('['); + assertTrue(replaceStart != -1); + before = before.substring(0, replaceStart) + before.substring(replaceStart + 1); + + int replaceEnd = before.indexOf(']'); + assertTrue(replaceEnd != -1); + before = before.substring(0, replaceEnd) + before.substring(replaceEnd + 1); + + check(before, replaceStart, replaceEnd, insert, expected, prefs); + } + + public void test1() throws Exception { + check( + // Before + "<root>\n" + + "[ <element/>\n" + + " <second/>\n" + + "]\n" + + "</root>\n", + + // Insert + " <element/>\n" + + " <second/>\n", + + // After + "<root>\n" + + " <element/>\n" + + " <second/>\n" + + "\n" + + "</root>\n", + + XmlFormatPreferences.defaults()); + } + + public void test2() throws Exception { + XmlFormatPreferences prefs = XmlFormatPreferences.defaults(); + prefs.removeEmptyLines = true; + + check( + // Before + "<root>\n" + + "\n" + + "\n" + + "[ <element/>\n" + + " <second/>\n" + + "]\n" + + "\n" + + "\n" + + "</root>\n", + + // Insert + " <element/>\n" + + " <second/>\n", + + // After + "<root>\n" + + " <element/>\n" + + " <second/>\n" + + "</root>\n", + + prefs); + } + + public void test3() throws Exception { + XmlFormatPreferences prefs = XmlFormatPreferences.defaults(); + prefs.removeEmptyLines = true; + + check( + // Before + "<root>\n" + + "\n" + + "\n" + + " [<element/>\n" + + " <second/>]\n" + + "\n" + + "\n" + + "\n" + + "</root>\n", + + // Insert + " <element/>\n" + + " <second/>", + + // After + "<root>\n" + + " <element/>\n" + + " <second/>\n" + + "</root>\n", + + prefs); + } + + public void test4() throws Exception { + check( + "<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" + + "\n" + + " [<TextView\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_centerHorizontal=\"true\"\n" + + " android:layout_centerVertical=\"true\"\n" + + " android:text=\"foo\"\n" + + " tools:context=\".MainActivity\" />]\n" + + "\n" + + "</RelativeLayout>\n", + + // Insert + "\n" + + " <TextView\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_centerHorizontal=\"true\"\n" + + " android:layout_centerVertical=\"true\"\n" + + " android:text=\"foo\"\n" + + " tools:context=\".MainActivity\" />\n", + + // After + "<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" + + "\n" + + " <TextView\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_centerHorizontal=\"true\"\n" + + " android:layout_centerVertical=\"true\"\n" + + " android:text=\"foo\"\n" + + " tools:context=\".MainActivity\" />\n" + + "\n" + + "</RelativeLayout>\n", + + XmlFormatPreferences.defaults()); + } + + public void testCrLf1() throws Exception { + check( + // Before + "<root>\r\n" + + "[ <element/>\r\n" + + " <second/>\r\n" + + "]\r\n" + + "</root>\r\n", + + // Insert + " <element/>\r\n" + + " <second/>\r\n", + + // After + "<root>\r\n" + + " <element/>\r\n" + + " <second/>\r\n" + + "\r\n" + + "</root>\r\n", + + XmlFormatPreferences.defaults()); + } + + public void testCrLf2() throws Exception { + XmlFormatPreferences prefs = XmlFormatPreferences.defaults(); + prefs.removeEmptyLines = true; + + check( + // Before + "<root>\r\n" + + "\r\n" + + "\r\n" + + "[ <element/>\r\n" + + " <second/>\r\n" + + "]\r\n" + + "\r\n" + + "\r\n" + + "</root>\r\n", + + // Insert + " <element/>\r\n" + + " <second/>\r\n", + + // After + "<root>\r\n" + + " <element/>\r\n" + + " <second/>\r\n" + + "</root>\r\n", + + prefs); + } + + public void testCrLf3() throws Exception { + XmlFormatPreferences prefs = XmlFormatPreferences.defaults(); + prefs.removeEmptyLines = true; + + check( + // Before + "<root>\r\n" + + "\r\n" + + "\r\n" + + " [<element/>\r\n" + + " <second/>]\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "</root>\r\n", + + // Insert + " <element/>\r\n" + + " <second/>", + + // After + "<root>\r\n" + + " <element/>\r\n" + + " <second/>\r\n" + + "</root>\r\n", + + prefs); + } + + + public void testCrlf4() throws Exception { + check( + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n" + + " xmlns:tools=\"http://schemas.android.com/tools\"\r\n" + + " android:layout_width=\"match_parent\"\r\n" + + " android:layout_height=\"match_parent\" >\r\n" + + "\r\n" + + " [<TextView\r\n" + + " android:layout_width=\"wrap_content\"\r\n" + + " android:layout_height=\"wrap_content\"\r\n" + + " android:layout_centerHorizontal=\"true\"\r\n" + + " android:layout_centerVertical=\"true\"\r\n" + + " android:text=\"foo\"\r\n" + + " tools:context=\".MainActivity\" />]\r\n" + + "\r\n" + + "</RelativeLayout>\r\n", + + // Insert + "\r\n" + + " <TextView\r\n" + + " android:layout_width=\"wrap_content\"\r\n" + + " android:layout_height=\"wrap_content\"\r\n" + + " android:layout_centerHorizontal=\"true\"\r\n" + + " android:layout_centerVertical=\"true\"\r\n" + + " android:text=\"foo\"\r\n" + + " tools:context=\".MainActivity\" />\r\n", + + // After + "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n" + + " xmlns:tools=\"http://schemas.android.com/tools\"\r\n" + + " android:layout_width=\"match_parent\"\r\n" + + " android:layout_height=\"match_parent\" >\r\n" + + "\r\n" + + " <TextView\r\n" + + " android:layout_width=\"wrap_content\"\r\n" + + " android:layout_height=\"wrap_content\"\r\n" + + " android:layout_centerHorizontal=\"true\"\r\n" + + " android:layout_centerVertical=\"true\"\r\n" + + " android:text=\"foo\"\r\n" + + " tools:context=\".MainActivity\" />\r\n" + + "\r\n" + + "</RelativeLayout>\r\n", + + XmlFormatPreferences.defaults()); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/ExplodeRenderingHelperTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/ExplodeRenderingHelperTest.java new file mode 100644 index 000000000..0e528674a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/ExplodeRenderingHelperTest.java @@ -0,0 +1,256 @@ +/* + * 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.eclipse.adt.internal.editors.layout; + +import com.android.SdkConstants; +import com.android.ide.eclipse.adt.internal.editors.mock.MockXmlNode; + +import org.w3c.dom.Node; + +import java.util.HashSet; + +import junit.framework.TestCase; + +public class ExplodeRenderingHelperTest extends TestCase { + + private final HashSet<String> mLayoutNames = new HashSet<String>(); + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mLayoutNames.add("LinearLayout"); + mLayoutNames.add("RelativeLayout"); + } + + public void testSingleHorizontalLinearLayout() { + // Single layout, horizontal, 2 buttons. + MockXmlNode layout = createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton(), createButton()} ); + + ExplodedRenderingHelper helper = new ExplodedRenderingHelper(layout, mLayoutNames); + assertEquals(1, helper.getHeightPadding()); + assertEquals(1, helper.getWidthPadding()); + } + + public void testSingleVerticalLinearLayout() { + // Single layout, horizontal, with 2 buttons. + // LinearLayout(H:[Button Button]) + MockXmlNode layout = createLinearLayout(false /*horizontal*/, + new MockXmlNode[] { createButton(), createButton()} ); + + ExplodedRenderingHelper helper = new ExplodedRenderingHelper(layout, mLayoutNames); + assertEquals(1, helper.getWidthPadding()); + assertEquals(1, helper.getHeightPadding()); + } + + public void testEmbeddedLinearLayouts() { + /* + * LinearLayout(vertical): + * LinearLayout(H:[Button Button]) + * LinearLayout(H:[Button Button Button]) + * + * Result should be 2 in x, 3 in y + */ + MockXmlNode layout = createLinearLayout(false /*horizontal*/, + new MockXmlNode[] { + createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton(), createButton()}), + createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton(), createButton(), createButton()}), + } ); + + ExplodedRenderingHelper helper = new ExplodedRenderingHelper(layout, mLayoutNames); + assertEquals(2, helper.getWidthPadding()); + assertEquals(3, helper.getHeightPadding()); + } + + public void testSimpleRelativeLayoutWithOneLinearLayouts() { + /* + * RelativeLayout: + * LinearLayout(H:[Button Button]) + * + * Result should be 2 in x, 2 in y + */ + MockXmlNode layout = createRelativeLayout( + new MockXmlNode[] { + createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton(), createButton()}), + } ); + + ExplodedRenderingHelper helper = new ExplodedRenderingHelper(layout, mLayoutNames); + assertEquals(2, helper.getWidthPadding()); + assertEquals(2, helper.getHeightPadding()); + } + + public void /*test*/RelativeLayoutWithVerticalLinearLayouts() { + //FIXME: Reenable once the relative layout are properly supported. + /* + * Children of the relative layouts, one below the other. + * Each with only buttons in them. + * RelativeLayout: + * LinearLayout(H:[Button Button]) + * ^ + * LinearLayout(H:[Button Button]) + * + * Result should be 2 in x, 3 in y + */ + + // create the linearlayouts. + MockXmlNode linear1 = createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton(), createButton()}); + linear1.addAttributes(SdkConstants.NS_RESOURCES, "id", "@+id/linear1"); + + MockXmlNode linear2 = createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton(), createButton()}); + linear2.addAttributes(SdkConstants.NS_RESOURCES, "id", "@+id/linear2"); + + // position linear2 below linear1 + linear2.addAttributes(SdkConstants.NS_RESOURCES, "layout_below", "@+id/linear1"); + + + MockXmlNode layout = createRelativeLayout(new MockXmlNode[] { linear1, linear2 } ); + + ExplodedRenderingHelper helper = new ExplodedRenderingHelper(layout, mLayoutNames); + assertEquals(2, helper.getWidthPadding()); + assertEquals(3, helper.getHeightPadding()); + } + + public void /*test*/RelativeLayoutWithVerticalLinearLayouts2() { + //FIXME: Reenable once the relative layout are properly supported. + /* + * Children of the relative layouts, one above the other. + * Each with only buttons in them. + * RelativeLayout: + * LinearLayout(H:[Button Button]) + * v + * LinearLayout(H:[Button Button]) + * + * Result should be 2 in x, 3 in y + */ + + // create the linearlayouts. + MockXmlNode linear1 = createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton(), createButton() } ); + linear1.addAttributes(SdkConstants.NS_RESOURCES, "id", "@+id/linear1"); + + MockXmlNode linear2 = createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton(), createButton() } ); + linear2.addAttributes(SdkConstants.NS_RESOURCES, "id", "@+id/linear2"); + + // position linear2 below linear1 + linear2.addAttributes(SdkConstants.NS_RESOURCES, "layout_above", "@+id/linear1"); + + + MockXmlNode layout = createRelativeLayout(new MockXmlNode[] { linear1, linear2 } ); + + ExplodedRenderingHelper helper = new ExplodedRenderingHelper(layout, mLayoutNames); + assertEquals(2, helper.getWidthPadding()); + assertEquals(3, helper.getHeightPadding()); + } + + public void /*test*/ComplexRelativeLayout() { + //FIXME: Reenable once the relative layout are properly supported. + /* + * RelativeLayout: + * + * < LinearLayout1(V: [button]) > LinearLayout2(V: [button]) + * v + * Button1 > LinearLayout3(V: [button]) < Button2 + * v + * < LinearLayout4(V: [button]) + * ^ + * <LinearLayout5(V: [button]) + * + * Result should be 4 in x, 5 in y + */ + + // create the elements + MockXmlNode button1 = createButton(); + button1.addAttributes(SdkConstants.NS_RESOURCES, "id", "@+id/button1"); + + MockXmlNode button2 = createButton(); + button2.addAttributes(SdkConstants.NS_RESOURCES, "id", "@+id/button2"); + + MockXmlNode linear1 = createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton() } ); + linear1.addAttributes(SdkConstants.NS_RESOURCES, "id", "@+id/linear1"); + + MockXmlNode linear2 = createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton() } ); + linear2.addAttributes(SdkConstants.NS_RESOURCES, "id", "@+id/linear2"); + + MockXmlNode linear3 = createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton() } ); + linear3.addAttributes(SdkConstants.NS_RESOURCES, "id", "@+id/linear3"); + + MockXmlNode linear4 = createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton() } ); + linear4.addAttributes(SdkConstants.NS_RESOURCES, "id", "@+id/linear4"); + + MockXmlNode linear5 = createLinearLayout(true /*horizontal*/, + new MockXmlNode[] { createButton() } ); + linear5.addAttributes(SdkConstants.NS_RESOURCES, "id", "@+id/linear5"); + + + // link them + button1.addAttributes(SdkConstants.NS_RESOURCES, "layout_toLeftOf", "@+id/linear3"); + + button2.addAttributes(SdkConstants.NS_RESOURCES, "layout_toRightOf", "@+id/linear3"); + + linear1.addAttributes(SdkConstants.NS_RESOURCES, "layout_toRightOf", "@+id/linear3"); + linear1.addAttributes(SdkConstants.NS_RESOURCES, "layout_toLeftOf", "@+id/linear2"); + linear1.addAttributes(SdkConstants.NS_RESOURCES, "layout_above", "@+id/button2"); + + linear3.addAttributes(SdkConstants.NS_RESOURCES, "layout_above", "@+id/linear4"); + + linear4.addAttributes(SdkConstants.NS_RESOURCES, "layout_toRightOf", "@+id/button1"); + + linear5.addAttributes(SdkConstants.NS_RESOURCES, "layout_toRightOf", "@+id/linear4"); + linear5.addAttributes(SdkConstants.NS_RESOURCES, "layout_below", "@+id/linear4"); + + MockXmlNode layout = createRelativeLayout( + new MockXmlNode[] { + button1, button2, linear1, linear2, linear3, linear4, linear5 } ); + + ExplodedRenderingHelper helper = new ExplodedRenderingHelper(layout, mLayoutNames); + assertEquals(4, helper.getWidthPadding()); + assertEquals(5, helper.getHeightPadding()); + } + + + // ----- helper to deal with mocks + + private MockXmlNode createButton() { + return new MockXmlNode(null, "Button", Node.ELEMENT_NODE, null); + } + + private MockXmlNode createLinearLayout(boolean horizontal, MockXmlNode[] children) { + MockXmlNode layout = new MockXmlNode(null, "LinearLayout", Node.ELEMENT_NODE, children); + + layout.addAttributes(SdkConstants.NS_RESOURCES, "orientation", + horizontal ? "horizontal" : "vertical"); + + return layout; + } + + private MockXmlNode createRelativeLayout(MockXmlNode[] children) { + MockXmlNode layout = new MockXmlNode(null, "RelativeLayout", Node.ELEMENT_NODE, children); + + return layout; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParserTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParserTest.java new file mode 100644 index 000000000..5cac663d7 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParserTest.java @@ -0,0 +1,256 @@ +/* + * 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.eclipse.adt.internal.editors.layout; + +import com.android.SdkConstants; +import com.android.ide.common.api.IAttributeInfo.Format; +import com.android.ide.common.resources.platform.AttributeInfo; +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.mock.MockXmlNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.resources.Density; + +import org.w3c.dom.Node; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.util.HashMap; + +import junit.framework.TestCase; + +public class UiElementPullParserTest extends TestCase { + + private UiElementNode ui; + private HashMap<String, String> button1Map; + private HashMap<String, String> button2Map; + private HashMap<String, String> textMap; + + private TextAttributeDescriptor createTextAttrDesc(String xmlName) { + return new TextAttributeDescriptor( + xmlName, // xmlLocalName + SdkConstants.NS_RESOURCES, + new AttributeInfo(xmlName, Format.STRING_SET) + ); + } + + @Override + protected void setUp() throws Exception { + // set up some basic descriptors. + // We have button, textview, linear layout, relative layout. + // only the layouts have children (all 4 descriptors possible) + // Also add some dummy attributes. + ElementDescriptor buttonDescriptor = new ElementDescriptor("Button", "Button", "", "", + new AttributeDescriptor[] { + createTextAttrDesc("name"), + createTextAttrDesc("text"), + }, + new ElementDescriptor[] {}, false); + + ElementDescriptor textDescriptor = new ElementDescriptor("TextView", "TextView", "", "", + new AttributeDescriptor[] { + createTextAttrDesc("name"), + createTextAttrDesc("text"), + }, + new ElementDescriptor[] {}, false); + + ElementDescriptor linearDescriptor = new ElementDescriptor("LinearLayout", "Linear Layout", + "", "", + new AttributeDescriptor[] { + createTextAttrDesc("orientation"), + }, + new ElementDescriptor[] { }, false); + + ElementDescriptor relativeDescriptor = new ElementDescriptor("RelativeLayout", + "Relative Layout", "", "", + new AttributeDescriptor[] { + createTextAttrDesc("orientation"), + }, + new ElementDescriptor[] { }, false); + + ElementDescriptor[] a = new ElementDescriptor[] { + buttonDescriptor, textDescriptor, linearDescriptor, relativeDescriptor + }; + + linearDescriptor.setChildren(a); + relativeDescriptor.setChildren(a); + + // document descriptor + ElementDescriptor rootDescriptor = new ElementDescriptor("root", "", "", "", + new AttributeDescriptor[] { }, a, false); + + + ui = new UiElementNode(rootDescriptor); + + /* create a dummy XML file. + * <LinearLayout android:orientation="vertical"> + * <Button android:name="button1" android:text="button1text"/> + * <RelativeLayout android:orientation="toto"> + * <Button android:name="button2" android:text="button2text"/> + * <TextView android:name="text1" android:text="text1text"/> + * </RelativeLayout> + * </LinearLayout> + */ + MockXmlNode button1 = new MockXmlNode(null /* namespace */, "Button", Node.ELEMENT_NODE, + null); + button1.addAttributes(SdkConstants.NS_RESOURCES, "name", "button1"); + button1.addAttributes(SdkConstants.NS_RESOURCES, "text", "button1text"); + + // create a map of the attributes we add to the multi-attribute nodes so that + // we can more easily test the values when we parse the XML. + // This is due to some attributes showing in a certain order for a node and in a different + // order in another node. Since the order doesn't matter, we just simplify the test. + button1Map = new HashMap<String, String>(); + button1Map.put("name", "button1"); + button1Map.put("text", "button1text"); + + MockXmlNode button2 = new MockXmlNode(null /* namespace */, "Button", Node.ELEMENT_NODE, + null); + button2.addAttributes(SdkConstants.NS_RESOURCES, "name", "button2"); + button2.addAttributes(SdkConstants.NS_RESOURCES, "text", "button2text"); + + button2Map = new HashMap<String, String>(); + button2Map.put("name", "button2"); + button2Map.put("text", "button2text"); + + MockXmlNode text = new MockXmlNode(null /* namespace */, "TextView", Node.ELEMENT_NODE, + null); + text.addAttributes(SdkConstants.NS_RESOURCES, "name", "text1"); + text.addAttributes(SdkConstants.NS_RESOURCES, "text", "text1text"); + + textMap = new HashMap<String, String>(); + textMap.put("name", "text1"); + textMap.put("text", "text1text"); + + MockXmlNode relative = new MockXmlNode(null /* namespace */, "RelativeLayout", + Node.ELEMENT_NODE, new MockXmlNode[] { button2, text }); + relative.addAttributes(SdkConstants.NS_RESOURCES, "orientation", "toto"); + + MockXmlNode linear = new MockXmlNode(null /* namespace */, "LinearLayout", + Node.ELEMENT_NODE, new MockXmlNode[] { button1, relative }); + linear.addAttributes(SdkConstants.NS_RESOURCES, "orientation", "vertical"); + + MockXmlNode root = new MockXmlNode(null /* namespace */, "root", Node.ELEMENT_NODE, + new MockXmlNode[] { linear }); + + // put the namespace/prefix in place + root.setPrefix(SdkConstants.NS_RESOURCES, "android"); + + // load the xml into the UiElementNode + ui.loadFromXmlNode(root); + + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testParser() { + try { + // wrap the parser around the ui element node, and start parsing + UiElementPullParser parser = new UiElementPullParser( + ui, // model + false, // explodedView + null, // explodeNodes + Density.MEDIUM, // density (default from ConfigurationComposite) + null // iProject + ); + + assertEquals(XmlPullParser.START_DOCUMENT, parser.getEventType()); + + // top level Linear layout + assertEquals(XmlPullParser.START_TAG, parser.next()); + assertEquals("LinearLayout", parser.getName()); + assertEquals(1, parser.getAttributeCount()); + assertEquals("orientation", parser.getAttributeName(0)); + assertEquals(SdkConstants.NS_RESOURCES, parser.getAttributeNamespace(0)); + assertEquals("android", parser.getAttributePrefix(0)); + assertEquals("vertical", parser.getAttributeValue(0)); + + // Button + assertEquals(XmlPullParser.START_TAG, parser.next()); + assertEquals("Button", parser.getName()); + assertEquals(2, parser.getAttributeCount()); + check(parser, 0, button1Map); + check(parser, 1, button1Map); + // end of button + assertEquals(XmlPullParser.END_TAG, parser.next()); + + // Relative Layout + assertEquals(XmlPullParser.START_TAG, parser.next()); + assertEquals("RelativeLayout", parser.getName()); + assertEquals(1, parser.getAttributeCount()); + assertEquals("orientation", parser.getAttributeName(0)); + assertEquals(SdkConstants.NS_RESOURCES, parser.getAttributeNamespace(0)); + assertEquals("android", parser.getAttributePrefix(0)); + assertEquals("toto", parser.getAttributeValue(0)); + + // Button + assertEquals(XmlPullParser.START_TAG, parser.next()); + assertEquals("Button", parser.getName()); + assertEquals(2, parser.getAttributeCount()); + check(parser, 0, button2Map); + check(parser, 1, button2Map); + // end of button + assertEquals(XmlPullParser.END_TAG, parser.next()); + + // TextView + assertEquals(XmlPullParser.START_TAG, parser.next()); + assertEquals("TextView", parser.getName()); + assertEquals(2, parser.getAttributeCount()); + check(parser, 0, textMap); + check(parser, 1, textMap); + // end of TextView + assertEquals(XmlPullParser.END_TAG, parser.next()); + + // end of RelativeLayout + assertEquals(XmlPullParser.END_TAG, parser.next()); + + + // end of top level linear layout + assertEquals(XmlPullParser.END_TAG, parser.next()); + + assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); + } catch (XmlPullParserException e) { + e.printStackTrace(); + assertTrue(false); + } + } + + /** + * Receives a {@link XmlPullParser} at the START_TAG level, and checks the i-th attribute + * to be present in the {@link HashMap} with the proper (name, value) + * @param parser + * @param i + * @param map + */ + private void check(UiElementPullParser parser, int i, HashMap<String, String> map) { + String name = parser.getAttributeName(i); + String value = parser.getAttributeValue(i); + + String referenceValue = map.get(name); + assertNotNull(referenceValue); + assertEquals(referenceValue, value); + + assertEquals(SdkConstants.NS_RESOURCES, parser.getAttributeNamespace(i)); + assertEquals("android", parser.getAttributePrefix(i)); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationTest.java new file mode 100644 index 000000000..5b07d7b88 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationTest.java @@ -0,0 +1,143 @@ +/* + * 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.eclipse.adt.internal.editors.layout.configuration; + +import static com.android.ide.common.resources.configuration.LocaleQualifier.FAKE_VALUE; + +import com.android.ide.common.resources.configuration.FolderConfiguration; +import com.android.ide.common.resources.configuration.LocaleQualifier; +import com.android.resources.Density; +import com.android.sdklib.devices.Device; +import com.android.sdklib.devices.DeviceManager; +import com.android.sdklib.devices.Screen; +import com.android.utils.StdLogger; +import com.google.common.collect.Lists; + +import java.lang.reflect.Constructor; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class ConfigurationTest extends TestCase { + private Configuration createConfiguration() throws Exception { + // Using reflection instead since we want to pass null to + // a constructor marked with @NonNull, so the test won't compile. + Constructor<Configuration> constructor = + Configuration.class.getDeclaredConstructor(ConfigurationChooser.class); + constructor.setAccessible(true); + ConfigurationChooser chooser = null; + return constructor.newInstance(chooser); + } + + public void test() throws Exception { + Configuration configuration = createConfiguration(); + assertNotNull(configuration); + configuration.setTheme("@style/Theme"); + assertEquals("@style/Theme", configuration.getTheme()); + + DeviceManager deviceManager = DeviceManager.createInstance( + null /*osSdkPath*/, + new StdLogger(StdLogger.Level.VERBOSE)); + Collection<Device> devices = deviceManager.getDevices(DeviceManager.DeviceFilter.DEFAULT); + assertNotNull(devices); + assertTrue(devices.size() > 0); + configuration.setDevice(devices.iterator().next(), false); + + // Check syncing + FolderConfiguration folderConfig = configuration.getFullConfig(); + assertEquals(FAKE_VALUE, folderConfig.getLocaleQualifier().getLanguage()); + assertEquals(FAKE_VALUE, folderConfig.getLocaleQualifier().getRegion()); + assertEquals(Locale.ANY, configuration.getLocale()); + + Locale language = Locale.create(new LocaleQualifier("nb")); + configuration.setLocale(language, true /* skipSync */); + assertEquals(FAKE_VALUE, folderConfig.getLocaleQualifier().getLanguage()); + assertEquals(FAKE_VALUE, folderConfig.getLocaleQualifier().getRegion()); + + configuration.setLocale(language, false /* skipSync */); + assertEquals(FAKE_VALUE, folderConfig.getLocaleQualifier().getRegion()); + assertEquals("nb", folderConfig.getLocaleQualifier().getLanguage()); + + assertEquals("2.7in QVGA::nb-__:+Theme::notnight::", configuration.toPersistentString()); + + configuration.setActivity("foo.bar.FooActivity"); + configuration.setTheme("@android:style/Theme.Holo.Light"); + + assertEquals("2.7in QVGA", + ConfigurationChooser.getDeviceLabel(configuration.getDevice(), true)); + assertEquals("2.7in QVGA", + ConfigurationChooser.getDeviceLabel(configuration.getDevice(), false)); + assertEquals("Light", + ConfigurationChooser.getThemeLabel(configuration.getTheme(), true)); + assertEquals("Theme.Holo.Light", + ConfigurationChooser.getThemeLabel(configuration.getTheme(), false)); + assertEquals("nb", + ConfigurationChooser.getLocaleLabel(null, configuration.getLocale(), true)); + assertEquals("Norwegian Bokm\u00e5l (nb)", + ConfigurationChooser.getLocaleLabel(null, configuration.getLocale(), false)); + + assertEquals("FooActivity", + ConfigurationChooser.getActivityLabel(configuration.getActivity(), true)); + assertEquals("foo.bar.FooActivity", + ConfigurationChooser.getActivityLabel(configuration.getActivity(), false)); + + assertEquals("2.7in QVGA::nb-__:-Theme.Holo.Light::notnight::foo.bar.FooActivity", + configuration.toPersistentString()); + + assertEquals(Density.MEDIUM, configuration.getDensity()); + Screen screen = configuration.getDevice().getDefaultHardware().getScreen(); + assertEquals(145.0f, screen.getXdpi(), 0.001); + assertEquals(145.0f, screen.getYdpi(), 0.001); + } + + public void testCopy() throws Exception { + Configuration configuration = createConfiguration(); + assertNotNull(configuration); + configuration.setTheme("@style/Theme"); + assertEquals("@style/Theme", configuration.getTheme()); + DeviceManager deviceManager = DeviceManager.createInstance( + null /*osSdkPath*/, + new StdLogger(StdLogger.Level.VERBOSE)); + List<Device> devices = Lists.newArrayList(deviceManager.getDevices(DeviceManager.DeviceFilter.DEFAULT)); + assertNotNull(devices); + assertTrue(devices.size() > 0); + configuration.setDevice(devices.get(0), false); + configuration.setActivity("foo.bar.FooActivity"); + configuration.setTheme("@android:style/Theme.Holo.Light"); + Locale locale = Locale.create(new LocaleQualifier("nb")); + configuration.setLocale(locale, false /* skipSync */); + + Configuration copy = Configuration.copy(configuration); + assertEquals(locale, copy.getLocale()); + assertEquals("foo.bar.FooActivity", copy.getActivity()); + assertEquals("@android:style/Theme.Holo.Light", copy.getTheme()); + assertEquals(devices.get(0), copy.getDevice()); + + // Make sure edits to master does not affect the child + configuration.setLocale(Locale.ANY, false); + configuration.setTheme("@android:style/Theme.Holo"); + configuration.setDevice(devices.get(1), true); + + assertTrue(copy.getFullConfig().getLocaleQualifier().equals(locale.qualifier)); + assertEquals(locale, copy.getLocale()); + assertEquals("foo.bar.FooActivity", copy.getActivity()); + assertEquals("@android:style/Theme.Holo.Light", copy.getTheme()); + assertEquals(devices.get(0), copy.getDevice()); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/FlagManagerTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/FlagManagerTest.java new file mode 100644 index 000000000..4286aaa30 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/FlagManagerTest.java @@ -0,0 +1,185 @@ +/* + * 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.eclipse.adt.internal.editors.layout.configuration; + +import com.android.SdkConstants; +import com.android.ide.common.resources.LocaleManager; +import com.google.common.base.Function; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + +import org.eclipse.swt.graphics.Image; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class FlagManagerTest extends TestCase { + public void testGetFlagImage() { + FlagManager manager = FlagManager.get(); + Image us = manager.getFlag("US"); + Image gb = manager.getFlag("GB"); + Image ca = manager.getFlag("CA"); + Image es = manager.getFlag("ES"); + Image br = manager.getFlag("BR"); + Image pt = manager.getFlag("PT"); + assertSame(us, manager.getFlag("en", "US")); + assertSame(gb, manager.getFlag("en", "GB")); + assertSame(ca, manager.getFlag("en", "CA")); + Locale.setDefault(Locale.US); + assertSame(us, manager.getFlag("en", null)); + Locale.setDefault(Locale.UK); + assertSame(gb, manager.getFlag("en", null)); + Locale.setDefault(Locale.CANADA); + assertSame(ca, manager.getFlag("en", null)); + assertSame(manager.getFlag("NO"), manager.getFlag("nb", null)); + assertSame(manager.getFlag("FR"), manager.getFlag("fr", null)); + + Locale.setDefault(new Locale("pt", "br")); + assertSame(br, manager.getFlag("pt", null)); + assertSame(pt, manager.getFlag("pt", "PT")); + Locale.setDefault(new Locale("pt", "pt")); + assertSame(pt, manager.getFlag("pt", null)); + assertSame(br, manager.getFlag("pt", "BR")); + + // Special cases where we have custom flags + assertNotSame(gb, manager.getFlag("cy", null)); // Wales + assertNotSame(es, manager.getFlag("ca", null)); // Catalonia + + // Aliases - http://developer.android.com/reference/java/util/Locale.html + assertSame(manager.getFlag("yi", null), manager.getFlag("ji", null)); + assertSame(manager.getFlag("in", null), manager.getFlag("id", null)); + assertSame(manager.getFlag("iw", null), manager.getFlag("he", null)); + assertSame(us, manager.getFlagForFolderName("values-en-rUS")); + assertSame(gb, manager.getFlagForFolderName("values-en-rGB")); + Locale.setDefault(Locale.CANADA); + assertSame(ca, manager.getFlagForFolderName("values-en")); + } + + public void testAvailableImages() { + // Images we have from WindowBuilder (which are really the famfamfam + // icons; + // see http://www.famfamfam.com/lab/icons/flags) + String[] icons = new String[] { + "ad", "ae", "af", "ag", "ai", "al", "am", "ao", "ar", "as", "at", "au", "aw", "ax", + "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj", "bm", "bn", "bo", "br", + "bs", "bt", "bv", "bw", "by", "bz", "ca", "catalonia", "cc", "cd", "cf", "cg", + "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de", + "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "england", "er", "es", "et", + "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh", "gi", + "gl", "gm", "gn", "gp", "gq", "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", + "hr", "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir", "is", "it", "jm", + "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", + "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc", "md", "me", + "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", + "mw", "mx", "my", "mz", "na", "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", + "nu", "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm", "pn", "pr", "ps", + "pt", "pw", "py", "qa", "re", "ro", "rs", "ru", "rw", "sa", "sb", "sc", "scotland", + "sd", "se", "sg", "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "ss", "st", + "sv", "sy", "sz", "tc", "td", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to", + "tr", "tt", "tv", "tw", "tz", "ua", "ug", "um", "us", "uy", "uz", "va", "vc", "ve", + "vg", "vi", "vn", "vu", "wales", "wf", "ws", "ye", "yt", "za", "zm", "zw" + }; + + Set<String> sImages = new HashSet<String>(100); + for (String code : icons) { + if (code.length() > 2) { + continue; + } + code = code.toUpperCase(Locale.US); + sImages.add(code); + + if (!LocaleManager.isValidRegionCode(code)) { + System.out.println("No region name found for region code " + code); + } + } + + Set<String> unused = Sets.newHashSet(LocaleManager.getRegionCodes(false)); + Set<String> reachable = Sets.newHashSet(); + Multimap<String, String> regionToLanguages = ArrayListMultimap.create(); + for (String language : LocaleManager.getLanguageCodes(false)) { + for (String region : LocaleManager.getRelevantRegions(language)) { + reachable.add(region); + regionToLanguages.put(region, language); + } + } + unused.removeAll(reachable); + + for (String region : reachable) { + if (!sImages.contains(region)) { + StringBuilder sb = new StringBuilder(); + + sb.append("No icon found for region ").append(region).append(" ") + .append(LocaleManager.getRegionName(region)); + sb.append(", used for languages "); + + for (String language : regionToLanguages.get(region)) { + sb.append(language).append("(").append(LocaleManager.getLanguageName(language)) + .append(") "); + } + System.out.println(sb.toString()); + } + } + + // Known regions that we don't have language to region mappings for + unused.remove("AQ"); + unused.remove("VA"); + unused.remove("GS"); + unused.remove("TF"); + unused.remove("BV"); + unused.remove("HM"); + + if (!unused.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("The following icons are not referenced by any of the " + + "language to country bindings:"); + for (String code : unused) { + sb.append(code.toLowerCase(Locale.US)).append(SdkConstants.DOT_PNG).append(" ("); + sb.append(LocaleManager.getRegionName(code)).append(") "); + } + System.out.println(sb.toString()); + } + } + + public void testMissingFlag() { + Image icon = FlagManager.get().getFlag("AQ"); + assertNotNull(icon); + assertSame(FlagManager.get().getEmptyIcon(), icon); + + icon = FlagManager.get().getFlag("AQ"); + assertNotNull(icon); + assertSame(FlagManager.get().getEmptyIcon(), icon); + + icon = FlagManager.get().getFlag("WO"); // Not used in ISO 3166-1 + assertNotNull(icon); + assertSame(FlagManager.get().getEmptyIcon(), icon); + } + + public void testKnownFlag() { + Image icon = FlagManager.get().getFlag("US"); + assertNotNull(icon); + assertNotSame(FlagManager.get().getEmptyIcon(), icon); + } +} + diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LocaleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LocaleTest.java new file mode 100644 index 000000000..904ade39c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LocaleTest.java @@ -0,0 +1,99 @@ +/* + * 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.eclipse.adt.internal.editors.layout.configuration; + +import com.android.ide.common.resources.configuration.FolderConfiguration; +import com.android.ide.common.resources.configuration.LocaleQualifier; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class LocaleTest extends TestCase { + public void test1() { + Locale locale = Locale.create("en-rUS"); + assertEquals("en", locale.qualifier.getLanguage()); + assertEquals("US", locale.qualifier.getRegion()); + assertTrue(locale.hasLanguage()); + assertTrue(locale.hasRegion()); + } + + public void test2() { + Locale locale = Locale.create("zh"); + assertEquals("zh", locale.qualifier.getLanguage()); + assertNull(locale.qualifier.getRegion()); + assertTrue(locale.hasLanguage()); + assertFalse(locale.hasRegion()); + } + + public void testEquals() { + Locale locale = Locale.create("zh"); + assertEquals("zh", locale.qualifier.getLanguage()); + assertNull(locale.qualifier.getRegion()); + assertTrue(locale.hasLanguage()); + assertFalse(locale.hasRegion()); + } + + public void test() { + LocaleQualifier qualifier1 = LocaleQualifier.getQualifier("nb"); + LocaleQualifier qualifier2 = LocaleQualifier.getQualifier("no"); + LocaleQualifier qualifier3 = LocaleQualifier.getQualifier("nb-rNO"); + LocaleQualifier qualifier4 = LocaleQualifier.getQualifier("nb-rSE"); + LocaleQualifier qualifier5 = LocaleQualifier.getQualifier("no-rSE"); + assertNotNull(qualifier1); + assertNotNull(qualifier2); + assertNotNull(qualifier3); + assertNotNull(qualifier4); + assertNotNull(qualifier5); + + assertEquals(Locale.ANY, Locale.ANY); + assertFalse(Locale.ANY.hasLanguage()); + assertFalse(Locale.ANY.hasRegion()); + // noinspection ConstantConditions + assertFalse(Locale.create(new LocaleQualifier(LocaleQualifier.FAKE_VALUE)).hasLanguage()); + // noinspection ConstantConditions + assertFalse(Locale.create(new LocaleQualifier(LocaleQualifier.FAKE_VALUE)).hasRegion()); + + assertEquals(Locale.create(qualifier1), Locale.create(qualifier1)); + assertTrue(Locale.create(qualifier1).hasLanguage()); + assertFalse(Locale.create(qualifier1).hasRegion()); + assertTrue(Locale.create(qualifier3).hasLanguage()); + assertTrue(Locale.create(qualifier3).hasRegion()); + + assertEquals(Locale.create(qualifier3), Locale.create(qualifier3)); + assertEquals(Locale.create(qualifier1), Locale.create(qualifier1)); + assertTrue(Locale.create(qualifier1).equals(Locale.create(qualifier1))); + assertTrue(Locale.create(qualifier3).equals(Locale.create(qualifier3))); + assertFalse(Locale.create(qualifier3).equals(Locale.create(qualifier4))); + assertFalse(Locale.create(qualifier1).equals(Locale.create(qualifier3))); + assertFalse(Locale.create(qualifier1).equals(Locale.create(qualifier2))); + assertFalse(Locale.create(qualifier3).equals(Locale.create(qualifier5))); + assertEquals("nb", Locale.create(qualifier1).toString()); + assertEquals("nb-NO", Locale.create(qualifier3).toString()); + + assertEquals(Locale.create(qualifier1), Locale.create("b+nb")); + assertEquals(Locale.create(qualifier3), Locale.create("b+nb+NO")); + } + + public void testFolderConfig() { + FolderConfiguration config = new FolderConfiguration(); + assertEquals(Locale.ANY, Locale.create(config)); + config.setLocaleQualifier(LocaleQualifier.getQualifier("en")); + assertEquals(Locale.create("en"), Locale.create(config)); + config.setLocaleQualifier(LocaleQualifier.getQualifier("en-rUS")); + assertEquals(Locale.create("en-rUS"), Locale.create(config)); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java new file mode 100644 index 000000000..5996f01c0 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java @@ -0,0 +1,797 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import com.android.ide.common.rendering.api.Capability; +import com.android.ide.common.rendering.api.DataBindingItem; +import com.android.ide.common.rendering.api.MergeCookie; +import com.android.ide.common.rendering.api.ViewInfo; +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; +import com.android.utils.Pair; + +import org.eclipse.swt.graphics.Rectangle; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class CanvasViewInfoTest extends TestCase { + + public static ViewElementDescriptor createDesc(String name, String fqn, boolean hasChildren) { + if (hasChildren) { + return new ViewElementDescriptor(name, name, fqn, "", "", new AttributeDescriptor[0], + new AttributeDescriptor[0], new ElementDescriptor[1], false); + } else { + return new ViewElementDescriptor(name, fqn); + } + } + + public static UiViewElementNode createNode(UiViewElementNode parent, String fqn, + boolean hasChildren) { + String name = fqn.substring(fqn.lastIndexOf('.') + 1); + ViewElementDescriptor descriptor = createDesc(name, fqn, hasChildren); + if (parent == null) { + // All node hierarchies should be wrapped inside a document node at the root + parent = new UiViewElementNode(createDesc("doc", "doc", true)); + } + return (UiViewElementNode) parent.appendNewUiChild(descriptor); + } + + public static UiViewElementNode createNode(String fqn, boolean hasChildren) { + return createNode(null, fqn, hasChildren); + } + + public void testNormalCreate() throws Exception { + normal(true); + } + + public void testNormalCreateLayoutLib5() throws Exception { + normal(false); + } + + private void normal(boolean layoutlib5) { + + // Normal view hierarchy, no null keys anywhere + + UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); + ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); + UiViewElementNode child1Node = createNode(rootNode, "android.widget.Button", false); + ViewInfo child1 = new ViewInfo("Button", child1Node, 0, 0, 50, 20); + UiViewElementNode child2Node = createNode(rootNode, "android.widget.Button", false); + ViewInfo child2 = new ViewInfo("Button", child2Node, 0, 20, 70, 25); + root.setChildren(Arrays.asList(child1, child2)); + + CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); + assertNotNull(rootView); + assertEquals("LinearLayout", rootView.getName()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); + assertNull(rootView.getParent()); + assertSame(rootView.getUiViewNode(), rootNode); + assertEquals(2, rootView.getChildren().size()); + CanvasViewInfo childView1 = rootView.getChildren().get(0); + CanvasViewInfo childView2 = rootView.getChildren().get(1); + + assertEquals("Button", childView1.getName()); + assertSame(rootView, childView1.getParent()); + assertEquals(new Rectangle(10, 10, 49, 19), childView1.getAbsRect()); + assertEquals(new Rectangle(10, 10, 49, 19), childView1.getSelectionRect()); + assertSame(childView1.getUiViewNode(), child1Node); + + assertEquals("Button", childView2.getName()); + assertSame(rootView, childView2.getParent()); + assertEquals(new Rectangle(10, 30, 69, 4), childView2.getAbsRect()); + assertEquals(new Rectangle(10, 30, 69, 5), childView2.getSelectionRect()); + assertSame(childView2.getUiViewNode(), child2Node); + } + + public void testShowIn() throws Exception { + showIn(false); + } + + public void testShowInLayoutLib5() throws Exception { + showIn(true); + } + + public void showIn(boolean layoutlib5) throws Exception { + + // Test rendering of "Show Included In" (included content rendered + // within an outer content that has null keys) + + ViewInfo root = new ViewInfo("LinearLayout", null, 10, 10, 100, 100); + ViewInfo child1 = new ViewInfo("CheckBox", null, 0, 0, 50, 20); + UiViewElementNode child2Node = createNode("android.widget.RelativeLayout", true); + ViewInfo child2 = new ViewInfo("RelativeLayout", child2Node, 0, 20, 70, 25); + root.setChildren(Arrays.asList(child1, child2)); + UiViewElementNode child21Node = createNode("android.widget.Button", false); + ViewInfo child21 = new ViewInfo("RadioButton", child21Node, 0, 20, 70, 25); + child2.setChildren(Arrays.asList(child21)); + + CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); + assertNotNull(rootView); + assertEquals("LinearLayout", rootView.getName()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); + assertNull(rootView.getParent()); + assertNull(rootView.getUiViewNode()); + assertEquals(1, rootView.getChildren().size()); + CanvasViewInfo includedView = rootView.getChildren().get(0); + + assertEquals("RelativeLayout", includedView.getName()); + assertSame(rootView, includedView.getParent()); + assertEquals(new Rectangle(10, 30, 69, 4), includedView.getAbsRect()); + assertEquals(new Rectangle(10, 30, 69, 5), includedView.getSelectionRect()); + assertSame(includedView.getUiViewNode(), child2Node); + + CanvasViewInfo grandChild = includedView.getChildren().get(0); + assertNotNull(grandChild); + assertEquals("RadioButton", grandChild.getName()); + assertSame(child21Node, grandChild.getUiViewNode()); + assertEquals(new Rectangle(10, 50, 69, 4), grandChild.getAbsRect()); + assertEquals(new Rectangle(10, 50, 69, 5), grandChild.getSelectionRect()); + } + + public void testIncludeTag() throws Exception { + boolean layoutlib5 = true; + + // Test rendering of included views on layoutlib 5+ (e.g. has <include> tag) + + UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); + ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); + UiViewElementNode child1Node = createNode(rootNode, "android.widget.Button", false); + ViewInfo child1 = new ViewInfo("CheckBox", child1Node, 0, 0, 50, 20); + UiViewElementNode child2Node = createNode(rootNode, "include", true); + ViewInfo child2 = new ViewInfo("RelativeLayout", child2Node, 0, 20, 70, 25); + root.setChildren(Arrays.asList(child1, child2)); + ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25); + child2.setChildren(Arrays.asList(child21)); + + CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); + assertNotNull(rootView); + assertEquals("LinearLayout", rootView.getName()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); + assertNull(rootView.getParent()); + assertSame(rootNode, rootView.getUiViewNode()); + assertEquals(2, rootView.getChildren().size()); + + CanvasViewInfo childView1 = rootView.getChildren().get(0); + CanvasViewInfo includedView = rootView.getChildren().get(1); + + assertEquals("CheckBox", childView1.getName()); + assertSame(rootView, childView1.getParent()); + assertEquals(new Rectangle(10, 10, 49, 19), childView1.getAbsRect()); + assertEquals(new Rectangle(10, 10, 49, 19), childView1.getSelectionRect()); + assertSame(childView1.getUiViewNode(), child1Node); + + assertEquals("RelativeLayout", includedView.getName()); + assertSame(rootView, includedView.getParent()); + assertEquals(new Rectangle(10, 30, 69, 4), includedView.getAbsRect()); + assertEquals(new Rectangle(10, 30, 69, 5), includedView.getSelectionRect()); + assertSame(includedView.getUiViewNode(), child2Node); + assertEquals(0, includedView.getChildren().size()); + } + + public void testNoIncludeTag() throws Exception { + boolean layoutlib5 = false; + + // Test rendering of included views on layoutlib 4- (e.g. no <include> tag cookie + // in view info) + + UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); + ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); + UiViewElementNode child1Node = createNode(rootNode, "android.widget.Button", false); + ViewInfo child1 = new ViewInfo("CheckBox", child1Node, 0, 0, 50, 20); + UiViewElementNode child2Node = createNode(rootNode, "include", true); + ViewInfo child2 = new ViewInfo("RelativeLayout", null /* layoutlib 4 */, 0, 20, 70, 25); + root.setChildren(Arrays.asList(child1, child2)); + ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25); + child2.setChildren(Arrays.asList(child21)); + + CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); + assertNotNull(rootView); + assertEquals("LinearLayout", rootView.getName()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); + assertNull(rootView.getParent()); + assertSame(rootNode, rootView.getUiViewNode()); + assertEquals(2, rootView.getChildren().size()); + + CanvasViewInfo childView1 = rootView.getChildren().get(0); + CanvasViewInfo includedView = rootView.getChildren().get(1); + + assertEquals("CheckBox", childView1.getName()); + assertSame(rootView, childView1.getParent()); + assertEquals(new Rectangle(10, 10, 49, 19), childView1.getAbsRect()); + assertEquals(new Rectangle(10, 10, 49, 19), childView1.getSelectionRect()); + assertSame(childView1.getUiViewNode(), child1Node); + + assertEquals("RelativeLayout", includedView.getName()); + assertSame(rootView, includedView.getParent()); + assertEquals(new Rectangle(10, 30, 69, 4), includedView.getAbsRect()); + assertEquals(new Rectangle(10, 30, 69, 5), includedView.getSelectionRect()); + assertSame(includedView.getUiViewNode(), child2Node); + assertEquals(0, includedView.getChildren().size()); + } + + public void testMergeMatching() throws Exception { + boolean layoutlib5 = false; + + // Test rendering of MULTIPLE included views or when there is no simple match + // between view info and ui element node children + + UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); + ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); + UiViewElementNode child1Node = createNode(rootNode, "android.widget.Button", false); + ViewInfo child1 = new ViewInfo("CheckBox", child1Node, 0, 0, 50, 20); + UiViewElementNode multiChildNode1 = createNode(rootNode, "foo", true); + UiViewElementNode multiChildNode2 = createNode(rootNode, "bar", true); + ViewInfo child2 = new ViewInfo("RelativeLayout", null, 0, 20, 70, 25); + ViewInfo child3 = new ViewInfo("AbsoluteLayout", null, 10, 40, 50, 15); + root.setChildren(Arrays.asList(child1, child2, child3)); + ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25); + child2.setChildren(Arrays.asList(child21)); + + CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); + assertNotNull(rootView); + assertEquals("LinearLayout", rootView.getName()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); + assertNull(rootView.getParent()); + assertTrue(rootView.isRoot()); + assertSame(rootNode, rootView.getUiViewNode()); + assertEquals(3, rootView.getChildren().size()); + + CanvasViewInfo childView1 = rootView.getChildren().get(0); + assertFalse(childView1.isRoot()); + CanvasViewInfo includedView1 = rootView.getChildren().get(1); + assertFalse(includedView1.isRoot()); + CanvasViewInfo includedView2 = rootView.getChildren().get(2); + assertFalse(includedView1.isRoot()); + + assertEquals("CheckBox", childView1.getName()); + assertSame(rootView, childView1.getParent()); + assertEquals(new Rectangle(10, 10, 49, 19), childView1.getAbsRect()); + assertEquals(new Rectangle(10, 10, 49, 19), childView1.getSelectionRect()); + assertSame(childView1.getUiViewNode(), child1Node); + + assertEquals("RelativeLayout", includedView1.getName()); + assertSame(multiChildNode1, includedView1.getUiViewNode()); + assertEquals("foo", includedView1.getUiViewNode().getDescriptor().getXmlName()); + assertSame(multiChildNode2, includedView2.getUiViewNode()); + assertEquals("AbsoluteLayout", includedView2.getName()); + assertEquals("bar", includedView2.getUiViewNode().getDescriptor().getXmlName()); + assertSame(rootView, includedView1.getParent()); + assertSame(rootView, includedView2.getParent()); + assertEquals(new Rectangle(10, 30, 69, 4), includedView1.getAbsRect()); + assertEquals(new Rectangle(10, 30, 69, 5), includedView1.getSelectionRect()); + assertEquals(new Rectangle(20, 50, 39, -26), includedView2.getAbsRect()); + assertEquals(new Rectangle(20, 35, 39, 5), includedView2.getSelectionRect()); + assertEquals(0, includedView1.getChildren().size()); + assertEquals(0, includedView2.getChildren().size()); + } + + public void testMerge() throws Exception { + boolean layoutlib5 = false; + + // Test rendering of MULTIPLE included views or when there is no simple match + // between view info and ui element node children + + UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); + ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); + UiViewElementNode child1Node = createNode(rootNode, "android.widget.Button", false); + ViewInfo child1 = new ViewInfo("CheckBox", child1Node, 0, 0, 50, 20); + UiViewElementNode multiChildNode = createNode(rootNode, "foo", true); + ViewInfo child2 = new ViewInfo("RelativeLayout", null, 0, 20, 70, 25); + ViewInfo child3 = new ViewInfo("AbsoluteLayout", null, 10, 40, 50, 15); + root.setChildren(Arrays.asList(child1, child2, child3)); + ViewInfo child21 = new ViewInfo("RadioButton", null, 0, 20, 70, 25); + child2.setChildren(Arrays.asList(child21)); + + CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); + assertNotNull(rootView); + assertEquals("LinearLayout", rootView.getName()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); + assertNull(rootView.getParent()); + assertSame(rootNode, rootView.getUiViewNode()); + assertEquals(2, rootView.getChildren().size()); + + CanvasViewInfo childView1 = rootView.getChildren().get(0); + CanvasViewInfo includedView = rootView.getChildren().get(1); + + assertEquals("CheckBox", childView1.getName()); + assertSame(rootView, childView1.getParent()); + assertEquals(new Rectangle(10, 10, 49, 19), childView1.getAbsRect()); + assertEquals(new Rectangle(10, 10, 49, 19), childView1.getSelectionRect()); + assertSame(childView1.getUiViewNode(), child1Node); + + assertEquals("RelativeLayout", includedView.getName()); + assertSame(rootView, includedView.getParent()); + assertEquals(new Rectangle(10, 30, 69, 4), includedView.getAbsRect()); + assertEquals(new Rectangle(10, 30, 69, 5), includedView.getSelectionRect()); + assertEquals(0, includedView.getChildren().size()); + assertSame(multiChildNode, includedView.getUiViewNode()); + } + + public void testInsertMerge() throws Exception { + boolean layoutlib5 = false; + + // Test rendering of MULTIPLE included views or when there is no simple match + // between view info and ui element node children + + UiViewElementNode mergeNode = createNode("merge", true); + UiViewElementNode rootNode = createNode(mergeNode, "android.widget.Button", false); + ViewInfo root = new ViewInfo("Button", rootNode, 10, 10, 100, 100); + + CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); + assertNotNull(rootView); + assertEquals("merge", rootView.getName()); + assertSame(rootView.getUiViewNode(), mergeNode); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getAbsRect()); + assertEquals(new Rectangle(10, 10, 89, 89), rootView.getSelectionRect()); + assertNull(rootView.getParent()); + assertSame(mergeNode, rootView.getUiViewNode()); + assertEquals(1, rootView.getChildren().size()); + + CanvasViewInfo childView1 = rootView.getChildren().get(0); + + assertEquals("Button", childView1.getName()); + assertSame(rootView, childView1.getParent()); + assertEquals(new Rectangle(10, 10, 89, 89), childView1.getAbsRect()); + assertEquals(new Rectangle(10, 10, 89, 89), childView1.getSelectionRect()); + assertSame(childView1.getUiViewNode(), rootNode); + } + + public void testUnmatchedMissing() throws Exception { + boolean layoutlib5 = false; + + UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); + ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100); + List<ViewInfo> children = new ArrayList<ViewInfo>(); + // Should be matched up with corresponding node: + Set<Integer> missingKeys = new HashSet<Integer>(); + // Should not be matched with any views, but should get view created: + Set<Integer> extraKeys = new HashSet<Integer>(); + // Should not be matched with any nodes + Set<Integer> extraViews = new HashSet<Integer>(); + int numViews = 30; + missingKeys.add(0); + missingKeys.add(4); + missingKeys.add(14); + missingKeys.add(29); + extraKeys.add(9); + extraKeys.add(20); + extraKeys.add(22); + extraViews.add(18); + extraViews.add(24); + + List<String> expectedViewNames = new ArrayList<String>(); + List<String> expectedNodeNames = new ArrayList<String>(); + + for (int i = 0; i < numViews; i++) { + UiViewElementNode childNode = null; + if (!extraViews.contains(i)) { + childNode = createNode(rootNode, "childNode" + i, false); + } + Object cookie = missingKeys.contains(i) || extraViews.contains(i) ? null : childNode; + ViewInfo childView = new ViewInfo("childView" + i, cookie, + 0, i * 20, 50, (i + 1) * 20); + children.add(childView); + + if (!extraViews.contains(i)) { + expectedViewNames.add("childView" + i); + expectedNodeNames.add("childNode" + i); + } + + if (extraKeys.contains(i)) { + createNode(rootNode, "extraNodeAt" + i, false); + + expectedViewNames.add("extraNodeAt" + i); + expectedNodeNames.add("extraNodeAt" + i); + } + } + root.setChildren(children); + + CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); + assertNotNull(rootView); + + // dump(root, 0); + // dump(rootView, 0); + + assertEquals("LinearLayout", rootView.getName()); + assertNull(rootView.getParent()); + assertSame(rootNode, rootView.getUiViewNode()); + assertEquals(numViews + extraKeys.size() - extraViews.size(), rootNode.getUiChildren() + .size()); + assertEquals(numViews + extraKeys.size() - extraViews.size(), + rootView.getChildren().size()); + assertEquals(expectedViewNames.size(), rootView.getChildren().size()); + for (int i = 0, n = rootView.getChildren().size(); i < n; i++) { + CanvasViewInfo childView = rootView.getChildren().get(i); + String expectedViewName = expectedViewNames.get(i); + String expectedNodeName = expectedNodeNames.get(i); + assertEquals(expectedViewName, childView.getName()); + assertNotNull(childView.getUiViewNode()); + assertEquals(expectedNodeName, childView.getUiViewNode().getDescriptor().getXmlName()); + } + } + + public void testMergeCookies() throws Exception { + boolean layoutlib5 = true; + + UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); + ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100); + + // Create the merge cookies in the opposite order to ensure that we don't + // apply our own logic when matching up views with nodes + LinkedList<MergeCookie> cookies = new LinkedList<MergeCookie>(); + for (int i = 0; i < 10; i++) { + UiViewElementNode node = createNode(rootNode, "childNode" + i, false); + cookies.addFirst(new MergeCookie(node)); + } + Iterator<MergeCookie> it = cookies.iterator(); + ArrayList<ViewInfo> children = new ArrayList<ViewInfo>(); + for (int i = 0; i < 10; i++) { + ViewInfo childView = new ViewInfo("childView" + i, it.next(), 0, i * 20, 50, + (i + 1) * 20); + children.add(childView); + } + root.setChildren(children); + + CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); + assertNotNull(rootView); + + assertEquals("LinearLayout", rootView.getName()); + assertNull(rootView.getParent()); + assertSame(rootNode, rootView.getUiViewNode()); + for (int i = 0, n = rootView.getChildren().size(); i < n; i++) { + CanvasViewInfo childView = rootView.getChildren().get(i); + assertEquals("childView" + i, childView.getName()); + assertEquals("childNode" + (9 - i), childView.getUiViewNode().getDescriptor() + .getXmlName()); + } + } + + public void testMergeCookies2() throws Exception { + boolean layoutlib5 = true; + + UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); + ViewInfo root = new ViewInfo("LinearLayout", rootNode, 0, 0, 100, 100); + + UiViewElementNode node1 = createNode(rootNode, "childNode1", false); + UiViewElementNode node2 = createNode(rootNode, "childNode2", false); + MergeCookie cookie1 = new MergeCookie(node1); + MergeCookie cookie2 = new MergeCookie(node2); + + // Sets alternating merge cookies and checks whether the node sibling lists are + // okay and merged correctly + + ArrayList<ViewInfo> children = new ArrayList<ViewInfo>(); + for (int i = 0; i < 10; i++) { + Object cookie = (i % 2) == 0 ? cookie1 : cookie2; + ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50, + (i + 1) * 20); + children.add(childView); + } + root.setChildren(children); + + Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5); + CanvasViewInfo rootView = result.getFirst(); + List<Rectangle> bounds = result.getSecond(); + assertNull(bounds); + assertNotNull(rootView); + + assertEquals("LinearLayout", rootView.getName()); + assertNull(rootView.getParent()); + assertSame(rootNode, rootView.getUiViewNode()); + assertEquals(10, rootView.getChildren().size()); + assertEquals(2, rootView.getUniqueChildren().size()); + for (int i = 0, n = rootView.getChildren().size(); i < n; i++) { + CanvasViewInfo childView = rootView.getChildren().get(i); + assertEquals("childView" + i, childView.getName()); + Object cookie = (i % 2) == 0 ? node1 : node2; + assertSame(cookie, childView.getUiViewNode()); + List<CanvasViewInfo> nodeSiblings = childView.getNodeSiblings(); + assertEquals(5, nodeSiblings.size()); + } + List<CanvasViewInfo> nodeSiblings = rootView.getChildren().get(0).getNodeSiblings(); + for (int j = 0; j < 5; j++) { + assertEquals("childView" + (j * 2), nodeSiblings.get(j).getName()); + } + nodeSiblings = rootView.getChildren().get(1).getNodeSiblings(); + for (int j = 0; j < 5; j++) { + assertEquals("childView" + (j * 2 + 1), nodeSiblings.get(j).getName()); + } + } + + public void testIncludeBounds() throws Exception { + boolean layoutlib5 = true; + + UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); + ViewInfo root = new ViewInfo("included", null, 0, 0, 100, 100); + + UiViewElementNode node1 = createNode(rootNode, "childNode1", false); + UiViewElementNode node2 = createNode(rootNode, "childNode2", false); + MergeCookie cookie1 = new MergeCookie(node1); + MergeCookie cookie2 = new MergeCookie(node2); + + // Sets alternating merge cookies and checks whether the node sibling lists are + // okay and merged correctly + + ArrayList<ViewInfo> children = new ArrayList<ViewInfo>(); + for (int i = 0; i < 10; i++) { + Object cookie = (i % 2) == 0 ? cookie1 : cookie2; + ViewInfo childView = new ViewInfo("childView" + i, cookie, 0, i * 20, 50, + (i + 1) * 20); + children.add(childView); + } + root.setChildren(children); + + Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5); + CanvasViewInfo rootView = result.getFirst(); + List<Rectangle> bounds = result.getSecond(); + assertNotNull(rootView); + + assertEquals("included", rootView.getName()); + assertNull(rootView.getParent()); + assertNull(rootView.getUiViewNode()); + assertEquals(10, rootView.getChildren().size()); + assertEquals(2, rootView.getUniqueChildren().size()); + for (int i = 0, n = rootView.getChildren().size(); i < n; i++) { + CanvasViewInfo childView = rootView.getChildren().get(i); + assertEquals("childView" + i, childView.getName()); + Object cookie = (i % 2) == 0 ? node1 : node2; + assertSame(cookie, childView.getUiViewNode()); + List<CanvasViewInfo> nodeSiblings = childView.getNodeSiblings(); + assertEquals(5, nodeSiblings.size()); + } + List<CanvasViewInfo> nodeSiblings = rootView.getChildren().get(0).getNodeSiblings(); + for (int j = 0; j < 5; j++) { + assertEquals("childView" + (j * 2), nodeSiblings.get(j).getName()); + } + nodeSiblings = rootView.getChildren().get(1).getNodeSiblings(); + for (int j = 0; j < 5; j++) { + assertEquals("childView" + (j * 2 + 1), nodeSiblings.get(j).getName()); + } + + // Only show the primary bounds as included + assertEquals(2, bounds.size()); + assertEquals(new Rectangle(0, 0, 49, 19), bounds.get(0)); + assertEquals(new Rectangle(0, 20, 49, 19), bounds.get(1)); + } + + public void testIncludeBounds2() throws Exception { + includeBounds2(false); + } + + public void testIncludeBounds2LayoutLib5() throws Exception { + includeBounds2(true); + } + + public void includeBounds2(boolean layoutlib5) throws Exception { + + UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); + ViewInfo root = new ViewInfo("included", null, 0, 0, 100, 100); + + UiViewElementNode node1 = createNode(rootNode, "childNode1", false); + UiViewElementNode node2 = createNode(rootNode, "childNode2", false); + + ViewInfo childView1 = new ViewInfo("childView1", node1, 0, 20, 50, 40); + ViewInfo childView2 = new ViewInfo("childView2", node2, 0, 40, 50, 60); + + root.setChildren(Arrays.asList(childView1, childView2)); + + Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, layoutlib5); + CanvasViewInfo rootView = result.getFirst(); + List<Rectangle> bounds = result.getSecond(); + assertNotNull(rootView); + + assertEquals("included", rootView.getName()); + assertNull(rootView.getParent()); + assertNull(rootView.getUiViewNode()); + assertEquals(2, rootView.getChildren().size()); + assertEquals(2, rootView.getUniqueChildren().size()); + + Rectangle bounds1 = bounds.get(0); + Rectangle bounds2 = bounds.get(1); + assertEquals(new Rectangle(0, 20, 49, 19), bounds1); + assertEquals(new Rectangle(0, 40, 49, 19), bounds2); + } + + public void testCookieWorkaround() throws Exception { + UiViewElementNode rootNode = createNode("android.widget.LinearLayout", true); + ViewInfo root = new ViewInfo("included", null, 0, 0, 100, 100); + + UiViewElementNode node2 = createNode(rootNode, "childNode2", false); + MergeCookie mergeCookie = new MergeCookie(root); + + ViewInfo childView1 = new ViewInfo("childView1", mergeCookie, 0, 20, 50, 40); + ViewInfo childView2 = new ViewInfo("childView2", node2, 0, 40, 50, 60); + + root.setChildren(Arrays.asList(childView1, childView2)); + + Pair<CanvasViewInfo, List<Rectangle>> result = CanvasViewInfo.create(root, true); + CanvasViewInfo rootView = result.getFirst(); + List<Rectangle> bounds = result.getSecond(); + assertNotNull(rootView); + + assertEquals("included", rootView.getName()); + assertNull(rootView.getParent()); + assertNull(rootView.getUiViewNode()); + // childView1 should have been removed since it has the wrong merge cookie + assertEquals(1, rootView.getChildren().size()); + assertEquals(1, rootView.getUniqueChildren().size()); + + Rectangle bounds1 = bounds.get(0); + assertEquals(new Rectangle(0, 40, 49, 19), bounds1); + } + + public void testGestureOverlayView() throws Exception { + boolean layoutlib5 = true; + + // Test rendering of included views on layoutlib 5+ (e.g. has <include> tag) + + UiViewElementNode rootNode = createNode("android.gesture.GestureOverlayView", true); + UiViewElementNode childNode = createNode(rootNode, "android.widget.LinearLayout", false); + UiViewElementNode grandChildNode = createNode(childNode, "android.widget.Button", false); + ViewInfo root = new ViewInfo("GestureOverlayView", rootNode, 10, 10, 100, 100); + ViewInfo child = new ViewInfo("LinearLayout", childNode, 0, 0, 50, 20); + root.setChildren(Collections.singletonList(child)); + ViewInfo grandChild = new ViewInfo("Button", grandChildNode, 0, 20, 70, 25); + child.setChildren(Collections.singletonList(grandChild)); + CanvasViewInfo rootView = CanvasViewInfo.create(root, layoutlib5).getFirst(); + assertNotNull(rootView); + assertEquals("GestureOverlayView", rootView.getName()); + + assertTrue(rootView.isRoot()); + assertNull(rootView.getParent()); + assertSame(rootNode, rootView.getUiViewNode()); + assertEquals(1, rootView.getChildren().size()); + + CanvasViewInfo childView = rootView.getChildren().get(0); + assertEquals("LinearLayout", childView.getName()); + + // This should also be a root for the special case that the root is + assertTrue(childView.isRoot()); + + assertEquals(1, childView.getChildren().size()); + CanvasViewInfo grandChildView = childView.getChildren().get(0); + assertEquals("Button", grandChildView.getName()); + assertFalse(grandChildView.isRoot()); + } + + public void testListView() throws Exception { + // For ListViews we get AdapterItemReferences as cookies. Ensure that this + // works properly. + // + // android.widget.FrameLayout [0,50,320,480] <FrameLayout> + // android.widget.ListView [0,0,320,430] <ListView> + // android.widget.LinearLayout [0,0,320,17] SessionParams$AdapterItemReference + // android.widget.TextView [0,0,73,17] + // android.widget.LinearLayout [0,18,320,35] SessionParams$AdapterItemReference + // android.widget.TextView [0,0,73,17] + // android.widget.LinearLayout [0,36,320,53] SessionParams$AdapterItemReference + // android.widget.TextView [0,0,73,17] + // ... + + UiViewElementNode rootNode = createNode("FrameLayout", true); + UiViewElementNode childNode = createNode(rootNode, "ListView", false); + /*UiViewElementNode grandChildNode =*/ createNode(childNode, "LinearLayout", false); + /*UiViewElementNode greatGrandChildNode =*/ createNode(childNode, "TextView", false); + DataBindingItem dataBindingItem = new DataBindingItem("foo"); + + ViewInfo root = new ViewInfo("FrameLayout", rootNode, 0, 50, 320, 480); + ViewInfo child = new ViewInfo("ListView", childNode, 0, 0, 320, 430); + root.setChildren(Collections.singletonList(child)); + ViewInfo grandChild = new ViewInfo("LinearLayout", dataBindingItem, 0, 0, 320, 17); + child.setChildren(Collections.singletonList(grandChild)); + ViewInfo greatGrandChild = new ViewInfo("Button", null, 0, 0, 73, 17); + grandChild.setChildren(Collections.singletonList(greatGrandChild)); + CanvasViewInfo rootView = CanvasViewInfo.create(root, true /*layoutlib5*/).getFirst(); + assertNotNull(rootView); + + assertEquals("FrameLayout", rootView.getName()); + assertEquals(1, rootView.getChildren().size()); + assertSame(rootNode, rootView.getUiViewNode()); + + CanvasViewInfo childView = rootView.getChildren().get(0); + assertEquals("ListView", childView.getName()); + assertEquals(0, childView.getChildren().size()); + assertSame(childNode, childView.getUiViewNode()); + } + + /** + * Dumps out the given {@link ViewInfo} hierarchy to standard out. + * Useful during development. + * + * @param graphicalEditor the editor associated with this hierarchy + * @param root the root of the {@link ViewInfo} hierarchy + */ + public static void dump(GraphicalEditorPart graphicalEditor, ViewInfo root) { + System.out.println("\n\nRendering:"); + boolean supportsEmbedding = graphicalEditor.renderingSupports(Capability.EMBEDDED_LAYOUT); + System.out.println("Supports Embedded Layout=" + supportsEmbedding); + System.out.println("Rendering context=" + graphicalEditor.getIncludedWithin()); + dump(root, 0); + } + + /** Helper for {@link #dump(GraphicalEditorPart, ViewInfo)} */ + public static void dump(ViewInfo info, int depth) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < depth; i++) { + sb.append(" "); + } + sb.append(info.getClassName()); + sb.append(" ["); + sb.append(info.getLeft()); + sb.append(","); + sb.append(info.getTop()); + sb.append(","); + sb.append(info.getRight()); + sb.append(","); + sb.append(info.getBottom()); + sb.append("] "); + Object cookie = info.getCookie(); + if (cookie instanceof UiViewElementNode) { + sb.append(" "); + UiViewElementNode node = (UiViewElementNode) cookie; + sb.append("<"); + sb.append(node.getDescriptor().getXmlName()); + sb.append("> "); + } else if (cookie != null) { + sb.append(" cookie=" + cookie); + } + + System.out.println(sb.toString()); + + for (ViewInfo child : info.getChildren()) { + dump(child, depth + 1); + } + } + + /** Helper for {@link #dump(GraphicalEditorPart, ViewInfo)} */ + public static void dump(CanvasViewInfo info, int depth) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < depth; i++) { + sb.append(" "); + } + sb.append(info.getName()); + sb.append(" ["); + sb.append(info.getAbsRect()); + sb.append("], node="); + sb.append(info.getUiViewNode()); + + System.out.println(sb.toString()); + + for (CanvasViewInfo child : info.getChildren()) { + dump(child, depth + 1); + } + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPointTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPointTest.java new file mode 100644 index 000000000..366691e61 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPointTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.eclipse.org/org/documents/epl-v10.php + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.adt.internal.editors.layout.gle2; + +import org.eclipse.swt.events.MouseEvent; + +public class ControlPointTest extends PointTestCases { + public void testCreateFromMouseEvent() throws Exception { + MouseEvent mouseEvent = canvasMouseEvent(10, 20, 0); + + ControlPoint point = ControlPoint.create(mCanvas, mouseEvent); + assertEquals(10, point.x); + assertEquals(20, point.y); + } + + public void testCreateFromCoordinates() throws Exception { + ControlPoint point = ControlPoint.create(mCanvas, 10, 20); + assertEquals(10, point.x); + assertEquals(20, point.y); + } + + public void testConvertToLayout() throws Exception { + ControlPoint point = ControlPoint.create(new TestLayoutCanvas(), 10, 20); + assertEquals(10, point.x); + assertEquals(20, point.y); + + LayoutPoint layoutPoint = point.toLayout(); + assertNotNull(layoutPoint); + assertEquals(40, layoutPoint.x); + assertEquals(60, layoutPoint.y); + + // For sanity let's also convert back and verify + ControlPoint controlPoint = layoutPoint.toControl(); + assertNotNull(controlPoint); + assertNotSame(controlPoint, point); + assertEquals(point, controlPoint); + assertEquals(10, controlPoint.x); + assertEquals(20, controlPoint.y); + } + + public void testEquals() throws Exception { + ControlPoint point1 = ControlPoint.create(mCanvas, 1, 1); + ControlPoint point2 = ControlPoint.create(mCanvas, 1, 2); + ControlPoint point3 = ControlPoint.create(mCanvas, 2, 1); + ControlPoint point2b = ControlPoint.create(mCanvas, 1, 2); + + assertFalse(point2.equals(null)); + + assertEquals(point2, point2); + assertEquals(point2, point2b); + assertEquals(point2.hashCode(), point2b.hashCode()); + assertNotSame(point2, point2b); + + assertFalse(point1.equals(point2)); + assertFalse(point1.equals(point3)); + assertFalse(point2.equals(point3)); + assertFalse(point1.equals(point2)); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilitiesTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilitiesTest.java new file mode 100644 index 000000000..6576bea3a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilitiesTest.java @@ -0,0 +1,206 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import static com.android.SdkConstants.ANDROID_URI; +import static com.android.SdkConstants.TOOLS_URI; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.Arrays; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class DomUtilitiesTest extends TestCase { + + public void testIsEquivalent() throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setValidating(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document1 = builder.newDocument(); + Document document2 = builder.newDocument(); + document1.appendChild(document1.createElement("root")); + document2.appendChild(document2.createElement("root")); + + assertFalse(DomUtilities.isEquivalent(null, null)); + Element root1 = document1.getDocumentElement(); + assertFalse(DomUtilities.isEquivalent(null, root1)); + Element root2 = document2.getDocumentElement(); + assertFalse(DomUtilities.isEquivalent(root2, null)); + assertTrue(DomUtilities.isEquivalent(root1, root2)); + + root1.appendChild(document1.createTextNode(" ")); + // Differences in text are NOT significant! + assertTrue(DomUtilities.isEquivalent(root1, root2)); + root2.appendChild(document2.createTextNode(" ")); + assertTrue(DomUtilities.isEquivalent(root1, root2)); + + Element foo1 = document1.createElement("foo"); + Element foo2 = document2.createElement("foo"); + root1.appendChild(foo1); + assertFalse(DomUtilities.isEquivalent(root1, root2)); + root2.appendChild(foo2); + assertTrue(DomUtilities.isEquivalent(root1, root2)); + + root1.appendChild(document1.createElement("bar")); + assertFalse(DomUtilities.isEquivalent(root1, root2)); + root2.appendChild(document2.createElement("bar")); + assertTrue(DomUtilities.isEquivalent(root1, root2)); + + // Add attributes in opposite order + foo1.setAttribute("attribute1", "value1"); + foo1.setAttribute("attribute2", "value2"); + assertFalse(DomUtilities.isEquivalent(root1, root2)); + foo2.setAttribute("attribute2", "value2"); + foo2.setAttribute("attribute1", "valueWrong"); + assertFalse(DomUtilities.isEquivalent(root1, root2)); + foo2.setAttribute("attribute1", "value1"); + assertTrue(DomUtilities.isEquivalent(root1, root2)); + foo2.setAttributeNS(TOOLS_URI, "foo", "bar"); + assertTrue(DomUtilities.isEquivalent(root1, root2)); + foo2.setAttributeNS(ANDROID_URI, "foo", "bar"); + assertFalse(DomUtilities.isEquivalent(root1, root2)); + + // TODO - test different tag names + // TODO - test different name spaces! + } + + public void testIsContiguous() throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setValidating(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.newDocument(); + document.appendChild(document.createElement("root")); + Element root = document.getDocumentElement(); + root.appendChild(document.createTextNode(" ")); + Element foo = document.createElement("foo"); + root.appendChild(foo); + root.appendChild(document.createTextNode(" ")); + Element bar = document.createElement("bar"); + root.appendChild(bar); + Element baz = document.createElement("baz"); + root.appendChild(baz); + + assertTrue(DomUtilities.isContiguous(Arrays.asList(foo))); + assertTrue(DomUtilities.isContiguous(Arrays.asList(foo, bar))); + assertTrue(DomUtilities.isContiguous(Arrays.asList(foo, bar, baz))); + assertTrue(DomUtilities.isContiguous(Arrays.asList(foo, bar, baz))); + assertTrue(DomUtilities.isContiguous(Arrays.asList(bar, baz, foo))); + assertTrue(DomUtilities.isContiguous(Arrays.asList(baz, bar, foo))); + assertTrue(DomUtilities.isContiguous(Arrays.asList(baz, foo, bar))); + + assertFalse(DomUtilities.isContiguous(Arrays.asList(foo, baz))); + assertFalse(DomUtilities.isContiguous(Arrays.asList(root, baz))); + } + + public void testGetCommonAncestor() throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setValidating(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.newDocument(); + + // A + // / \ + // B C + // / \ + // D E + + document.appendChild(document.createElement("A")); + Element a = document.getDocumentElement(); + assertSame(a, DomUtilities.getCommonAncestor(a, a)); + + Element b = document.createElement("B"); + a.appendChild(b); + Element c = document.createElement("C"); + a.appendChild(c); + Element d = document.createElement("D"); + c.appendChild(d); + Element e = document.createElement("E"); + c.appendChild(e); + + assertSame(a, DomUtilities.getCommonAncestor(a, b)); + assertSame(a, DomUtilities.getCommonAncestor(b, a)); + assertSame(a, DomUtilities.getCommonAncestor(b, c)); + assertSame(a, DomUtilities.getCommonAncestor(b, d)); + assertSame(a, DomUtilities.getCommonAncestor(b, e)); + assertSame(a, DomUtilities.getCommonAncestor(a, e)); + + assertSame(c, DomUtilities.getCommonAncestor(d, e)); + assertSame(c, DomUtilities.getCommonAncestor(c, e)); + assertSame(c, DomUtilities.getCommonAncestor(d, c)); + assertSame(c, DomUtilities.getCommonAncestor(c, c)); + } + + public void testGetDepth() throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setValidating(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.newDocument(); + + // A + // / \ + // B C + // / \ + // D E + + document.appendChild(document.createElement("A")); + Element a = document.getDocumentElement(); + assertSame(a, DomUtilities.getCommonAncestor(a, a)); + Element b = document.createElement("B"); + a.appendChild(b); + Element c = document.createElement("C"); + a.appendChild(c); + Element d = document.createElement("D"); + c.appendChild(d); + Element e = document.createElement("E"); + c.appendChild(e); + + assertEquals(0, DomUtilities.getDepth(document)); + + assertEquals(1, DomUtilities.getDepth(a)); + assertEquals(2, DomUtilities.getDepth(b)); + assertEquals(2, DomUtilities.getDepth(c)); + assertEquals(3, DomUtilities.getDepth(d)); + assertEquals(3, DomUtilities.getDepth(e)); + } + + public void testHasChildren() throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setValidating(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.newDocument(); + assertFalse(DomUtilities.hasElementChildren(document)); + document.appendChild(document.createElement("A")); + Element a = document.getDocumentElement(); + assertFalse(DomUtilities.hasElementChildren(a)); + a.appendChild(document.createTextNode("foo")); + assertFalse(DomUtilities.hasElementChildren(a)); + Element b = document.createElement("B"); + a.appendChild(b); + assertTrue(DomUtilities.hasElementChildren(a)); + assertFalse(DomUtilities.hasElementChildren(b)); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtilsTest.java new file mode 100644 index 000000000..d1c56c233 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtilsTest.java @@ -0,0 +1,368 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import com.android.ide.common.api.Rect; + +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class ImageUtilsTest extends TestCase { + public void testCropBlank() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.dispose(); + + BufferedImage crop = ImageUtils.cropBlank(image, null); + assertNull(crop); + } + + public void testCropBlankPre() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.dispose(); + + BufferedImage crop = ImageUtils.cropBlank(image, new Rect(5, 5, 80, 80)); + assertNull(crop); + } + + public void testCropNonblank() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0, false)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.dispose(); + + BufferedImage crop = ImageUtils.cropBlank(image, null); + assertNotNull(crop); + assertEquals(image.getWidth(), crop.getWidth()); + assertEquals(image.getHeight(), crop.getHeight()); + } + + public void testCropSomething() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.setColor(new Color(0xFF00FF00, true)); + g.fillRect(25, 25, 50, 50); + g.dispose(); + + BufferedImage crop = ImageUtils.cropBlank(image, null); + assertNotNull(crop); + assertEquals(50, crop.getWidth()); + assertEquals(50, crop.getHeight()); + assertEquals(0xFF00FF00, crop.getRGB(0, 0)); + assertEquals(0xFF00FF00, crop.getRGB(49, 49)); + } + + public void testCropSomethingPre() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.setColor(new Color(0xFF00FF00, true)); + g.fillRect(25, 25, 50, 50); + g.dispose(); + + BufferedImage crop = ImageUtils.cropBlank(image, new Rect(0, 0, 100, 100)); + assertNotNull(crop); + assertEquals(50, crop.getWidth()); + assertEquals(50, crop.getHeight()); + assertEquals(0xFF00FF00, crop.getRGB(0, 0)); + assertEquals(0xFF00FF00, crop.getRGB(49, 49)); + } + + public void testCropSomethingPre2() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.setColor(new Color(0xFF00FF00, true)); + g.fillRect(25, 25, 50, 50); + g.dispose(); + + BufferedImage crop = ImageUtils.cropBlank(image, new Rect(5, 5, 80, 80)); + assertNotNull(crop); + assertEquals(50, crop.getWidth()); + assertEquals(50, crop.getHeight()); + assertEquals(0xFF00FF00, crop.getRGB(0, 0)); + assertEquals(0xFF00FF00, crop.getRGB(49, 49)); + } + + public void testCropColor() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0xFF00FF00, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.dispose(); + + BufferedImage crop = ImageUtils.cropColor(image, 0xFF00FF00, null); + assertNull(crop); + } + + public void testCropNonColor() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0xFF00FF00, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.dispose(); + + BufferedImage crop = ImageUtils.cropColor(image, 0xFFFF0000, null); + assertNotNull(crop); + assertEquals(image.getWidth(), crop.getWidth()); + assertEquals(image.getHeight(), crop.getHeight()); + } + + public void testCropColorSomething() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0xFF00FF00, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.setColor(new Color(0xFFFF0000, true)); + g.fillRect(25, 25, 50, 50); + g.dispose(); + + BufferedImage crop = ImageUtils.cropColor(image, 0xFF00FF00, null); + assertEquals(50, crop.getWidth()); + assertEquals(50, crop.getHeight()); + assertEquals(0xFFFF0000, crop.getRGB(0, 0)); + assertEquals(0xFFFF0000, crop.getRGB(49, 49)); + } + + public void testNothingTodo() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0xFF00FF00, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.dispose(); + + BufferedImage crop = ImageUtils.cropColor(image, 0xFFFF0000, new Rect(40, 40, 0, 0)); + assertNull(crop); + } + + public void testContainsDarkPixels() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.dispose(); + + assertFalse(ImageUtils.containsDarkPixels(image)); + + image.setRGB(50, 50, 0xFFFFFFFF); + assertFalse(ImageUtils.containsDarkPixels(image)); + image.setRGB(50, 50, 0xFFAAAAAA); + assertFalse(ImageUtils.containsDarkPixels(image)); + image.setRGB(50, 50, 0xFF00FF00); + assertFalse(ImageUtils.containsDarkPixels(image)); + image.setRGB(50, 50, 0xFFFF8800); + assertFalse(ImageUtils.containsDarkPixels(image)); + image.setRGB(50, 50, 0xFF333333); + assertTrue(ImageUtils.containsDarkPixels(image)); + + } + + public void testGetBoundingRectangle() { + assertEquals(null, ImageUtils.getBoundingRectangle(Collections.<Rectangle> emptyList())); + + assertEquals(new Rectangle(1, 2, 3, 4), ImageUtils.getBoundingRectangle(Arrays + .asList(new Rectangle(1, 2, 3, 4)))); + assertEquals(new Rectangle(1, 2, 3, 4), ImageUtils.getBoundingRectangle(Arrays + .asList(new Rectangle(1, 2, 3, 4), new Rectangle(1, 2, 1, 1)))); + assertEquals(new Rectangle(5, 5, 25, 25), ImageUtils.getBoundingRectangle(Arrays.asList( + new Rectangle(10, 10, 20, 20), new Rectangle(5, 5, 1, 1)))); + } + + /** + * Paints a set of {@link Rectangle} object out of a rendered {@link BufferedImage} + * such that the resulting image is transparent except for a minimum bounding + * rectangle of the selected elements. + * + * @param image the source image + * @param rectangles the set of rectangles to copy + * @param boundingBox the bounding rectangle of the set of rectangles to copy, can be + * computed by {@link ImageUtils#getBoundingRectangle} + * @param scale a scale factor to apply to the result, e.g. 0.5 to shrink the + * destination down 50%, 1.0 to leave it alone and 2.0 to zoom in by + * doubling the image size + * @return a rendered image, or null + */ + public static BufferedImage drawRectangles(BufferedImage image, + List<Rectangle> rectangles, Rectangle boundingBox, double scale) { + + // This code is not a test. When I implemented image cropping, I first implemented + // it for BufferedImages (since it's easier; easy image painting, easy scaling, + // easy transparency handling, etc). However, this meant that we would need to + // convert the SWT images from the ImageOverlay to BufferedImages, crop and convert + // back; not ideal, so I rewrote it in SWT (see SwtUtils). However, I + // don't want to throw away the code in case we start keeping BufferedImages rather + // than SWT images or need it for other purposes, but rather than place it in the + // production codebase I'm leaving this utility here in the associated ImageUtils + // test class. It was used like this: + // @formatter:off + // + // BufferedImage wholeImage = SwtUtils.convertToAwt(image); + // BufferedImage result = ImageUtils.cropSelection(wholeImage, + // rectangles, boundingBox, scale); + // e.image = SwtUtils.convertToSwt(image.getDevice(), result, true, + // DRAG_TRANSPARENCY); + // + // @formatter:on + + if (boundingBox == null) { + return null; + } + + int destWidth = (int) (scale * boundingBox.width); + int destHeight = (int) (scale * boundingBox.height); + BufferedImage dest = new BufferedImage(destWidth, destHeight, image.getType()); + + Graphics2D g = dest.createGraphics(); + + for (Rectangle bounds : rectangles) { + int dx1 = bounds.x - boundingBox.x; + int dy1 = bounds.y - boundingBox.y; + int dx2 = dx1 + bounds.width; + int dy2 = dy1 + bounds.height; + + dx1 *= scale; + dy1 *= scale; + dx2 *= scale; + dy2 *= scale; + + int sx1 = bounds.x; + int sy1 = bounds.y; + int sx2 = sx1 + bounds.width; + int sy2 = sy1 + bounds.height; + + g.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null); + } + + g.dispose(); + + return dest; + } + + public void testSubImage() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0xFF00FF00, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.setColor(new Color(0xFFFF0000, true)); + g.fillRect(25, 25, 50, 50); + g.dispose(); + + BufferedImage sub = ImageUtils.subImage(image, 25, 25, 35, 45); + assertEquals(10, sub.getWidth()); + assertEquals(20, sub.getHeight()); + assertEquals(0xFFFF0000, sub.getRGB(0, 0)); + assertEquals(0xFFFF0000, sub.getRGB(9, 9)); + + sub = ImageUtils.subImage(image, 23, 23, 23 + 5, 23 + 5); + assertEquals(5, sub.getWidth()); + assertEquals(5, sub.getHeight()); + assertEquals(0xFF00FF00, sub.getRGB(0, 0)); + assertEquals(0xFFFF0000, sub.getRGB(4, 4)); + } + + public void testGetColor() throws Exception { + assertEquals(0xFF000000, ImageUtils.getColor("#000")); + assertEquals(0xFF000000, ImageUtils.getColor("#000000")); + assertEquals(0xABCDEF91, ImageUtils.getColor("#ABCDEF91")); + } + + public void testGetBrightness() throws Exception { + assertEquals(96, ImageUtils.getBrightness(0x456789)); + assertEquals(198, ImageUtils.getBrightness(0xABCDEF)); + + assertEquals(0, ImageUtils.getBrightness(0x0)); + assertEquals(255, ImageUtils.getBrightness(0xFFFFFF)); + assertEquals(299*255/1000, ImageUtils.getBrightness(0xFF0000)); + assertEquals(587*255/1000, ImageUtils.getBrightness(0x00FF00)); + assertEquals(114*255/1000, ImageUtils.getBrightness(0x0000FF)); + } + + public void testColorConversion() throws Exception { + assertEquals(0, ImageUtils.rgbToInt(ImageUtils.intToRgb(0), 0)); + assertEquals(0xFFFFFFFF, ImageUtils.rgbToInt(ImageUtils.intToRgb(0xFFFFFF), 0xFF)); + assertEquals(0x12345678, ImageUtils.rgbToInt(ImageUtils.intToRgb(0x345678), 0x12)); + } + + public void testScaleImage() throws Exception { + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); + Graphics g = image.getGraphics(); + g.setColor(new Color(0xFF00FF00, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.setColor(new Color(0xFFFF0000, true)); + g.fillRect(25, 25, 50, 50); + g.dispose(); + + BufferedImage scaled = ImageUtils.scale(image, 0.5, 0.5); + assertEquals(50, scaled.getWidth()); + assertEquals(50, scaled.getHeight()); + assertEquals(0xFF00FF00, scaled.getRGB(0, 0)); + assertEquals(0xFF00FF00, scaled.getRGB(49, 49)); + assertEquals(0xFFFF0000, scaled.getRGB(25, 25)); + + scaled = ImageUtils.scale(image, 2.0, 2.0); + assertEquals(200, scaled.getWidth()); + assertEquals(200, scaled.getHeight()); + assertEquals(0xFF00FF00, scaled.getRGB(0, 0)); + assertEquals(0xFF00FF00, scaled.getRGB(48, 48)); + assertEquals(0xFFFF0000, scaled.getRGB(100, 100)); + assertEquals(0xFF00FF00, scaled.getRGB(199, 199)); + + scaled = ImageUtils.scale(image, 0.25, 0.25); + assertEquals(25, scaled.getWidth()); + assertEquals(25, scaled.getHeight()); + assertEquals(0xFF00FF00, scaled.getRGB(0, 0)); + assertEquals(0xFF00FF00, scaled.getRGB(24, 24)); + assertEquals(0xFFFF0000, scaled.getRGB(13, 13)); + + scaled = ImageUtils.scale(image, 0.25, 0.25, 75, 95); + assertEquals(100, scaled.getWidth()); + assertEquals(120, scaled.getHeight()); + assertEquals(0xFF00FF00, scaled.getRGB(0, 0)); + assertEquals(0xFF00FF00, scaled.getRGB(24, 24)); + assertEquals(0xFFFF0000, scaled.getRGB(13, 13)); + + } + + public void testCreateColoredImage() throws Exception { + BufferedImage image = ImageUtils.createColoredImage(120, 110, new RGB(0xFE, 0xFD, 0xFC)); + assertEquals(120, image.getWidth()); + assertEquals(110, image.getHeight()); + assertEquals(0xFFFEFDFC, image.getRGB(0, 0)); + assertEquals(0xFFFEFDFC, image.getRGB(50, 50)); + assertEquals(0xFFFEFDFC, image.getRGB(119, 109)); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinderTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinderTest.java new file mode 100644 index 000000000..c86623cd6 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinderTest.java @@ -0,0 +1,128 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class IncludeFinderTest extends TestCase { + public void testEncodeDecode1() throws Exception { + // Test ending with just a key + String s = "bar,baz,foo"; + assertEquals(s, IncludeFinder.encodeMap(IncludeFinder.decodeMap(s))); + } + + public void testDecode1() throws Exception { + // Test ending with just a key + String s = "foo"; + assertTrue(IncludeFinder.decodeMap(s).containsKey("foo")); + assertEquals(0, IncludeFinder.decodeMap(s).get("foo").size()); + } + + public void testDecode2() throws Exception { + // Test ending with just a key + String s = "foo=>{bar,baz}"; + assertTrue(IncludeFinder.decodeMap(s).containsKey("foo")); + assertEquals("[bar, baz]", + IncludeFinder.decodeMap(s).get("foo").toString()); + } + + public void testNoBlanks() throws Exception { + // Make sure we skip the }, + String s = "foo=>{bar,baz},bar"; + assertNull(IncludeFinder.decodeMap(s).get("")); + } + + public void testEncodeDecode2() throws Exception { + // Test ending with just a key + String s = "bar,key1=>{value1,value2},key2=>{value3,value4}"; + assertEquals(s, IncludeFinder.encodeMap(IncludeFinder.decodeMap(s))); + } + + public void testUpdates() throws Exception { + IncludeFinder finder = IncludeFinder.create(); + assertEquals(null, finder.getIncludedBy("foo")); + + finder.setIncluded("bar", Arrays.<String>asList("foo", "baz"), false); + finder.setIncluded("baz", Arrays.<String>asList("foo"), false); + assertEquals(Arrays.asList("bar", "baz"), finder.getIncludedBy("foo")); + finder.setIncluded("bar", Collections.<String>emptyList(), false); + assertEquals(Arrays.asList("baz"), finder.getIncludedBy("foo")); + finder.setIncluded("baz", Collections.<String>emptyList(), false); + assertEquals(Collections.emptyList(), finder.getIncludedBy("foo")); + } + + public void testFindIncludes() throws Exception { + String xml = + "<?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=\"match_parent\"\n" + + " android:orientation=\"vertical\" >\n" + + "\n" + + " <RadioButton\n" + + " android:id=\"@+id/radioButton1\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:text=\"RadioButton\" />\n" + + "\n" + + " <include\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " layout=\"@layout/layout3\" />\n" + + "\n" + + " <include\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " layout=\"@layout/layout4\" />\n" + + "\n" + + "</LinearLayout>"; + List<String> includes = IncludeFinder.findIncludes(xml); + Collections.sort(includes); + assertEquals(Arrays.asList("layout3", "layout4"), includes); + } + + public void testFindFragments() throws Exception { + String xml = + "<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:context=\".MainActivity\" >\n" + + "\n" + + " <fragment\n" + + " android:id=\"@+id/fragment1\"\n" + + " android:name=\"android.app.ListFragment\"\n" + + " android:layout_width=\"wrap_content\"\n" + + " android:layout_height=\"wrap_content\"\n" + + " android:layout_alignParentLeft=\"true\"\n" + + " android:layout_alignParentTop=\"true\"\n" + + " android:layout_marginLeft=\"58dp\"\n" + + " android:layout_marginTop=\"74dp\"\n" + + " tools:layout=\"@layout/myfragment\" />\n" + + "\n" + + "</RelativeLayout>"; + List<String> includes = IncludeFinder.findIncludes(xml); + Collections.sort(includes); + assertEquals(Arrays.asList("myfragment"), includes); + } + + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeOverlayTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeOverlayTest.java new file mode 100644 index 000000000..a96b1a387 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeOverlayTest.java @@ -0,0 +1,75 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import org.eclipse.swt.graphics.Rectangle; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import junit.framework.TestCase; + +public class IncludeOverlayTest extends TestCase { + + public void testSubtractRectangles() throws Exception { + checkSubtract(new Rectangle(0, 0, 100, 80), Collections.<Rectangle> emptyList()); + + checkSubtract(new Rectangle(0, 0, 100, 80), Arrays.asList(new Rectangle(50, 50, 20, 20))); + + checkSubtract(new Rectangle(0, 0, 100, 80), Arrays.asList(new Rectangle(50, 50, 20, 20), + new Rectangle(90, 90, 10, 10))); + + checkSubtract(new Rectangle(0, 0, 100, 80), Arrays.asList(new Rectangle(50, 50, 20, 20), + new Rectangle(90, 90, 10, 10), new Rectangle(0, 0, 10, 10))); + + } + + private void checkSubtract(Rectangle rectangle, List<Rectangle> holes) { + Collection<Rectangle> result = IncludeOverlay.subtractRectangles(rectangle, holes); + + // Do some Monte Carlo testing - pick random coordinates and check that if they + // are within one of the holes then they are not in the result list and vice versa + Random random = new Random(42L); + for (int i = 0; i < 1000; i++) { + int x = random.nextInt(rectangle.width); + int y = random.nextInt(rectangle.height); + + boolean inHole = false; + for (Rectangle hole : holes) { + if (hole.contains(x, y)) { + inHole = true; + } + } + + boolean inResult = false; + for (Rectangle r : result) { + if (r.contains(x, y)) { + inResult = true; + break; + } + } + + if (inHole == inResult) { + fail("Wrong result at (" + x + "," + y + ") for rectangle=" + rectangle + + " and holes=" + holes + " where inHole=inResult=" + + inResult); + } + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutPointTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutPointTest.java new file mode 100644 index 000000000..c053de854 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutPointTest.java @@ -0,0 +1,64 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +public class LayoutPointTest extends PointTestCases { + public void testCreateFromCoordinates() throws Exception { + LayoutPoint point = LayoutPoint.create(mCanvas, 10, 20); + assertEquals(10, point.x); + assertEquals(20, point.y); + } + + public void testEquals() throws Exception { + LayoutPoint point1 = LayoutPoint.create(mCanvas, 1, 1); + LayoutPoint point2 = LayoutPoint.create(mCanvas, 1, 2); + LayoutPoint point3 = LayoutPoint.create(mCanvas, 2, 1); + LayoutPoint point2b = LayoutPoint.create(mCanvas, 1, 2); + + assertFalse(point2.equals(null)); + + assertEquals(point2, point2); + assertEquals(point2, point2b); + assertEquals(point2.hashCode(), point2b.hashCode()); + assertNotSame(point2, point2b); + + assertFalse(point1.equals(point2)); + assertFalse(point1.equals(point3)); + assertFalse(point2.equals(point3)); + assertFalse(point1.equals(point2)); + } + + public void testConvertToControl() throws Exception { + LayoutPoint point = LayoutPoint.create(new TestLayoutCanvas(), 10, 20); + assertEquals(10, point.x); + assertEquals(20, point.y); + + ControlPoint controlPoint = point.toControl(); + assertNotNull(controlPoint); + assertEquals(-5, controlPoint.x); + assertEquals(0, controlPoint.y); + + // For sanity let's also convert back and verify + LayoutPoint layoutPoint = controlPoint.toLayout(); + assertNotNull(layoutPoint); + assertNotSame(layoutPoint, point); + assertEquals(point, layoutPoint); + assertEquals(10, layoutPoint.x); + assertEquals(20, layoutPoint.y); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PointTestCases.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PointTestCases.java new file mode 100644 index 000000000..91d0e133f --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PointTestCases.java @@ -0,0 +1,107 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.List; +import org.eclipse.swt.widgets.ScrollBar; +import org.eclipse.swt.widgets.Shell; + +import junit.framework.TestCase; + +/** + * Common utilities for the point tests {@link LayoutPointTest} and + * {@link ControlPointTest} + */ +public class PointTestCases extends TestCase { + LayoutCanvas mCanvas = new TestLayoutCanvas(); + + protected MouseEvent canvasMouseEvent(int x, int y, int stateMask) { + Event event = new Event(); + event.x = x; + event.y = y; + event.stateMask = stateMask; + event.widget = mCanvas; + MouseEvent mouseEvent = new MouseEvent(event); + return mouseEvent; + } + + /** Mock implementation of LayoutCanvas */ + protected static class TestLayoutCanvas extends LayoutCanvas { + float mScaleX; + + float mScaleY; + + float mTranslateX; + + float mTranslateY; + + public TestLayoutCanvas(float scaleX, float scaleY, float translateX, float translateY) { + super(null, null, new Shell(), 0); + + this.mScaleX = scaleX; + this.mScaleY = scaleY; + this.mTranslateX = translateX; + this.mTranslateY = translateY; + } + + public TestLayoutCanvas() { + this(2.0f, 2.0f, 20.0f, 20.0f); + } + + @Override + CanvasTransform getHorizontalTransform() { + ScrollBar scrollBar = new List(this, SWT.V_SCROLL|SWT.H_SCROLL).getHorizontalBar(); + return new TestCanvasTransform(scrollBar, mScaleX, mTranslateX); + } + + @Override + CanvasTransform getVerticalTransform() { + ScrollBar scrollBar = new List(this, SWT.V_SCROLL|SWT.H_SCROLL).getVerticalBar(); + return new TestCanvasTransform(scrollBar, mScaleY, mTranslateY); + } + } + + static class TestCanvasTransform extends CanvasTransform { + float mScale; + + float mTranslate; + + public TestCanvasTransform(ScrollBar scrollBar, float scale, float translate) { + super(null, scrollBar); + this.mScale = scale; + this.mTranslate = translate; + } + + @Override + public int translate(int value) { + return (int) ((value - mTranslate) / mScale); + } + + @Override + public int inverseTranslate(int value) { + return (int) (value * mScale + mTranslate); + } + } + + 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/eclipse/adt/internal/editors/layout/gle2/RenderLoggerTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderLoggerTest.java new file mode 100644 index 000000000..c28d9a966 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderLoggerTest.java @@ -0,0 +1,82 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import junit.framework.TestCase; + +public class RenderLoggerTest extends TestCase { + public void testLogger1() throws Exception { + RenderLogger l = new RenderLogger("foo", null); + assertFalse(l.hasProblems()); + } + + public void testLogger2() throws Exception { + RenderLogger l = new RenderLogger("foo", null); + assertFalse(l.hasProblems()); + l.fidelityWarning(null, "No perspective Transforms", null, null); + l.fidelityWarning(null, "No GPS", null, null); + assertTrue(l.hasProblems()); + assertEquals("The graphics preview in the layout editor may not be accurate:\n" + + "* No perspective Transforms\n" + "* No GPS\n", l.getProblems(true)); + assertFalse(l.seenTag("foo")); + assertFalse(l.seenTag(null)); + } + + public void testLogger3() throws Exception { + RenderLogger l = new RenderLogger("foo", null); + assertFalse(l.hasProblems()); + l.error("timeout", "Sample Error", new RuntimeException(), null); + l.warning("slow", "Sample warning", null); + assertTrue(l.hasProblems()); + assertEquals("Sample Error\n" + "Sample warning\n" + + "Exception details are logged in Window > Show View > Error Log", + l.getProblems(true)); + assertFalse(l.seenTag("foo")); + assertTrue(l.seenTag("timeout")); + assertTrue(l.seenTag("slow")); + assertFalse(l.seenTagPrefix("foo")); + assertTrue(l.seenTagPrefix("timeout")); + assertTrue(l.seenTagPrefix("slow")); + assertTrue(l.seenTagPrefix("time")); + assertFalse(l.seenTagPrefix("timeouts")); + } + + public void testLoggerSuppressWarnings() throws Exception { + RenderLogger l = new RenderLogger("foo", null); + assertFalse(l.hasProblems()); + RenderLogger.ignoreFidelityWarning("No perspective Transforms"); + l.fidelityWarning(null, "No perspective Transforms", null, null); + l.fidelityWarning(null, "No GPS", null, null); + assertTrue(l.hasProblems()); + assertEquals("The graphics preview in the layout editor may not be accurate:\n" + + "* No GPS\n", l.getProblems(true)); + assertEquals("", l.getProblems(false)); + assertFalse(l.seenTag("foo")); + assertFalse(l.seenTag(null)); + + l = new RenderLogger("foo", null); + assertFalse(l.hasProblems()); + RenderLogger.ignoreFidelityWarning("No perspective Transforms"); + RenderLogger.ignoreFidelityWarning("No GPS"); + l.fidelityWarning(null, "No perspective Transforms", null, null); + l.fidelityWarning(null, "No GPS", null, null); + assertFalse(l.hasProblems()); + assertEquals("", l.getProblems(true)); + assertFalse(l.seenTag("foo")); + assertFalse(l.seenTag(null)); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java new file mode 100644 index 000000000..b29f9f3c5 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java @@ -0,0 +1,120 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import com.android.ide.common.rendering.api.ViewInfo; +import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; + +import org.eclipse.swt.widgets.Shell; + +import java.util.Arrays; + +import junit.framework.TestCase; + +public class SelectionManagerTest extends TestCase { + private SelectionManager createManager() { + LayoutCanvas canvas = new LayoutCanvas(null, null, new Shell(), 0); + return new SelectionManager(canvas); + } + + public void testEmpty() { + SelectionManager manager = createManager(); + + assertNotNull(manager.getSelections()); + assertEquals(0, manager.getSelections().size()); + assertFalse(manager.hasMultiSelection()); + assertTrue(manager.isEmpty()); + } + + public void testBasic() { + SelectionManager manager = createManager(); + assertTrue(manager.isEmpty()); + + UiViewElementNode rootNode = CanvasViewInfoTest.createNode("android.widget.LinearLayout", + true); + ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); + UiViewElementNode child1Node = CanvasViewInfoTest.createNode(rootNode, + "android.widget.Button", false); + ViewInfo child1 = new ViewInfo("Button1", child1Node, 0, 0, 50, 20); + UiViewElementNode child2Node = CanvasViewInfoTest.createNode(rootNode, + "android.widget.Button", false); + ViewInfo child2 = new ViewInfo("Button2", child2Node, 0, 20, 70, 25); + root.setChildren(Arrays.asList(child1, child2)); + CanvasViewInfo rootView = CanvasViewInfo.create(root, true /* layoutlib5 */).getFirst(); + assertNotNull(rootView); + + manager.selectMultiple(Arrays.asList(rootView, rootView.getChildren().get(0), rootView + .getChildren().get(1))); + assertEquals(3, manager.getSelections().size()); + assertFalse(manager.isEmpty()); + assertTrue(manager.hasMultiSelection()); + + // Expect read-only result; ensure that's the case + try { + manager.getSelections().remove(0); + fail("Result should be read only collection"); + } catch (Exception e) { + ; //ok, what we expected + } + + manager.selectNone(); + assertEquals(0, manager.getSelections().size()); + assertTrue(manager.isEmpty()); + + manager.selectSingle(rootView); + assertEquals(1, manager.getSelections().size()); + assertFalse(manager.isEmpty()); + assertSame(rootView, manager.getSelections().get(0).getViewInfo()); + + manager.selectMultiple(Arrays.asList(rootView, rootView.getChildren().get(0), rootView + .getChildren().get(1))); + assertEquals(3, manager.getSelections().size()); + + manager.deselect(rootView.getChildren().get(0)); + assertEquals(2, manager.getSelections().size()); + manager.deselect(rootView); + assertEquals(1, manager.getSelections().size()); + assertSame(rootView.getChildren().get(1), manager.getSelections().get(0).getViewInfo()); + } + + public void testSelectParent() { + SelectionManager manager = createManager(); + assertTrue(manager.isEmpty()); + + UiViewElementNode rootNode = CanvasViewInfoTest.createNode("android.widget.LinearLayout", + true); + ViewInfo root = new ViewInfo("LinearLayout", rootNode, 10, 10, 100, 100); + UiViewElementNode child1Node = CanvasViewInfoTest.createNode(rootNode, + "android.widget.Button", false); + ViewInfo child1 = new ViewInfo("Button1", child1Node, 0, 0, 50, 20); + UiViewElementNode child2Node = CanvasViewInfoTest.createNode(rootNode, + "android.widget.Button", false); + ViewInfo child2 = new ViewInfo("Button2", child2Node, 0, 20, 70, 25); + root.setChildren(Arrays.asList(child1, child2)); + CanvasViewInfo rootView = CanvasViewInfo.create(root, true /* layoutlib5 */).getFirst(); + assertNotNull(rootView); + + manager.selectMultiple(Arrays.asList(rootView.getChildren().get(0))); + assertEquals(1, manager.getSelections().size()); + assertFalse(manager.isEmpty()); + assertSame(rootView.getChildren().get(0), manager.getSelections().get(0).getViewInfo()); + + manager.selectParent(); + assertEquals(1, manager.getSelections().size()); + assertFalse(manager.isEmpty()); + assertSame(rootView, manager.getSelections().get(0).getViewInfo()); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttributeTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttributeTest.java new file mode 100755 index 000000000..f4f8eb81a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttributeTest.java @@ -0,0 +1,109 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import com.android.SdkConstants; + +import junit.framework.TestCase; + +public class SimpleAttributeTest extends TestCase { + + private SimpleAttribute a; + + @Override + protected void setUp() throws Exception { + super.setUp(); + a = new SimpleAttribute(SdkConstants.NS_RESOURCES, "name", "value = with space "); + } + + public final void testSimpleAttribute() { + assertEquals(SdkConstants.NS_RESOURCES, a.getUri()); + assertEquals("name", a.getName()); + assertEquals("value = with space ", a.getValue()); + } + + public final void testGetUri() { + assertEquals(SdkConstants.NS_RESOURCES, a.getUri()); + } + + public final void testGetName() { + assertEquals("name", a.getName()); + } + + public final void testGetValue() { + assertEquals("value = with space ", a.getValue()); + } + + public final void testToString() { + assertEquals( + "@name:" + SdkConstants.NS_RESOURCES + "=value = with space \n", + a.toString()); + } + + public final void testParseString() { + String s = "@name:" + SdkConstants.NS_RESOURCES + "=value = with space \n"; + SimpleAttribute b = SimpleAttribute.parseString(s); + assertEquals(a, b); + } + + public final void testEqualsObject() { + assertFalse(a.equals(null)); + assertFalse(a.equals(new Object())); + + // wrong name + assertFalse(a.equals(new SimpleAttribute(SdkConstants.NS_RESOURCES, + "wrong name", + "value = with space "))); + // wrong value + assertFalse(a.equals(new SimpleAttribute(SdkConstants.NS_RESOURCES, + "name", + "value"))); + // wrong uri + assertFalse(a.equals(new SimpleAttribute("uri", "name", "value = with space "))); + // all fields wrong + assertFalse(a.equals(new SimpleAttribute("uri", "wrong name", "value"))); + + assertTrue(a.equals(new SimpleAttribute(SdkConstants.NS_RESOURCES, + "name", + "value = with space "))); + } + + public final void testHashCode() { + + int ah = a.hashCode(); + + assertFalse(ah == new Object().hashCode()); + + // wrong name + assertFalse(ah == new SimpleAttribute(SdkConstants.NS_RESOURCES, + "wrong name", + "value = with space ").hashCode()); + // wrong value + assertFalse(ah == new SimpleAttribute(SdkConstants.NS_RESOURCES, + "name", + "value").hashCode()); + // wrong uri + assertFalse(ah == new SimpleAttribute("uri", "name", "value = with space ").hashCode()); + // all fields wrong + assertFalse(ah == new SimpleAttribute("uri", "wrong name", "value").hashCode()); + + assertEquals(ah, new SimpleAttribute(SdkConstants.NS_RESOURCES, + "name", + "value = with space ").hashCode()); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElementTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElementTest.java new file mode 100755 index 000000000..091aa378c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElementTest.java @@ -0,0 +1,343 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import com.android.ide.common.api.Rect; + +import java.util.Arrays; + +import junit.framework.TestCase; + +public class SimpleElementTest extends TestCase { + + private SimpleElement e; + + /** + * Helper method to compare arrays' *content* is equal (instead of object identity). + * Also produces a suitable output to understand mismatch, if any. + * <p/> + * Pre-requisite: The arrays' elements must properly implement {@link Object#equals(Object)} + * and a sensible {@link Object#toString()}. + */ + private static void assertArrayEquals(Object[] expected, Object[] actual) { + if (!Arrays.equals(expected, actual)) { + // In case of failure, transform the arguments into strings and let + // assertEquals(string) handle it as it can produce a nice diff of the string. + String strExpected = expected == null ? "(null)" : Arrays.toString(expected); + String strActual = actual == null ? "(null)" : Arrays.toString(actual); + + if (strExpected.equals(strActual)) { + fail(String.format("Array not equal:\n Expected[%d]=%s\n Actual[%d]=%s", + expected == null ? 0 : expected.length, + strExpected, + actual == null ? 0 : actual.length, + strActual)); + } else { + assertEquals(strExpected, strActual); + } + } + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + e = new SimpleElement("android.view.LinearLayout", // fqcn + "android.view.FrameLayout", // parentFqcn + new Rect(10, 5, 60, 40), // bounds + new Rect(0, 0, 320, 480)); // parentBounds + } + + public final void testGetFqcn() { + assertEquals("android.view.LinearLayout", e.getFqcn()); + } + + public final void testGetParentFqcn() { + assertEquals("android.view.FrameLayout", e.getParentFqcn()); + } + + public final void testGetBounds() { + assertEquals(new Rect(10, 5, 60, 40), e.getBounds()); + } + + public final void testGetParentBounds() { + assertEquals(new Rect(0, 0, 320, 480), e.getParentBounds()); + } + + public final void testToString() { + assertEquals("{V=3,N=android.view.LinearLayout,P=android.view.FrameLayout,R=10 5 60 40,Q=0 0 320 480\n" + + "}\n", + e.toString()); + + e.addAttribute(new SimpleAttribute("uri", "name", "value")); + e.addAttribute(new SimpleAttribute("my-uri", "second-name", "my = value ")); + + assertEquals("{V=3,N=android.view.LinearLayout,P=android.view.FrameLayout,R=10 5 60 40,Q=0 0 320 480\n" + + "@name:uri=value\n" + + "@second-name:my-uri=my = value \n" + + "}\n", + e.toString()); + + SimpleElement e2 = new SimpleElement("android.view.Button", + "android.view.LinearLayout", + new Rect(10, 20, 30, 40), + new Rect(0, 0, 320, 480)); + e2.addAttribute(new SimpleAttribute("uri1", "name1", "value1")); + SimpleElement e3 = new SimpleElement("android.view.CheckBox", + "android.view.LinearLayout", + new Rect(-1, -2, -3, -4), // invalid rect is ignored + new Rect(-1, -2, -3, -4)); // invalid rectis ignored + e3.addAttribute(new SimpleAttribute("uri2", "name2", "value2")); + e3.addAttribute(new SimpleAttribute("uri3", "name3", "value3")); + e.addInnerElement(e2); + e.addInnerElement(e3); + + assertEquals("{V=3,N=android.view.LinearLayout,P=android.view.FrameLayout,R=10 5 60 40,Q=0 0 320 480\n" + + "@name:uri=value\n" + + "@second-name:my-uri=my = value \n" + + "{V=3,N=android.view.Button,P=android.view.LinearLayout,R=10 20 30 40,Q=0 0 320 480\n" + + "@name1:uri1=value1\n" + + "}\n" + + "{V=3,N=android.view.CheckBox,P=android.view.LinearLayout\n" + + "@name2:uri2=value2\n" + + "@name3:uri3=value3\n" + + "}\n" + + "}\n", + e.toString()); + } + + public final void testParseString() { + assertArrayEquals( + new SimpleElement[] { new SimpleElement("android.view.LinearLayout", + null, null, null) }, + SimpleElement.parseString( + "{V=3,N=android.view.LinearLayout\n" + + "}\n")); + + assertArrayEquals( + new SimpleElement[] { new SimpleElement("android.view.LinearLayout", + "android.view.FrameLayout", + null, null) }, + SimpleElement.parseString( + "{V=3,N=android.view.LinearLayout,P=android.view.FrameLayout\n" + + "}\n")); + + assertArrayEquals( + new SimpleElement[] { new SimpleElement("android.view.LinearLayout", + null, + new Rect(10, 5, 60, 40), + new Rect(0, 0, 320, 480)) }, + SimpleElement.parseString( + "{V=3,N=android.view.LinearLayout,R=10 5 60 40,Q=0 0 320 480\n" + + "}\n")); + + + assertArrayEquals( + new SimpleElement[] { e }, + SimpleElement.parseString( + "{V=3,N=android.view.LinearLayout,P=android.view.FrameLayout,R=10 5 60 40,Q=0 0 320 480\n" + + "}\n")); + + + e.addAttribute(new SimpleAttribute("uri", "name", "value")); + e.addAttribute(new SimpleAttribute("my-uri", "second-name", "my = value ")); + + assertArrayEquals( + new SimpleElement[] { e }, + SimpleElement.parseString( + "{V=3,N=android.view.LinearLayout,P=android.view.FrameLayout,R=10 5 60 40,Q=0 0 320 480\n" + + "@name:uri=value\n" + + "@second-name:my-uri=my = value \n" + + "}\n")); + + + SimpleElement e2 = new SimpleElement("android.view.Button", + "android.view.LinearLayout", + new Rect(10, 20, 30, 40), + new Rect(0, 0, 320, 480)); + e2.addAttribute(new SimpleAttribute("uri1", "name1", "value1")); + SimpleElement e3 = new SimpleElement("android.view.CheckBox", + "android.view.LinearLayout", + new Rect(-1, -2, -3, -4), + new Rect(-1, -2, -3, -4)); + e3.addAttribute(new SimpleAttribute("uri2", "name2", "value2")); + e3.addAttribute(new SimpleAttribute("uri3", "name3", "value3")); + e.addInnerElement(e2); + e.addInnerElement(e3); + + assertArrayEquals( + new SimpleElement[] { e }, + SimpleElement.parseString( + "{V=3,N=android.view.LinearLayout,P=android.view.FrameLayout,R=10 5 60 40,Q=0 0 320 480\n" + + "@name:uri=value\n" + + "@second-name:my-uri=my = value \n" + + "{V=3,N=android.view.Button,P=android.view.LinearLayout,R=10 20 30 40,Q=0 0 320 480\n" + + "@name1:uri1=value1\n" + + "}\n" + + "{V=3,N=android.view.CheckBox,P=android.view.LinearLayout,R=-1 -2 -3 -4,Q=-1 -2 -3 -4\n" + + "@name2:uri2=value2\n" + + "@name3:uri3=value3\n" + + "}\n" + + "}\n")); + + // Parse string can also parse an array of elements + assertArrayEquals( + new SimpleElement[] { e, e2, e3 }, + SimpleElement.parseString( + "{V=3,N=android.view.LinearLayout,P=android.view.FrameLayout,R=10 5 60 40,Q=0 0 320 480\n" + + "@name:uri=value\n" + + "@second-name:my-uri=my = value \n" + + "{V=3,N=android.view.Button,P=android.view.LinearLayout,R=10 20 30 40,Q=0 0 320 480\n" + + "@name1:uri1=value1\n" + + "}\n" + + "{V=3,N=android.view.CheckBox,P=android.view.LinearLayout,R=-1 -2 -3 -4\n" + + "@name2:uri2=value2\n" + + "@name3:uri3=value3\n" + + "}\n" + + "}\n" + + "{V=3,N=android.view.Button,P=android.view.LinearLayout,R=10 20 30 40,Q=0 0 320 480\n" + + "@name1:uri1=value1\n" + + "}\n" + + "{V=3,N=android.view.CheckBox,P=android.view.LinearLayout,R=-1 -2 -3 -4,Q=-1 -2 -3 -4\n" + + "@name2:uri2=value2\n" + + "@name3:uri3=value3\n" + + "}\n")); + + } + + public final void testAddGetAttribute() { + assertNotNull(e.getAttributes()); + assertArrayEquals( + new SimpleAttribute[] {}, + e.getAttributes()); + + e.addAttribute(new SimpleAttribute("uri", "name", "value")); + assertArrayEquals( + new SimpleAttribute[] { new SimpleAttribute("uri", "name", "value") }, + e.getAttributes()); + + e.addAttribute(new SimpleAttribute("my-uri", "second-name", "value")); + assertArrayEquals( + new SimpleAttribute[] { new SimpleAttribute("uri", "name", "value"), + new SimpleAttribute("my-uri", "second-name", "value") }, + e.getAttributes()); + + assertNull(e.getAttribute("unknown uri", "name")); + assertNull(e.getAttribute("uri", "unknown name")); + assertEquals(new SimpleAttribute("uri", "name", "value"), + e.getAttribute("uri", "name")); + assertEquals(new SimpleAttribute("my-uri", "second-name", "value"), + e.getAttribute("my-uri", "second-name")); + } + + public final void testAddGetInnerElements() { + assertNotNull(e.getInnerElements()); + assertArrayEquals( + new SimpleElement[] {}, + e.getInnerElements()); + + e.addInnerElement(new SimpleElement("android.view.Button", null, null, null)); + assertArrayEquals( + new SimpleElement[] { new SimpleElement("android.view.Button", null, null, null) }, + e.getInnerElements()); + + e.addInnerElement(new SimpleElement("android.view.CheckBox", null, null, null)); + assertArrayEquals( + new SimpleElement[] { new SimpleElement("android.view.Button", null, null, null), + new SimpleElement("android.view.CheckBox", null, null, null) }, + e.getInnerElements()); + } + + public final void testEqualsObject() { + assertFalse(e.equals(null)); + assertFalse(e.equals(new Object())); + + assertNotSame(new SimpleElement("android.view.LinearLayout", + "android.view.FrameLayout", + new Rect(10, 5, 60, 40), + new Rect(0, 0, 320, 480)), + e); + assertEquals(new SimpleElement("android.view.LinearLayout", + "android.view.FrameLayout", + new Rect(10, 5, 60, 40), + new Rect(0, 0, 320, 480)), + e); + assertTrue(e.equals(new SimpleElement("android.view.LinearLayout", + "android.view.FrameLayout", + new Rect(10, 5, 60, 40), + new Rect(0, 0, 320, 480)))); + + // not the same FQCN + assertFalse(e.equals(new SimpleElement("android.view.Button", + "android.view.FrameLayout", + new Rect(10, 5, 60, 40), + new Rect(0, 0, 320, 480)))); + + // not the same parent + assertFalse(e.equals(new SimpleElement("android.view.LinearLayout", + "android.view.LinearLayout", + new Rect(10, 5, 60, 40), + new Rect(0, 0, 320, 480)))); + + // not the same bounds + assertFalse(e.equals(new SimpleElement("android.view.LinearLayout", + "android.view.FrameLayout", + new Rect(10, 25, 30, 40), + new Rect(0, 0, 320, 480)))); + + // not the same parent bounds + assertFalse(e.equals(new SimpleElement("android.view.LinearLayout", + "android.view.FrameLayout", + new Rect(10, 5, 60, 40), + new Rect(10, 100, 160, 240)))); + } + + public final void testHashCode() { + int he = e.hashCode(); + + assertEquals(he, new SimpleElement("android.view.LinearLayout", + "android.view.FrameLayout", + new Rect(10, 5, 60, 40), + new Rect(0, 0, 320, 480)).hashCode()); + + + // not the same FQCN + assertFalse(he == new SimpleElement("android.view.Button", + "android.view.FrameLayout", + new Rect(10, 5, 60, 40), + new Rect(0, 0, 320, 480)).hashCode()); + + // not the same parent + assertFalse(he == new SimpleElement("android.view.LinearLayout", + "android.view.Button", + new Rect(10, 5, 60, 40), + new Rect(0, 0, 320, 480)).hashCode()); + + // not the same bounds + assertFalse(he == new SimpleElement("android.view.LinearLayout", + "android.view.FrameLayout", + new Rect(10, 25, 30, 40), + new Rect(0, 0, 320, 480)).hashCode()); + + // not the same parent bounds + assertFalse(he == new SimpleElement("android.view.LinearLayout", + "android.view.FrameLayout", + new Rect(10, 25, 30, 40), + new Rect(10, 100, 160, 240)).hashCode()); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java new file mode 100644 index 000000000..de7999c24 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java @@ -0,0 +1,343 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import com.android.ide.common.api.Rect; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.PaletteData; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +public class SwtUtilsTest extends TestCase { + + public void testImageConvertNoAlpha() throws Exception { + // Note: We need an TYPE_INT_ARGB SWT image here (instead of TYPE_INT_ARGB_PRE) to + // prevent the alpha from being pre-multiplied into the RGB when drawing the image. + BufferedImage inImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); + Graphics g = inImage.getGraphics(); + g.setColor(new Color(0xAA112233, true)); + g.fillRect(0, 0, inImage.getWidth(), inImage.getHeight()); + g.dispose(); + + Shell shell = new Shell(); + Display display = shell.getDisplay(); + + // Convert the RGB image, effectively discarding the alpha channel entirely. + Image outImage = SwtUtils.convertToSwt(display, inImage, false, -1); + assertNotNull(outImage); + + ImageData outData = outImage.getImageData(); + assertEquals(inImage.getWidth(), outData.width); + assertEquals(inImage.getHeight(), outData.height); + assertNull(outData.alphaData); + assertEquals(SWT.TRANSPARENCY_NONE, outData.getTransparencyType()); + + PaletteData inPalette = SwtUtils.getAwtPaletteData(inImage.getType()); + PaletteData outPalette = outData.palette; + + for (int y = 0; y < outData.height; y++) { + for (int x = 0; x < outData.width; x++) { + // Note: we can't compare pixel directly as integers since convertToSwt() might + // have changed the RGBA ordering depending on the platform (e.g. it will on + // Windows.) + RGB expected = inPalette.getRGB( inImage.getRGB( x, y)); + RGB actual = outPalette.getRGB(outData.getPixel(x, y)); + assertEquals(expected, actual); + } + } + + // Convert back to AWT and compare with original AWT image + BufferedImage awtImage = SwtUtils.convertToAwt(outImage); + assertNotNull(awtImage); + + // Both image have the same RGBA ordering + assertEquals(BufferedImage.TYPE_INT_ARGB, inImage.getType()); + assertEquals(BufferedImage.TYPE_INT_ARGB, awtImage.getType()); + + int awtAlphaMask = 0xFF000000; + + for (int y = 0; y < outData.height; y++) { + for (int x = 0; x < outData.width; x++) { + // Note: we can compare pixels as integers since we just + // asserted both images have the same color image type except + // for the content of the alpha channel. + int actual = awtImage.getRGB(x, y); + assertEquals(awtAlphaMask, actual & awtAlphaMask); + assertEquals(awtAlphaMask | inImage.getRGB(x, y), awtImage.getRGB(x, y)); + } + } + } + + public void testImageConvertGlobalAlpha() throws Exception { + BufferedImage inImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); + Graphics g = inImage.getGraphics(); + g.setColor(new Color(0xAA112233, true)); + g.fillRect(0, 0, inImage.getWidth(), inImage.getHeight()); + g.dispose(); + + Shell shell = new Shell(); + Display display = shell.getDisplay(); + + Image outImage = SwtUtils.convertToSwt(display, inImage, false, 128); + assertNotNull(outImage); + + ImageData outData = outImage.getImageData(); + assertEquals(inImage.getWidth(), outData.width); + assertEquals(inImage.getHeight(), outData.height); + assertEquals(128, outData.alpha); + assertEquals(SWT.TRANSPARENCY_NONE, outData.getTransparencyType()); + assertNull(outData.alphaData); + + PaletteData inPalette = SwtUtils.getAwtPaletteData(inImage.getType()); + PaletteData outPalette = outData.palette; + + for (int y = 0; y < outData.height; y++) { + for (int x = 0; x < outData.width; x++) { + + RGB expected = inPalette.getRGB( inImage.getRGB( x, y)); + RGB actual = outPalette.getRGB(outData.getPixel(x, y)); + assertEquals(expected, actual); + } + } + } + + public void testImageConvertAlpha() throws Exception { + BufferedImage inImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); + Graphics g = inImage.getGraphics(); + g.setColor(new Color(0xAA112233, true)); + g.fillRect(0, 0, inImage.getWidth(), inImage.getHeight()); + g.dispose(); + + Shell shell = new Shell(); + Display display = shell.getDisplay(); + + Image outImage = SwtUtils.convertToSwt(display, inImage, true, -1); + assertNotNull(outImage); + + ImageData outData = outImage.getImageData(); + assertEquals(inImage.getWidth(), outData.width); + assertEquals(inImage.getHeight(), outData.height); + assertEquals(SWT.TRANSPARENCY_ALPHA, outData.getTransparencyType()); + + PaletteData inPalette = SwtUtils.getAwtPaletteData(inImage.getType()); + PaletteData outPalette = outData.palette; + + for (int y = 0; y < outData.height; y++) { + for (int x = 0; x < outData.width; x++) { + RGB expected = inPalette.getRGB( inImage.getRGB( x, y)); + RGB actual = outPalette.getRGB(outData.getPixel(x, y)); + assertEquals(expected, actual); + + // Note: >> instead of >>> since we will compare with byte (a signed number) + int expectedAlpha = inImage.getRGB(x, y) >> 24; + int actualAlpha = outData.alphaData[y * outData.width + x]; + assertEquals(expectedAlpha, actualAlpha); + } + } + } + + public void testImageConvertAlphaMultiplied() throws Exception { + BufferedImage inImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); + Graphics g = inImage.getGraphics(); + g.setColor(new Color(0xAA112233, true)); + g.fillRect(0, 0, inImage.getWidth(), inImage.getHeight()); + g.dispose(); + + Shell shell = new Shell(); + Display display = shell.getDisplay(); + Image outImage = SwtUtils.convertToSwt(display, inImage, true, 32); + assertNotNull(outImage); + + // Expected alpha is 0xAA from the AWT input image pre-multiplied by 32 in convertToSwt. + int expectedAlpha = (0xAA * 32) >> 8; + + ImageData outData = outImage.getImageData(); + assertEquals(inImage.getWidth(), outData.width); + assertEquals(inImage.getHeight(), outData.height); + assertEquals(SWT.TRANSPARENCY_ALPHA, outData.getTransparencyType()); + + PaletteData inPalette = SwtUtils.getAwtPaletteData(inImage.getType()); + PaletteData outPalette = outData.palette; + + for (int y = 0; y < outData.height; y++) { + for (int x = 0; x < outData.width; x++) { + RGB expected = inPalette.getRGB( inImage.getRGB( x, y)); + RGB actual = outPalette.getRGB(outData.getPixel(x, y)); + assertEquals(expected, actual); + + byte actualAlpha = outData.alphaData[y * outData.width + x]; + assertEquals(expectedAlpha, actualAlpha); + } + } + } + + public final void testSetRectangle() { + Rect r = new Rect(1, 2, 3, 4); + Rectangle r2 = new Rectangle(3, 4, 20, 30); + SwtUtils.set(r, r2); + + assertEquals(3, r.x); + assertEquals(4, r.y); + assertEquals(20, r.w); + assertEquals(30, r.h); + } + + public final void testRectRectangle() { + Rectangle r = new Rectangle(3, 4, 20, 30); + Rect r2 = SwtUtils.toRect(r); + + assertEquals(3, r2.x); + assertEquals(4, r2.y); + assertEquals(20, r2.w); + assertEquals(30, r2.h); + } + + public final void testCropEmpty() { + Image image = createSampleImage(256, 256); + Image result = SwtUtils.drawRectangles(image, Collections.<Rectangle> emptyList(), null, + 1.0, (byte) 121); + assertNull(result); + } + + public final void testCrop1() { + Image image = createSampleImage(256, 256); + + byte alpha = 121; + double scale = 1.0; + + List<Rectangle> items = new ArrayList<Rectangle>(); + items.add(new Rectangle(30, 60, 20, 20)); + Image result = + SwtUtils.drawRectangles(image, items, ImageUtils.getBoundingRectangle(items), + scale, alpha); + + assertNotNull(result); + ImageData outData = result.getImageData(); + assertEquals(20, outData.width); + assertEquals(20, outData.height); + + PaletteData outPalette = outData.palette; + assertNotNull(outPalette); + + byte[] outAlphaData = outData.alphaData; + assertNotNull(outAlphaData); + + for (int y = 0; y < 20; y++) { + for (int x = 0; x < 20; x++) { + int r = y + 60; + int g = x + 30; + + RGB expected = new RGB(r, g, 0); + RGB actual = outPalette.getRGB(outData.getPixel(x, y)); + assertEquals(expected, actual); + + byte actualAlpha = outAlphaData[y*20+x]; + assertEquals(alpha, actualAlpha); + } + } + } + + public final void testCrop2() { + Image image = createSampleImage(256, 256); + + byte alpha = 121; + double scale = 1.0; + + List<Rectangle> items = new ArrayList<Rectangle>(); + items.add(new Rectangle(10, 10, 20, 20)); + items.add(new Rectangle(110, 80, 20, 20)); + Image result = + SwtUtils.drawRectangles(image, items, ImageUtils.getBoundingRectangle(items), + scale, alpha); + + assertNotNull(result); + ImageData outData = result.getImageData(); + assertEquals(120, outData.width); + assertEquals(90, outData.height); + + PaletteData outPalette = outData.palette; + assertNotNull(outPalette); + + byte[] outAlphaData = outData.alphaData; + assertNotNull(outAlphaData); + + for (int y = 0; y < 20; y++) { + for (int x = 0; x < 20; x++) { + int r = y + 10; + int g = x + 10; + + RGB expected = new RGB(r, g, 0); + RGB actual = outPalette.getRGB(outData.getPixel(x, y)); + assertEquals(expected, actual); + + assertEquals(alpha, outAlphaData[y*120+x]); + } + } + for (int y = 70; y < 90; y++) { + for (int x = 100; x < 120; x++) { + int r = y + 10; + int g = x + 10; + + RGB expected = new RGB(r, g, 0); + RGB actual = outPalette.getRGB(outData.getPixel(x, y)); + assertEquals(expected, actual); + + assertEquals(alpha, outAlphaData[y*120+x]); + } + } + assertEquals(0, outAlphaData[40]); + } + + /** + * Crop test utility: Create a sample image patterned with red=y and green=x, suitable + * for checking that in image copying operations we've copied and scaled the right + * bits. (Obviously the red/green will be mod'ed with 256). + * + * @param imageWidth the width of the target image + * @param imageHeight the height of the target image + * @return a new image with a red/green pattern + */ + private Image createSampleImage(int imageWidth, int imageHeight) { + Shell shell = new Shell(); + Display display = shell.getDisplay(); + + ImageData data = new ImageData(imageWidth, imageHeight, 32, new PaletteData(0x00FF0000, + 0x0000FF00, 0x000000FF)); + for (int y = 0; y < imageHeight; y++) { + for (int x = 0; x < imageWidth; x++) { + int pixelValue = (y & 0xFF) << 16 | (x & 0xFF) << 8; + data.setPixel(x, y, pixelValue); + } + } + Image image = new Image(display, data); + return image; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/MockNodeProxy.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/MockNodeProxy.java new file mode 100755 index 000000000..433d3903d --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/MockNodeProxy.java @@ -0,0 +1,68 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gre; + +import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; + +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.ui.internal.registry.ViewDescriptor; + +/** + * Mocks a {@link NodeProxy}, creating it using an XML local name and generating + * a made-up {@link UiViewElementNode} and a {@link ViewDescriptor} on the fly. + */ +public class MockNodeProxy extends NodeProxy { + + /** + * Generates a {@link NodeProxy} using an FQCN (e.g. android.view.View) + * and making the last segment of the FQCN the XML name of the view (e.g. "View") + * and wraps it as a {@link NodeProxy}. + * + * @param fqcn The fully qualified name of the class to wrap, e.g. "android.view.Button". + * @param bounds The bounds of a the view in the canvas. Must be either: <br/> + * - a valid rect for a view that is actually in the canvas <br/> + * - <b>*or*</b> null (or an invalid rect) for a view that has just been added dynamically + * to the model. We never store a null bounds rectangle in the node, a null rectangle + * will be converted to an invalid rectangle. + * @param factory A {@link NodeFactory} to create unique children nodes. + */ + public MockNodeProxy(String fqcn, Rectangle bounds, NodeFactory factory) { + super(makeUiViewNode(fqcn), bounds, factory); + } + + /** + * Generates a {@link ViewElementDescriptor} using an FQCN (e.g. android.view.View) + * and making the last segment of the FQCN the XML name of the view (e.g. "View"). + * + * @param fqcn The fully qualified name of the class to wrap, e.g. "android.view.Button" + * @return A new view element node with a new descriptor for the FQCN and an XML name + * matching the last FQCN segment (e.g. "Button") + */ + private static UiViewElementNode makeUiViewNode(String fqcn) { + String xmlName = fqcn; + int pos = xmlName.lastIndexOf('.'); + if (pos > 0) { + xmlName = xmlName.substring(pos + 1); + } + + ViewElementDescriptor desc = new ViewElementDescriptor(xmlName, fqcn); + UiViewElementNode uiNode = new UiViewElementNode(desc); + return uiNode; + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java new file mode 100755 index 000000000..0e6d33db3 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.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.eclipse.adt.internal.editors.layout.gre; + +import com.android.ide.common.api.INode; +import com.android.ide.common.api.Rect; +import com.android.ide.common.rendering.api.ViewInfo; +import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; +import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; + +import java.util.Arrays; + +import junit.framework.TestCase; + +public class NodeFactoryTest extends TestCase { + + private NodeFactory m; + + @Override + protected void setUp() throws Exception { + super.setUp(); + m = new NodeFactory(null); + + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + m = null; + } + + public final void testCreateCanvasViewInfo() { + ViewElementDescriptor ved = new ViewElementDescriptor("xml", "com.example.MyJavaClass"); + UiViewElementNode uiv = new UiViewElementNode(ved); + ViewInfo lvi = new ViewInfo("name", uiv, 10, 12, 110, 120); + CanvasViewInfo cvi = CanvasViewInfo.create(lvi, true /* layoutlib5 */).getFirst(); + + // Create a NodeProxy. + NodeProxy proxy = m.create(cvi); + + // getNode() is our only internal implementation method. + assertNotNull(proxy); + assertSame(uiv, proxy.getNode()); + + // Groovy scripts only see the INode interface so we want to primarily test that. + INode inode = proxy; + assertEquals(new Rect(10, 12, 110-10-1, 120-12-1), inode.getBounds()); + assertTrue(Arrays.equals(new INode[0], inode.getChildren())); + assertEquals("com.example.MyJavaClass", inode.getFqcn()); + assertNull(inode.getParent()); + assertSame(inode, inode.getRoot()); + + } + + public final void testCreateUiViewElementNode() { + ViewElementDescriptor ved = new ViewElementDescriptor("xml", "com.example.MyJavaClass"); + UiViewElementNode uiv = new UiViewElementNode(ved); + + // Create a NodeProxy. + NodeProxy proxy = m.create(uiv); + + // getNode() is our only internal implementation method. + assertNotNull(proxy); + assertSame(uiv, proxy.getNode()); + + // Groovy scripts only see the INode interface so we want to primarily test that. + INode inode = proxy; + // Nodes constructed using this create() method do not have valid bounds. + // There should be one invalid bound rectangle. + assertNotNull(inode.getBounds()); + assertFalse(inode.getBounds().isValid()); + // All the other properties should be set correctly. + assertTrue(Arrays.equals(new INode[0], inode.getChildren())); + assertEquals("com.example.MyJavaClass", inode.getFqcn()); + assertNull(inode.getParent()); + assertSame(inode, inode.getRoot()); + } + + public final void testCreateDup() { + ViewElementDescriptor ved = new ViewElementDescriptor("xml", "com.example.MyJavaClass"); + UiViewElementNode uiv = new UiViewElementNode(ved); + ViewInfo lvi = new ViewInfo("name", uiv, 10, 12, 110, 120); + CanvasViewInfo cvi = CanvasViewInfo.create(lvi, true /* layoutlib5 */).getFirst(); + + // NodeProxies are cached. Creating the same one twice returns the same proxy. + NodeProxy proxy1 = m.create(cvi); + NodeProxy proxy2 = m.create(cvi); + assertSame(proxy2, proxy1); + } + + public final void testClear() { + ViewElementDescriptor ved = new ViewElementDescriptor("xml", "com.example.MyJavaClass"); + UiViewElementNode uiv = new UiViewElementNode(ved); + ViewInfo lvi = new ViewInfo("name", uiv, 10, 12, 110, 120); + CanvasViewInfo cvi = CanvasViewInfo.create(lvi, true /* layoutlib5 */).getFirst(); + + // NodeProxies are cached. Creating the same one twice returns the same proxy. + NodeProxy proxy1 = m.create(cvi); + NodeProxy proxy2 = m.create(cvi); + assertSame(proxy2, proxy1); + + // Clearing the cache will force it create a new proxy. + m.clear(); + NodeProxy proxy3 = m.create(cvi); + assertNotSame(proxy1, proxy3); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngineTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngineTest.java new file mode 100755 index 000000000..d9a1a3dc1 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngineTest.java @@ -0,0 +1,37 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gre; + +import junit.framework.TestCase; + +public class RulesEngineTest extends TestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testDummy() { + // Here to avoid warning that RulesEngineTest is empty + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java new file mode 100644 index 000000000..50d438cdb --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java @@ -0,0 +1,77 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gre; + +import com.android.ide.common.api.IViewMetadata.FillPreference; +import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository.RenderMode; + +import java.util.Arrays; + +import junit.framework.TestCase; + +public class ViewMetadataRepositoryTest extends TestCase { + public void testSingleton() throws Exception { + assertSame(ViewMetadataRepository.get(), ViewMetadataRepository.get()); + } + + public void testBasic() throws Exception { + ViewMetadataRepository repository = ViewMetadataRepository.get(); + + assertEquals(FillPreference.WIDTH_IN_VERTICAL, + repository.getFillPreference("android.widget.Spinner")); + assertEquals(FillPreference.NONE, + repository.getFillPreference("foo.bar")); + } + + // Ensure that all basenames referenced in the metadata refer to other views in the file + // (e.g. no typos) + public void testRelatedTo() throws Exception { + // Make sure unit tests are run with assertions on + boolean assertionsEnabled = false; + assert assertionsEnabled = true; // Intentional assignment + assertTrue("This unit test must be run with assertions enabled (-ea)", assertionsEnabled); + + ViewMetadataRepository repository = ViewMetadataRepository.get(); + for (String fqcn : repository.getAllFqcns()) { + repository.getRelatedTo(fqcn); + } + } + + public void testSkip() throws Exception { + ViewMetadataRepository repository = ViewMetadataRepository.get(); + assertTrue(repository.getSkip("merge")); + assertFalse(repository.getSkip("android.widget.Button")); + } + + public void testRenderMode() throws Exception { + ViewMetadataRepository repository = ViewMetadataRepository.get(); + assertEquals(RenderMode.NORMAL, repository.getRenderMode("android.widget.Button")); + assertEquals(RenderMode.SKIP, repository.getRenderMode("android.widget.LinearLayout")); + assertEquals(RenderMode.ALONE, repository.getRenderMode("android.widget.TabHost")); + } + + public void testGetTopAttributes() throws Exception { + ViewMetadataRepository repository = ViewMetadataRepository.get(); + assertEquals(Arrays.asList("id", "text", "style"), + repository.getTopAttributes("android.widget.RadioButton")); + assertEquals(Arrays.asList("id", "gravity", "paddingLeft", "paddingRight", "checkMark", + "textAppearance"), + repository.getTopAttributes("android.widget.CheckedTextView")); + assertEquals(Arrays.asList("id"), + repository.getTopAttributes("android.widget.NonExistent")); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/properties/ValueCompleterTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/properties/ValueCompleterTest.java new file mode 100644 index 000000000..0014fe574 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/properties/ValueCompleterTest.java @@ -0,0 +1,151 @@ +/* + * 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.eclipse.adt.internal.editors.layout.properties; + +import static com.android.SdkConstants.ANDROID_URI; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.ide.common.api.IAttributeInfo; +import com.android.ide.common.api.IAttributeInfo.Format; +import com.android.ide.common.layout.TestAttributeInfo; +import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor; + +import org.eclipse.jface.fieldassist.IContentProposal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class ValueCompleterTest extends TestCase { + private void checkCompletion(String text, int offset, + String property, EnumSet<Format> formats, String[] values, + List<String> expected) { + assertTrue(text.length() >= offset); + + TestAttributeInfo info = + new TestAttributeInfo(property, formats, "unittest", values, values, null); + TestValueCompleter completer = new TestValueCompleter( + new TestTextAttributeDescriptor(property, info)); + IContentProposal[] proposals = completer.getProposals(text, offset); + List<String> actual = new ArrayList<String>(); + for (IContentProposal proposal : proposals) { + String content = proposal.getContent(); + actual.add(content); + } + assertEquals(expected.toString(), actual.toString()); + } + + public void test() throws Exception { + checkCompletion("@android:string", 3, "text", + EnumSet.of(Format.REFERENCE), null, + Arrays.asList(new String[] { })); + } + + public void test1a() throws Exception { + checkCompletion("matc", 4, "layout_width", + EnumSet.of(Format.DIMENSION, Format.ENUM), + new String[] { "fill_parent", "match_parent", "wrap_content" }, + + Arrays.asList(new String[] { "match_parent", "fill_parent", "wrap_content" })); + } + + public void test1b() throws Exception { + checkCompletion("fi", 2, "layout_width", + EnumSet.of(Format.DIMENSION, Format.ENUM), + new String[] { "fill_parent", "match_parent", "wrap_content" }, + + Arrays.asList(new String[] { "fill_parent", "match_parent", "wrap_content" })); + } + + public void test2() throws Exception { + checkCompletion("50", 2, "layout_width", + EnumSet.of(Format.DIMENSION, Format.ENUM), + new String[] { "fill_parent", "match_parent", "wrap_content" }, + + Arrays.asList(new String[] { "50dp", "fill_parent", "match_parent", + "wrap_content" })); + } + + public void test3() throws Exception { + checkCompletion("42", 2, "textSize", + EnumSet.of(Format.DIMENSION), + null, + + Arrays.asList(new String[] { "42sp", "42dp" })); + } + + public void test4() throws Exception { + checkCompletion("", 0, "gravity", + EnumSet.of(Format.FLAG), + new String[] { "top", "bottom", "left", "right", "center" }, + + Arrays.asList(new String[] { "top", "bottom", "left", "right", "center" })); + } + + public void test5() throws Exception { + checkCompletion("left", 4, "gravity", + EnumSet.of(Format.FLAG), + new String[] { "top", "bottom", "left", "right", "center" }, + + Arrays.asList(new String[] { + "left", "left|top", "left|bottom", "left|right", "left|center" })); + } + + public void test6() throws Exception { + checkCompletion("left|top", 8, "gravity", + EnumSet.of(Format.FLAG), + new String[] { "top", "bottom", "left", "right", "center" }, + + Arrays.asList(new String[] { + "left|top", "left|top|bottom", "left|top|right", "left|top|center" })); + } + + // TODO ?android + + private class TestTextAttributeDescriptor extends TextAttributeDescriptor { + public TestTextAttributeDescriptor(String xmlLocalName, IAttributeInfo attrInfo) { + super(xmlLocalName, ANDROID_URI, attrInfo); + } + } + + private class TestValueCompleter extends ValueCompleter { + private final AttributeDescriptor mDescriptor; + + TestValueCompleter(AttributeDescriptor descriptor) { + mDescriptor = descriptor; + assert descriptor.getAttributeInfo() != null; + } + + @Override + @Nullable + protected CommonXmlEditor getEditor() { + return null; + } + + @Override + @NonNull + protected AttributeDescriptor getDescriptor() { + return mDescriptor; + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java new file mode 100644 index 000000000..d19f933d0 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2007 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.eclipse.adt.internal.editors.manifest.model; + +import com.android.SdkConstants; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor.Mandatory; +import com.android.ide.eclipse.adt.internal.editors.mock.MockXmlNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.utils.XmlUtils; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import java.util.Iterator; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class UiElementNodeTest extends TestCase { + + private UiElementNode ui; + private ElementDescriptor mManifestDesc; + private ElementDescriptor mAppDesc; + private ElementDescriptor mUsesSdkDesc; + + @Override + protected void setUp() throws Exception { + mAppDesc = new ElementDescriptor("application", new ElementDescriptor[] { + new ElementDescriptor("provider"), + new ElementDescriptor("activity", new ElementDescriptor[] { + new ElementDescriptor("intent-filter") + }), + }, Mandatory.MANDATORY_LAST); + + mUsesSdkDesc = new ElementDescriptor("uses-sdk", new ElementDescriptor[] {}, + Mandatory.MANDATORY); + + mManifestDesc = new ElementDescriptor("manifest", new ElementDescriptor[] { + mAppDesc, + mUsesSdkDesc, + new ElementDescriptor("permission") + }, Mandatory.MANDATORY); + + ui = new UiElementNode(mManifestDesc); + + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + // pass + } + + /** + * Check initialization values for ui node + */ + public void testInit() { + assertSame(mManifestDesc, ui.getDescriptor()); + assertNull(ui.getUiParent()); + assertEquals(0, ui.getUiChildren().size()); + assertEquals(0, ui.getAllUiAttributes().size()); + } + + /** + * We declared the descriptors as having a "mandatory last" application element + * and a mandatory non-last uses-sdk element. This means if we create an empty + * UiModel, we should get these two created, with the application element after + * the uses-sdk. + */ + public void testMandatoryOrder() { + // Add the mandatory nodes with no XML backing, do it explicitly in the wrong order. + assertEquals(0, ui.getUiChildren().size()); + ui.appendNewUiChild(mAppDesc); + ui.appendNewUiChild(mUsesSdkDesc); + + assertEquals(2, ui.getUiChildren().size()); + assertSame(mAppDesc, ui.getUiChildren().get(0).getDescriptor()); + assertSame(mUsesSdkDesc, ui.getUiChildren().get(1).getDescriptor()); + + // Parse an XML with just a manifest. + MockXmlNode root = new MockXmlNode(null /* namespace */, "manifest", Node.ELEMENT_NODE, + new MockXmlNode[] { + new MockXmlNode(null /* namespace */, "application", Node.ELEMENT_NODE, null) + }); + + ui.loadFromXmlNode(root); + + // We should get 2 children, the 2 mandatory nodes but this time with uses-sdk + // before application since it's a mandatory-last so it "moves" to the end if possible. + assertEquals(2, ui.getUiChildren().size()); + assertSame(mUsesSdkDesc, ui.getUiChildren().get(0).getDescriptor()); + assertNull(ui.getUiChildren().get(0).getXmlNode()); + assertSame(mAppDesc, ui.getUiChildren().get(1).getDescriptor()); + assertNotNull(ui.getUiChildren().get(1).getXmlNode()); + } + + /** + * loadFrom() does nothing if the root node doesn't match what's expected + */ + public void testLoadFrom_InvalidRoot() { + assertEquals(0, ui.getUiChildren().size()); + MockXmlNode root = new MockXmlNode(null /* namespace */, "blah", Node.ELEMENT_NODE, null); + ui.loadFromXmlNode(root); + assertEquals(0, ui.getUiChildren().size()); + } + + /** + * UiElementNode.loadFrom should be used to populate an empty ui node from an + * existing XML node tree. + */ + public void testLoadFrom_NewTree_1_Node() { + MockXmlNode root = new MockXmlNode(null /* namespace */, "manifest", Node.ELEMENT_NODE, + new MockXmlNode[] { + new MockXmlNode(null /* namespace */, "application", Node.ELEMENT_NODE, null) + }); + + // get /manifest + ui.loadFromXmlNode(root); + assertEquals("manifest", ui.getDescriptor().getXmlName()); + assertEquals(1, ui.getUiChildren().size()); + assertEquals(0, ui.getAllUiAttributes().size()); + + // get /manifest/application + Iterator<UiElementNode> ui_child_it = ui.getUiChildren().iterator(); + UiElementNode application = ui_child_it.next(); + assertEquals("application", application.getDescriptor().getXmlName()); + assertEquals(0, application.getUiChildren().size()); + assertEquals(0, application.getAllUiAttributes().size()); + } + + + public void testLoadFrom_NewTree_2_Nodes() { + MockXmlNode root = new MockXmlNode(null /* namespace */, "manifest", Node.ELEMENT_NODE, + new MockXmlNode[] { + new MockXmlNode(null /* namespace */, "application", Node.ELEMENT_NODE, null), + new MockXmlNode(null /* namespace */, "permission", Node.ELEMENT_NODE, null), + }); + + // get /manifest + ui.loadFromXmlNode(root); + assertEquals("manifest", ui.getDescriptor().getXmlName()); + assertEquals(2, ui.getUiChildren().size()); + assertEquals(0, ui.getAllUiAttributes().size()); + + // get /manifest/application + Iterator<UiElementNode> ui_child_it = ui.getUiChildren().iterator(); + UiElementNode application = ui_child_it.next(); + assertEquals("application", application.getDescriptor().getXmlName()); + assertEquals(0, application.getUiChildren().size()); + assertEquals(0, application.getAllUiAttributes().size()); + assertEquals(0, application.getUiSiblingIndex()); + + // get /manifest/permission + UiElementNode first_permission = ui_child_it.next(); + assertEquals("permission", first_permission.getDescriptor().getXmlName()); + assertEquals(0, first_permission.getUiChildren().size()); + assertEquals(0, first_permission.getAllUiAttributes().size()); + assertEquals(1, first_permission.getUiSiblingIndex()); + } + + public void testLoadFrom_NewTree_N_Nodes() { + MockXmlNode root = new MockXmlNode(null /* namespace */, "manifest", Node.ELEMENT_NODE, + new MockXmlNode[] { + new MockXmlNode(null /* namespace */, "application", Node.ELEMENT_NODE, + new MockXmlNode[] { + new MockXmlNode(null /* namespace */, "activity", Node.ELEMENT_NODE, + null), + new MockXmlNode(null /* namespace */, "activity", Node.ELEMENT_NODE, + new MockXmlNode[] { + new MockXmlNode(null /* namespace */, "intent-filter", + Node.ELEMENT_NODE, null), + }), + new MockXmlNode(null /* namespace */, "provider", Node.ELEMENT_NODE, + null), + new MockXmlNode(null /* namespace */, "provider", Node.ELEMENT_NODE, + null), + }), + new MockXmlNode(null /* namespace */, "permission", Node.ELEMENT_NODE, + null), + new MockXmlNode(null /* namespace */, "permission", Node.ELEMENT_NODE, + null), + }); + + // get /manifest + ui.loadFromXmlNode(root); + assertEquals("manifest", ui.getDescriptor().getXmlName()); + assertEquals(3, ui.getUiChildren().size()); + assertEquals(0, ui.getAllUiAttributes().size()); + + // get /manifest/application + Iterator<UiElementNode> ui_child_it = ui.getUiChildren().iterator(); + UiElementNode application = ui_child_it.next(); + assertEquals("application", application.getDescriptor().getXmlName()); + assertEquals(4, application.getUiChildren().size()); + assertEquals(0, application.getAllUiAttributes().size()); + + // get /manifest/application/activity #1 + Iterator<UiElementNode> app_child_it = application.getUiChildren().iterator(); + UiElementNode first_activity = app_child_it.next(); + assertEquals("activity", first_activity.getDescriptor().getXmlName()); + assertEquals(0, first_activity.getUiChildren().size()); + assertEquals(0, first_activity.getAllUiAttributes().size()); + + // get /manifest/application/activity #2 + UiElementNode second_activity = app_child_it.next(); + assertEquals("activity", second_activity.getDescriptor().getXmlName()); + assertEquals(1, second_activity.getUiChildren().size()); + assertEquals(0, second_activity.getAllUiAttributes().size()); + + // get /manifest/application/activity #2/intent-filter #1 + Iterator<UiElementNode> activity_child_it = second_activity.getUiChildren().iterator(); + UiElementNode intent_filter = activity_child_it.next(); + assertEquals("intent-filter", intent_filter.getDescriptor().getXmlName()); + assertEquals(0, intent_filter.getUiChildren().size()); + assertEquals(0, intent_filter.getAllUiAttributes().size()); + + // get /manifest/application/provider #1 + UiElementNode first_provider = app_child_it.next(); + assertEquals("provider", first_provider.getDescriptor().getXmlName()); + assertEquals(0, first_provider.getUiChildren().size()); + assertEquals(0, first_provider.getAllUiAttributes().size()); + + // get /manifest/application/provider #2 + UiElementNode second_provider = app_child_it.next(); + assertEquals("provider", second_provider.getDescriptor().getXmlName()); + assertEquals(0, second_provider.getUiChildren().size()); + assertEquals(0, second_provider.getAllUiAttributes().size()); + + // get /manifest/permission #1 + UiElementNode first_permission = ui_child_it.next(); + assertEquals("permission", first_permission.getDescriptor().getXmlName()); + assertEquals(0, first_permission.getUiChildren().size()); + assertEquals(0, first_permission.getAllUiAttributes().size()); + + // get /manifest/permission #1 + UiElementNode second_permission = ui_child_it.next(); + assertEquals("permission", second_permission.getDescriptor().getXmlName()); + assertEquals(0, second_permission.getUiChildren().size()); + assertEquals(0, second_permission.getAllUiAttributes().size()); + } + + public void testCreateNameSpace() throws Exception { + // Setup + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setValidating(false); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.newDocument(); + Element rootElement = document.createElement("root"); + document.appendChild(rootElement); + Element root = document.getDocumentElement(); + root.appendChild(document.createTextNode(" ")); + Element foo = document.createElement("foo"); + root.appendChild(foo); + root.appendChild(document.createTextNode(" ")); + Element bar = document.createElement("bar"); + root.appendChild(bar); + Element baz = document.createElement("baz"); + root.appendChild(baz); + + String prefix = XmlUtils.lookupNamespacePrefix(baz, SdkConstants.ANDROID_URI); + assertEquals("android", prefix); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockNamedNodeMap.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockNamedNodeMap.java new file mode 100644 index 000000000..0330216d5 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockNamedNodeMap.java @@ -0,0 +1,112 @@ +/* + * 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.eclipse.adt.internal.editors.mock; + +import org.w3c.dom.DOMException; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import java.util.ArrayList; +import java.util.HashMap; + +class MockNamedNodeMap implements NamedNodeMap { + + /** map for access by namespace/name */ + private final HashMap<String, HashMap<String, Node>> mNodeMap = + new HashMap<String, HashMap<String, Node>>(); + + /** list for access by index */ + private final ArrayList<Node> mNodeList = new ArrayList<Node>(); + + public MockXmlNode addAttribute(String namespace, String localName, String value) { + MockXmlNode node = new MockXmlNode(namespace, localName, value); + + if (namespace == null) { + namespace = ""; // no namespace + } + + // get the map for the namespace + HashMap<String, Node> map = mNodeMap.get(namespace); + if (map == null) { + map = new HashMap<String, Node>(); + mNodeMap.put(namespace, map); + } + + + map.put(localName, node); + mNodeList.add(node); + + return node; + } + + // --------- NamedNodeMap ------- + + @Override + public int getLength() { + return mNodeList.size(); + } + + @Override + public Node getNamedItem(String name) { + HashMap<String, Node> map = mNodeMap.get(""); // no namespace + if (map != null) { + return map.get(name); + } + + return null; + } + + @Override + public Node getNamedItemNS(String namespaceURI, String localName) throws DOMException { + if (namespaceURI == null) { + namespaceURI = ""; //no namespace + } + + HashMap<String, Node> map = mNodeMap.get(namespaceURI); + if (map != null) { + return map.get(localName); + } + + return null; + } + + @Override + public Node item(int index) { + return mNodeList.get(index); + } + + @Override + public Node removeNamedItem(String name) throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public Node removeNamedItemNS(String namespaceURI, String localName) throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public Node setNamedItem(Node arg) throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public Node setNamedItemNS(Node arg) throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockNodeList.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockNodeList.java new file mode 100644 index 000000000..ff9df19a1 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockNodeList.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 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.eclipse.adt.internal.editors.mock; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.ArrayList; + + +/** + * A quick mock implementation of NodeList on top of ArrayList. + */ +public class MockNodeList implements NodeList { + + ArrayList<MockXmlNode> mChildren; + + /** + * Constructs a node list from a given children list. + * + * @param children The children list. Can be null. + */ + public MockNodeList(MockXmlNode[] children) { + mChildren = new ArrayList<MockXmlNode>(); + if (children != null) { + for (MockXmlNode n : children) { + mChildren.add(n); + } + } + } + + @Override + public int getLength() { + return mChildren.size(); + } + + @Override + public Node item(int index) { + if (index >= 0 && index < mChildren.size()) { + return mChildren.get(index); + } + return null; + } + + public ArrayList<MockXmlNode> getArrayList() { + return mChildren; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockXmlNode.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockXmlNode.java new file mode 100644 index 000000000..77de2c822 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockXmlNode.java @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2007 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.eclipse.adt.internal.editors.mock; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.UserDataHandler; + +import java.util.HashMap; + + +/** + * A mock XML node with only a minimal set of information. + */ +public class MockXmlNode implements Node { + + MockNodeList mNodeList; + private String mLocalName; + private String mNamespace; + private short mNodeType; + private MockXmlNode mParent; + private MockXmlNode mPreviousSibling; + private MockXmlNode mNextSibling; + private String mAttrValue; + private MockNamedNodeMap mAttributes; + + // namespace stuff only set in the root node + /** map from namespace to prefix. */ + private HashMap<String, String> mNsMap = null; + + /** + * Constructs a node from a given children list. + * + * @param namespace The namespace of the node or null if none + * @param localName The XML local node name. + * @param node_type One of Node.xxx_NODE constants, e.g. Node.ELEMENT_NODE + * @param children The children list. Can be null. + */ + public MockXmlNode(String namespace, String localName, short node_type, + MockXmlNode[] children) { + mLocalName = localName; + mNamespace = namespace; + mNodeType = node_type; + mNodeList = new MockNodeList(children); + fixNavigation(); + } + + /** + * Constructs an attribute node + * + * @param namespace The namespace of the node or null if none + * @param localName The XML local node name. + * @param value the value of the attribute + */ + public MockXmlNode(String namespace, String localName, String value) { + mLocalName = localName; + mNamespace = namespace; + mAttrValue = value; + mNodeType = Node.ATTRIBUTE_NODE; + mNodeList = new MockNodeList(new MockXmlNode[0]); + fixNavigation(); + } + + private void fixNavigation() { + MockXmlNode prev = null; + for (MockXmlNode n : mNodeList.getArrayList()) { + n.mParent = this; + n.mPreviousSibling = prev; + if (prev != null) { + prev.mNextSibling = n; + } + n.fixNavigation(); + prev = n; + } + } + + public void addAttributes(String namespaceURI, String localName, String value) { + if (mAttributes == null) { + mAttributes = new MockNamedNodeMap(); + } + + MockXmlNode node = mAttributes.addAttribute(namespaceURI, localName, value); + node.mParent = this; + } + + public void setPrefix(String namespace, String prefix) { + if (mNsMap == null) { + mNsMap = new HashMap<String, String>(); + } + + mNsMap.put(namespace, prefix); + } + + public String getPrefix(String namespace) { + if (mNsMap != null) { + return mNsMap.get(namespace); + } + + return mParent.getPrefix(namespace); + } + + + // ----------- Node methods + + @Override + public Node appendChild(Node newChild) throws DOMException { + mNodeList.getArrayList().add((MockXmlNode) newChild); + return newChild; + } + + @Override + public NamedNodeMap getAttributes() { + return mAttributes; + } + + @Override + public NodeList getChildNodes() { + return mNodeList; + } + + @Override + public Node getFirstChild() { + if (mNodeList.getLength() > 0) { + return mNodeList.item(0); + } + return null; + } + + @Override + public Node getLastChild() { + if (mNodeList.getLength() > 0) { + return mNodeList.item(mNodeList.getLength() - 1); + } + return null; + } + + @Override + public Node getNextSibling() { + return mNextSibling; + } + + @Override + public String getNodeName() { + return mLocalName; + } + + @Override + public String getLocalName() { + return mLocalName; + } + + @Override + public short getNodeType() { + return mNodeType; + } + + @Override + public Node getParentNode() { + return mParent; + } + + @Override + public Node getPreviousSibling() { + return mPreviousSibling; + } + + @Override + public boolean hasChildNodes() { + return mNodeList.getLength() > 0; + } + + @Override + public boolean hasAttributes() { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public boolean isSameNode(Node other) { + return this == other; + } + + @Override + public String getNodeValue() throws DOMException { + return mAttrValue; + } + + @Override + public String getPrefix() { + return getPrefix(getNamespaceURI()); + } + + @Override + public String getNamespaceURI() { + return mNamespace; + } + + + // --- methods not implemented --- + + @Override + public Node cloneNode(boolean deep) { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public short compareDocumentPosition(Node other) throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public String getBaseURI() { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public Object getFeature(String feature, String version) { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public Document getOwnerDocument() { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public String getTextContent() throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public Object getUserData(String key) { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public Node insertBefore(Node newChild, Node refChild) + throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public boolean isDefaultNamespace(String namespaceURI) { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public boolean isEqualNode(Node arg) { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public boolean isSupported(String feature, String version) { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public String lookupNamespaceURI(String prefix) { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public String lookupPrefix(String namespaceURI) { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public void normalize() { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public Node removeChild(Node oldChild) throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public Node replaceChild(Node newChild, Node oldChild) + throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public void setNodeValue(String nodeValue) throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public void setPrefix(String prefix) throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public void setTextContent(String textContent) throws DOMException { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } + + @Override + public Object setUserData(String key, Object data, + UserDataHandler handler) { + throw new UnsupportedOperationException("Operation not implemented."); //$NON-NLS-1$ + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java new file mode 100644 index 000000000..a6da135b6 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java @@ -0,0 +1,297 @@ +/* + * 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.eclipse.adt.internal.editors.resources.manager; + +import com.android.SdkConstants; +import com.android.ide.common.resources.ResourceFile; +import com.android.ide.common.resources.ResourceFolder; +import com.android.ide.common.resources.ResourceItem; +import com.android.ide.common.resources.ResourceRepository; +import com.android.ide.common.resources.SingleResourceFile; +import com.android.ide.common.resources.configuration.FolderConfiguration; +import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources; +import com.android.ide.eclipse.adt.io.IFileWrapper; +import com.android.ide.eclipse.adt.io.IFolderWrapper; +import com.android.ide.eclipse.mock.Mocks; +import com.android.io.IAbstractFolder; +import com.android.io.IAbstractResource; +import com.android.resources.Keyboard; +import com.android.resources.KeyboardState; +import com.android.resources.Navigation; +import com.android.resources.NavigationState; +import com.android.resources.NightMode; +import com.android.resources.ResourceFolderType; +import com.android.resources.ScreenOrientation; +import com.android.resources.TouchScreen; +import com.android.resources.UiMode; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; + +import junit.framework.TestCase; + +public class ConfigMatchTest extends TestCase { + private static final String SEARCHED_FILENAME = "main.xml"; //$NON-NLS-1$ + private static final String MISC1_FILENAME = "foo.xml"; //$NON-NLS-1$ + private static final String MISC2_FILENAME = "bar.xml"; //$NON-NLS-1$ + + private FolderConfiguration mDefaultConfig; + private ResourceRepository mResources; + private FolderConfiguration config4; + private FolderConfiguration config3; + private FolderConfiguration config2; + private FolderConfiguration config1; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + // create a default config with all qualifiers. + mDefaultConfig = new FolderConfiguration(); + mDefaultConfig.createDefault(); + + IAbstractFolder folder = Mocks.createAbstractFolder( + SdkConstants.FD_RESOURCES, new IAbstractResource[0]); + + // create the project resources. + mResources = new ResourceRepository(folder, false) { + @Override + protected ResourceItem createResourceItem(String name) { + return new ResourceItem(name); + } + }; + + // create 2 arrays of IResource. one with the filename being looked up, and one without. + // Since the required API uses IResource, we can use MockFolder for them. + IFile[] validMemberList = new IFile[] { + Mocks.createFile(MISC1_FILENAME), + Mocks.createFile(SEARCHED_FILENAME), + Mocks.createFile(MISC2_FILENAME), + }; + IFile[] invalidMemberList = new IFile[] { + Mocks.createFile(MISC1_FILENAME), + Mocks.createFile(MISC2_FILENAME), + }; + + // add multiple ResourceFolder to the project resource. + FolderConfiguration defaultConfig = getConfiguration( + null, // country code + null, // network code + null, // language + null, // region + null, // smallest width dp + null, // width dp + null, // height dp + null, // screen size + null, // screen ratio + null, // screen orientation + null, // dock mode + null, // night mode + null, // dpi + null, // touch mode + null, // keyboard state + null, // text input + null, // navigation state + null, // navigation method + null, // screen dimension + null);// version + + addFolder(mResources, defaultConfig, validMemberList); + + config1 = getConfiguration( + null, // country code + null, // network code + "en", // language + null, // region + null, // smallest width dp + null, // width dp + null, // height dp + null, // screen size + null, // screen ratio + null, // screen orientation + null, // dock mode + null, // night mode + null, // dpi + null, // touch mode + KeyboardState.EXPOSED.getResourceValue(), // keyboard state + null, // text input + null, // navigation state + null, // navigation method + null, // screen dimension + null);// version + + addFolder(mResources, config1, validMemberList); + + config2 = getConfiguration( + null, // country code + null, // network code + "en", // language + null, // region + null, // smallest width dp + null, // width dp + null, // height dp + null, // screen size + null, // screen ratio + null, // screen orientation + null, // dock mode + null, // night mode + null, // dpi + null, // touch mode + KeyboardState.HIDDEN.getResourceValue(), // keyboard state + null, // text input + null, // navigation state + null, // navigation method + null, // screen dimension + null);// version + + addFolder(mResources, config2, validMemberList); + + config3 = getConfiguration( + null, // country code + null, // network code + "en", // language + null, // region + null, // smallest width dp + null, // width dp + null, // height dp + null, // screen size + null, // screen ratio + ScreenOrientation.LANDSCAPE.getResourceValue(), // screen orientation + null, // dock mode + null, // night mode + null, // dpi + null, // touch mode + null, // keyboard state + null, // text input + null, // navigation state + null, // navigation method + null, // screen dimension + null);// version + + addFolder(mResources, config3, validMemberList); + + config4 = getConfiguration( + "mcc310", // country code + "mnc435", // network code + "en", // language + "rUS", // region + null, // smallest width dp + null, // width dp + null, // height dp + "normal", // screen size + "notlong", // screen ratio + ScreenOrientation.LANDSCAPE.getResourceValue(), // screen orientation + UiMode.DESK.getResourceValue(), // dock mode + NightMode.NIGHT.getResourceValue(), // night mode + "mdpi", // dpi + TouchScreen.FINGER.getResourceValue(), // touch mode + KeyboardState.EXPOSED.getResourceValue(), // keyboard state + Keyboard.QWERTY.getResourceValue(), // text input + NavigationState.EXPOSED.getResourceValue(), // navigation state + Navigation.DPAD.getResourceValue(), // navigation method + "480x320", // screen dimension + "v3"); // version + + addFolder(mResources, config4, invalidMemberList); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + mResources = null; + } + + public void test1() { + FolderConfiguration testConfig = getConfiguration( + "mcc310", // country code + "mnc435", // network code + "en", // language + "rUS", // region + null, // smallest width dp + null, // width dp + null, // height dp + "normal", // screen size + "notlong", // screen ratio + ScreenOrientation.LANDSCAPE.getResourceValue(), // screen orientation + UiMode.DESK.getResourceValue(), // dock mode + NightMode.NIGHT.getResourceValue(), // night mode + "mdpi", // dpi + TouchScreen.FINGER.getResourceValue(), // touch mode + KeyboardState.EXPOSED.getResourceValue(), // keyboard state + Keyboard.QWERTY.getResourceValue(), // text input + NavigationState.EXPOSED.getResourceValue(), // navigation state + Navigation.DPAD.getResourceValue(), // navigation method + "480x320", // screen dimension + "v3"); // version + + ResourceFile result = mResources.getMatchingFile(SEARCHED_FILENAME, + ResourceFolderType.LAYOUT, testConfig); + + boolean bresult = result.getFolder().getConfiguration().equals(config3); + assertEquals(bresult, true); + } + + /** + * Creates a {@link FolderConfiguration}. + * @param qualifierValues The list of qualifier values. The length must equals the total number + * of Qualifiers. <code>null</code> is permitted and will make the FolderConfiguration not use + * this particular qualifier. + */ + private FolderConfiguration getConfiguration(String... qualifierValues) { + // FolderConfiguration.getQualifierCount is always valid and up to date. + final int count = FolderConfiguration.getQualifierCount(); + + // Check we have the right number of qualifier. + assertEquals(qualifierValues.length, count); + + FolderConfiguration config = new FolderConfiguration(); + + for (int i = 0 ; i < count ; i++) { + String value = qualifierValues[i]; + if (value != null) { + assertTrue(mDefaultConfig.getQualifier(i).checkAndSet(value, config)); + } + } + + return config; + } + + /** + * Adds a folder to the given {@link ProjectResources} with the given + * {@link FolderConfiguration}. The folder is filled with files from the provided list. + * @param resources the {@link ResourceRepository} in which to add the folder. + * @param config the {@link FolderConfiguration} for the created folder. + * @param memberList the list of files for the folder. + */ + private void addFolder(ResourceRepository resources, FolderConfiguration config, + IFile[] memberList) throws Exception { + + // figure out the folder name based on the configuration + String folderName = config.getFolderName(ResourceFolderType.LAYOUT); + + // create the folder mock + IFolder folder = Mocks.createFolder(folderName, memberList); + + // add it to the resource, and get back a ResourceFolder object. + ResourceFolder resFolder = resources.processFolder(new IFolderWrapper(folder)); + + // and fill it with files from the list. + for (IFile file : memberList) { + resFolder.addFile(new SingleResourceFile(new IFileWrapper(file), resFolder)); + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/project/ProjectHelperTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/project/ProjectHelperTest.java new file mode 100644 index 000000000..22bfef560 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/project/ProjectHelperTest.java @@ -0,0 +1,66 @@ +/* + * 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.eclipse.adt.internal.project; + +import com.android.ide.eclipse.mock.Mocks; + +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaProject; + +import junit.framework.TestCase; + +public class ProjectHelperTest extends TestCase { + + /** The old container id */ + private final static String OLD_CONTAINER_ID = + "com.android.ide.eclipse.adt.project.AndroidClasspathContainerInitializer"; //$NON-NLS-1$ + + /** The container id for the android framework jar file */ + private final static String CONTAINER_ID = + "com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"; //$NON-NLS-1$ + + @Override + public void setUp() throws Exception { + // pass for now + } + + @Override + public void tearDown() throws Exception { + // pass for now + } + + public final void testFixProjectClasspathEntriesFromOldContainer() throws Exception { + // create a project with a path to an android .zip + IJavaProject javaProject = Mocks.createProject( + new IClasspathEntry[] { + Mocks.createClasspathEntry(new Path("Project/src"), //$NON-NLS-1$ + IClasspathEntry.CPE_SOURCE), + Mocks.createClasspathEntry(new Path(OLD_CONTAINER_ID), + IClasspathEntry.CPE_CONTAINER), + }, + new Path("Project/bin")); + + ProjectHelper.fixProjectClasspathEntries(javaProject); + + IClasspathEntry[] fixedEntries = javaProject.getRawClasspath(); + assertEquals(3, fixedEntries.length); + assertEquals("Project/src", fixedEntries[0].getPath().toString()); + assertEquals(OLD_CONTAINER_ID, fixedEntries[1].getPath().toString()); + assertEquals(CONTAINER_ID, fixedEntries[2].getPath().toString()); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeMoveParticipantTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeMoveParticipantTest.java new file mode 100644 index 000000000..262ea420e --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeMoveParticipantTest.java @@ -0,0 +1,135 @@ +/* + * 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.eclipse.adt.internal.refactorings.core; + +import com.android.annotations.NonNull; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; + +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.internal.corext.refactoring.reorg.IReorgPolicy.IMovePolicy; +import org.eclipse.jdt.internal.corext.refactoring.reorg.JavaMoveProcessor; +import org.eclipse.jdt.internal.corext.refactoring.reorg.ReorgDestinationFactory; +import org.eclipse.jdt.internal.corext.refactoring.reorg.ReorgPolicyFactory; +import org.eclipse.jdt.internal.ui.refactoring.reorg.CreateTargetQueries; +import org.eclipse.jdt.internal.ui.refactoring.reorg.ReorgQueries; +import org.eclipse.ltk.core.refactoring.participants.MoveRefactoring; +import org.eclipse.swt.widgets.Shell; + + +@SuppressWarnings({"javadoc", "restriction"}) +public class AndroidTypeMoveParticipantTest extends RefactoringTestBase { + public void testRefactor1() throws Exception { + moveType( + TEST_PROJECT2, + "com.example.refactoringtest.CustomView1", + "src/com/example/refactoringtest/subpackage", + true /*updateReferences*/, + + "CHANGES:\n" + + "-------\n" + + "[x] Move resource 'testRefactor1/src/com/example/refactoringtest/CustomView1.java' to 'subpackage'\n" + + "\n" + + "[x] Move resource 'testRefactor1/src/com/example/refactoringtest/CustomView1.java' to 'subpackage'\n" + + "\n" + + "[x] customviews.xml - /testRefactor1/res/layout/customviews.xml\n" + + " @@ -9 +9\n" + + " - <com.example.refactoringtest.CustomView1\n" + + " + <com.example.refactoringtest.subpackage.CustomView1\n" + + "\n" + + "\n" + + "[x] customviews.xml - /testRefactor1/res/layout-land/customviews.xml\n" + + " @@ -9 +9\n" + + " - <com.example.refactoringtest.CustomView1\n" + + " + <com.example.refactoringtest.subpackage.CustomView1"); + } + + public void testRefactorFragment() throws Exception { + moveType( + TEST_PROJECT2, + "com.example.refactoringtest.MyFragment", + "src/com/example/refactoringtest/subpackage", + true /*updateReferences*/, + + "CHANGES:\n" + + "-------\n" + + "[x] Move resource 'testRefactorFragment/src/com/example/refactoringtest/MyFragment.java' to 'subpackage'\n" + + "\n" + + "[x] Move resource 'testRefactorFragment/src/com/example/refactoringtest/MyFragment.java' to 'subpackage'\n" + + "\n" + + "[x] activity_main.xml - /testRefactorFragment/res/layout/activity_main.xml\n" + + " @@ -33 +33\n" + + " - <fragment android:name=\"com.example.refactoringtest.MyFragment\"/>\n" + + " + <fragment android:name=\"com.example.refactoringtest.subpackage.MyFragment\"/>"); + } + + public void testRefactor1_norefs() throws Exception { + moveType( + TEST_PROJECT2, + "com.example.refactoringtest.CustomView1", + "src/com/example/refactoringtest/subpackage", + false /*updateReferences*/, + + "CHANGES:\n" + + "-------\n" + + "[x] Move resource 'testRefactor1_norefs/src/com/example/refactoringtest/CustomView1.java' to 'subpackage'\n" + + "\n" + + "[x] Move resource 'testRefactor1_norefs/src/com/example/refactoringtest/CustomView1.java' to 'subpackage'"); + } + + // ---- Test infrastructure ---- + + protected void moveType( + @NonNull Object[] testData, + @NonNull String typeFqcn, + @NonNull String destination, + boolean updateReferences, + @NonNull String expected) throws Exception { + IProject project = createProject(testData); + + IFolder destinationFolder = project.getFolder(destination); + + IJavaProject javaProject = BaseProjectHelper.getJavaProject(project); + assertNotNull(javaProject); + IType type = javaProject.findType(typeFqcn); + assertNotNull(typeFqcn, type); + assertTrue(typeFqcn, type.exists()); + IResource resource = type.getResource(); + assertNotNull(typeFqcn, resource); + assertTrue(typeFqcn, resource.exists()); + + IResource[] resources = new IResource[] { resource }; + IJavaElement[] elements = new IJavaElement[] { type }; + IMovePolicy policy = ReorgPolicyFactory.createMovePolicy(resources, elements); + JavaMoveProcessor processor = new JavaMoveProcessor(policy); + processor.setUpdateReferences(updateReferences); + processor.setUpdateQualifiedNames(true); + assertTrue(policy.canEnable()); + processor.setDestination(ReorgDestinationFactory.createDestination(destinationFolder)); + Shell parent = AdtPlugin.getShell(); + assertNotNull(parent); + processor.setCreateTargetQueries(new CreateTargetQueries(parent)); + processor.setReorgQueries(new ReorgQueries(parent)); + + MoveRefactoring refactoring = new MoveRefactoring(processor); + checkRefactoring(refactoring, expected); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipantTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipantTest.java new file mode 100644 index 000000000..f65124a2c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipantTest.java @@ -0,0 +1,160 @@ +/* + * 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.eclipse.adt.internal.refactorings.core; + +import com.android.annotations.NonNull; +import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; + +import org.eclipse.core.resources.IProject; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.internal.corext.refactoring.rename.RenameTypeProcessor; +import org.eclipse.ltk.core.refactoring.participants.RenameRefactoring; + + +@SuppressWarnings({"javadoc", "restriction"}) +public class AndroidTypeRenameParticipantTest extends RefactoringTestBase { + public void testRefactor1() throws Exception { + renameType( + TEST_PROJECT, + "com.example.refactoringtest.MainActivity", + true /*updateReferences*/, + "NewActivityName", + + "CHANGES:\n" + + "-------\n" + + "[x] Rename compilation unit 'MainActivity.java' to 'NewActivityName.java'\n" + + "\n" + + "[x] activity_main.xml - /testRefactor1/res/layout/activity_main.xml\n" + + " @@ -5 +5\n" + + " - tools:context=\".MainActivity\" >\n" + + " + tools:context=\".NewActivityName\" >\n" + + "\n" + + "\n" + + "[x] activity_main.xml - /testRefactor1/res/layout-land/activity_main.xml\n" + + " @@ -5 +5\n" + + " - tools:context=\".MainActivity\" >\n" + + " + tools:context=\".NewActivityName\" >\n" + + "\n" + + "\n" + + "[x] AndroidManifest.xml - /testRefactor1/AndroidManifest.xml\n" + + " @@ -16 +16\n" + + " - android:name=\"com.example.refactoringtest.MainActivity\"\n" + + " + android:name=\"com.example.refactoringtest.NewActivityName\""); + } + + public void testRefactor1b() throws Exception { + renameType( + TEST_PROJECT, + "com.example.refactoringtest.MainActivity2", + true /*updateReferences*/, + "NewActivityName", + + "CHANGES:\n" + + "-------\n" + + "[x] Rename compilation unit 'MainActivity2.java' to 'NewActivityName.java'\n" + + "\n" + + "[x] AndroidManifest.xml - /testRefactor1b/AndroidManifest.xml\n" + + " @@ -25 +25\n" + + " - android:name=\".MainActivity2\"\n" + + " + android:name=\".NewActivityName\""); + } + + public void testRefactor1_noreferences() throws Exception { + renameType( + TEST_PROJECT, + "com.example.refactoringtest.MainActivity", + false /*updateReferences*/, + "NewActivityName", + + "CHANGES:\n" + + "-------\n" + + "[x] Rename compilation unit 'MainActivity.java' to 'NewActivityName.java'"); + } + + public void testRefactor2() throws Exception { + renameType( + TEST_PROJECT2, + "com.example.refactoringtest.CustomView1", + true /*updateReferences*/, + "NewCustomViewName", + + "CHANGES:\n" + + "-------\n" + + "[x] Rename compilation unit 'CustomView1.java' to 'NewCustomViewName.java'\n" + + "\n" + + "[x] attrs_custom_view.xml - /testRefactor2/res/values/attrs_custom_view.xml\n" + + " @@ -4 +4\n" + + " - <declare-styleable name=\"CustomView1\">\n" + + " + <declare-styleable name=\"NewCustomViewName\">\n" + + "\n" + + "\n" + + "[x] customviews.xml - /testRefactor2/res/layout/customviews.xml\n" + + " @@ -9 +9\n" + + " - <com.example.refactoringtest.CustomView1\n" + + " + <com.example.refactoringtest.NewCustomViewName\n" + + "\n" + + "\n" + + "[x] customviews.xml - /testRefactor2/res/layout-land/customviews.xml\n" + + " @@ -9 +9\n" + + " - <com.example.refactoringtest.CustomView1\n" + + " + <com.example.refactoringtest.NewCustomViewName"); + } + + public void testRefactorFragment() throws Exception { + renameType( + TEST_PROJECT2, + "com.example.refactoringtest.MyFragment", + true /*updateReferences*/, + "NewFragmentName", + + "CHANGES:\n" + + "-------\n" + + "[x] Rename compilation unit 'MyFragment.java' to 'NewFragmentName.java'\n" + + "\n" + + "[x] activity_main.xml - /testRefactorFragment/res/layout/activity_main.xml\n" + + " @@ -33 +33\n" + + " - <fragment android:name=\"com.example.refactoringtest.MyFragment\"/>\n" + + " + <fragment android:name=\"com.example.refactoringtest.NewFragmentName\"/>"); + } + + // ---- Test infrastructure ---- + + protected void renameType( + @NonNull Object[] testData, + @NonNull String typeFqcn, + boolean updateReferences, + @NonNull String newName, + @NonNull String expected) throws Exception { + IProject project = createProject(testData); + IJavaProject javaProject = BaseProjectHelper.getJavaProject(project); + assertNotNull(javaProject); + IType type = javaProject.findType(typeFqcn); + assertNotNull(typeFqcn, type); + assertTrue(typeFqcn, type.exists()); + RenameTypeProcessor processor = new RenameTypeProcessor(type); + processor.setNewElementName(newName); + processor.setUpdateQualifiedNames(true); + processor.setUpdateSimilarDeclarations(false); + //processor.setMatchStrategy(?); + //processor.setFilePatterns(patterns); + processor.setUpdateReferences(updateReferences); + assertNotNull(processor); + + RenameRefactoring refactoring = new RenameRefactoring(processor); + checkRefactoring(refactoring, expected); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceXmlTextActionTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceXmlTextActionTest.java new file mode 100644 index 000000000..e00a44b32 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceXmlTextActionTest.java @@ -0,0 +1,110 @@ +/* + * 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.eclipse.adt.internal.refactorings.core; + +import com.android.ide.common.resources.ResourceUrl; +import com.android.resources.ResourceType; + +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class RenameResourceXmlTextActionTest extends TestCase { + public void test_Simple() throws Exception { + checkWord("^foo", null); + checkWord("'foo'^", null); + checkWord("^@bogus", null); + checkWord("@bo^gus", null); + checkWord("bogus@^", null); + checkWord(" @string/nam^e ", getUrl(ResourceType.STRING, "name")); + checkWord("@string/nam^e ", getUrl(ResourceType.STRING, "name")); + checkWord("\"^@string/name ", getUrl(ResourceType.STRING, "name")); + checkWord("^@string/name ", getUrl(ResourceType.STRING, "name")); + checkWord("\n^@string/name ", getUrl(ResourceType.STRING, "name")); + checkWord("\n^@string/name(", getUrl(ResourceType.STRING, "name")); + checkWord("\n^@string/name;", getUrl(ResourceType.STRING, "name")); + checkWord("\n^@string/name5", getUrl(ResourceType.STRING, "name5")); + checkWord("\n@string/name5^", getUrl(ResourceType.STRING, "name5")); + checkWord("\n@string/name5^(", getUrl(ResourceType.STRING, "name5")); + checkWord("\n@stri^ng/name5(", getUrl(ResourceType.STRING, "name5")); + checkWord("\n@string^/name5(", getUrl(ResourceType.STRING, "name5")); + checkWord("\n@string/^name5(", getUrl(ResourceType.STRING, "name5")); + checkWord("\n@string^name5(", null); + checkWord("\n@strings^/name5(", null); + checkWord("\n@+id/^myid(", getUrl(ResourceType.ID, "myid")); + checkWord("\n?a^ttr/foo\"", getUrl(ResourceType.ATTR, "foo")); + checkWord("\n?f^oo\"", getUrl(ResourceType.ATTR, "foo")); + checkWord("\n^?foo\"", getUrl(ResourceType.ATTR, "foo")); + } + + private static ResourceUrl getUrl(ResourceType type, String name) { + return ResourceUrl.create(type, name, false, false); + } + + public void testClassNames() throws Exception { + checkClassName("^foo", null); + checkClassName("<^foo>", null); + checkClassName("'foo.bar.Baz'^", null); + checkClassName("<^foo.bar.Baz ", "foo.bar.Baz"); + checkClassName("<^foo.bar.Baz>", "foo.bar.Baz"); + checkClassName("<foo.^bar.Baz>", "foo.bar.Baz"); + checkClassName("<foo.bar.Baz^>", "foo.bar.Baz"); + checkClassName("<foo.bar.Baz^ >", "foo.bar.Baz"); + checkClassName("<foo.bar$Baz^ >", "foo.bar.Baz"); + checkClassName("</^foo.bar.Baz>", "foo.bar.Baz"); + checkClassName("</foo.^bar.Baz>", "foo.bar.Baz"); + + checkClassName("\"^foo.bar.Baz\"", "foo.bar.Baz"); + checkClassName("\"foo.^bar.Baz\"", "foo.bar.Baz"); + checkClassName("\"foo.bar.Baz^\"", "foo.bar.Baz"); + checkClassName("\"foo.bar$Baz^\"", "foo.bar.Baz"); + + checkClassName("<foo.^bar@Baz>", null); + } + + private void checkClassName(String contents, String expectedClassName) + throws Exception { + int cursor = contents.indexOf('^'); + assertTrue("Must set cursor position with ^ in " + contents, cursor != -1); + contents = contents.substring(0, cursor) + contents.substring(cursor + 1); + assertEquals(-1, contents.indexOf('^')); + assertEquals(-1, contents.indexOf('[')); + assertEquals(-1, contents.indexOf(']')); + + IDocument document = new Document(); + document.replace(0, 0, contents); + String className = + RenameResourceXmlTextAction.findClassName(document, null, cursor); + assertEquals(expectedClassName, className); + } + + private void checkWord(String contents, ResourceUrl expectedResource) + throws Exception { + int cursor = contents.indexOf('^'); + assertTrue("Must set cursor position with ^ in " + contents, cursor != -1); + contents = contents.substring(0, cursor) + contents.substring(cursor + 1); + assertEquals(-1, contents.indexOf('^')); + assertEquals(-1, contents.indexOf('[')); + assertEquals(-1, contents.indexOf(']')); + + IDocument document = new Document(); + document.replace(0, 0, contents); + ResourceUrl resource = RenameResourceXmlTextAction.findResource(document, cursor); + assertEquals(expectedResource, resource); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java new file mode 100644 index 000000000..de4e59101 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java @@ -0,0 +1,183 @@ +/* + * 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.eclipse.adt.internal.resources; + +import com.android.ide.common.resources.ResourceDeltaKind; +import com.android.ide.common.resources.configuration.FolderConfiguration; +import com.android.ide.common.resources.configuration.ResourceQualifier; +import com.android.resources.ResourceType; + +import org.eclipse.core.resources.IResourceDelta; + +import junit.framework.TestCase; + + +/** + * Test ResourceHelper + */ +@SuppressWarnings("javadoc") +public class ResourceHelperTest extends TestCase { + + /** + * temp fake qualifier class. + */ + private static class FakeQualifierClass extends ResourceQualifier { + + @Override + public boolean checkAndSet(String value, FolderConfiguration config) { + return false; + } + + @Override + public boolean equals(Object object) { + return false; + } + + @Override + public String getFolderSegment() { + return null; + } + + @Override + public String getLongDisplayValue() { + return null; + } + + @Override + public String getName() { + return null; + } + + @Override + public String getShortDisplayValue() { + return null; + } + + @Override + public String getShortName() { + return null; + } + + @Override + public boolean hasFakeValue() { + return false; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean isValid() { + return false; + } + + @Override + public int since() { + return 0; + } + } + + public void testGetIcon() throws Exception { + // check that the method returns null for an unknown qualifier class + assertNull(ResourceHelper.getIcon(FakeQualifierClass.class)); + + // find all the qualifiers through FolderConfiguration.createdefault() + FolderConfiguration config = new FolderConfiguration(); + config.createDefault(); + final int count = FolderConfiguration.getQualifierCount(); + for (int i = 0 ; i < count ; i++) { + ResourceQualifier qual = config.getQualifier(i); + assertNotNull(qual); + assertNotNull(qual.getClass().getCanonicalName(), + ResourceHelper.getIcon(qual.getClass())); + } + } + + public void testGetResourceDeltaKind() { + assertEquals(ResourceDeltaKind.ADDED, + ResourceHelper.getResourceDeltaKind(IResourceDelta.ADDED)); + assertEquals(ResourceDeltaKind.REMOVED, + ResourceHelper.getResourceDeltaKind(IResourceDelta.REMOVED)); + assertEquals(ResourceDeltaKind.CHANGED, + ResourceHelper.getResourceDeltaKind(IResourceDelta.CHANGED)); + + assertNull(ResourceHelper.getResourceDeltaKind(IResourceDelta.ADDED_PHANTOM)); + } + + public void testIsFileBasedResourceType() throws Exception { + assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.ANIMATOR)); + assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.LAYOUT)); + + assertFalse(ResourceHelper.isFileBasedResourceType(ResourceType.STRING)); + assertFalse(ResourceHelper.isFileBasedResourceType(ResourceType.DIMEN)); + assertFalse(ResourceHelper.isFileBasedResourceType(ResourceType.ID)); + + // Both: + assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.DRAWABLE)); + assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.COLOR)); + } + + public void testIsValueBasedResourceType() throws Exception { + assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.STRING)); + assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.DIMEN)); + assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.ID)); + + assertFalse(ResourceHelper.isValueBasedResourceType(ResourceType.LAYOUT)); + + // These can be both: + assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.DRAWABLE)); + assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.COLOR)); + } + + public void testCanCreateResource() throws Exception { + assertTrue(ResourceHelper.canCreateResource("@layout/foo")); + assertTrue(ResourceHelper.canCreateResource("@string/foo")); + assertTrue(ResourceHelper.canCreateResource("@dimen/foo")); + assertTrue(ResourceHelper.canCreateResource("@color/foo")); + + assertFalse(ResourceHelper.canCreateResource("@typo/foo")); // nonexistent type + assertFalse(ResourceHelper.canCreateResource("@layout/foo bar")); // space + assertFalse(ResourceHelper.canCreateResource("@layout/new")); // keyword + assertFalse(ResourceHelper.canCreateResource("@android:string/foo")); // framework + assertFalse(ResourceHelper.canCreateResource("@android:dimen/foo")); + assertFalse(ResourceHelper.canCreateResource("@android:color/foo")); + } + + public void testCanCreateResourceType() throws Exception { + assertTrue(ResourceHelper.canCreateResourceType(ResourceType.LAYOUT)); + assertTrue(ResourceHelper.canCreateResourceType(ResourceType.STRING)); + assertTrue(ResourceHelper.canCreateResourceType(ResourceType.DIMEN)); + assertTrue(ResourceHelper.canCreateResourceType(ResourceType.COLOR)); + + assertFalse(ResourceHelper.canCreateResourceType(ResourceType.RAW)); + assertFalse(ResourceHelper.canCreateResourceType(ResourceType.XML)); + } + + public void testStyleToTheme() throws Exception { + assertEquals("Foo", ResourceHelper.styleToTheme("Foo")); + assertEquals("Theme", ResourceHelper.styleToTheme("@android:style/Theme")); + assertEquals("LocalTheme", ResourceHelper.styleToTheme("@style/LocalTheme")); + //assertEquals("LocalTheme", ResourceHelper.styleToTheme("@foo.bar:style/LocalTheme")); + } + + public void testIsProjectStyle() throws Exception { + assertFalse(ResourceHelper.isProjectStyle("@android:style/Theme")); + assertTrue(ResourceHelper.isProjectStyle("@style/LocalTheme")); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java new file mode 100644 index 000000000..2cc2c932c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java @@ -0,0 +1,101 @@ +/* + * 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.eclipse.adt.internal.resources; + +import com.android.resources.ResourceFolderType; +import com.android.resources.ResourceType; + +import org.eclipse.core.resources.IProject; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class ResourceNameValidatorTest extends TestCase { + public void testValidator() throws Exception { + // Valid + ResourceNameValidator validator = ResourceNameValidator.create(true, + ResourceFolderType.VALUES); + assertTrue(validator.isValid("foo") == null); + assertTrue(validator.isValid("foo.xml") == null); + assertTrue(validator.isValid("Foo123_$") == null); + assertTrue(validator.isValid("foo.xm") == null); // For non-file types, . => _ + + // Invalid + assertTrue(validator.isValid("") != null); + assertTrue(validator.isValid(" ") != null); + assertTrue(validator.isValid("foo bar") != null); + assertTrue(validator.isValid("1foo") != null); + assertTrue(validator.isValid("foo%bar") != null); + assertTrue(ResourceNameValidator.create(true, Collections.singleton("foo"), + ResourceType.STRING).isValid("foo") != null); + assertTrue(ResourceNameValidator.create(true, + ResourceFolderType.DRAWABLE).isValid("foo.xm") != null); + assertTrue(ResourceNameValidator.create(false, + ResourceFolderType.DRAWABLE).isValid("foo.xm") != null); + + // Only lowercase chars allowed in file-based resource names + assertTrue(ResourceNameValidator.create(true, ResourceFolderType.LAYOUT) + .isValid("Foo123_$") != null); + assertTrue(ResourceNameValidator.create(true, ResourceFolderType.LAYOUT) + .isValid("foo123_") == null); + + // Can't start with _ in file-based resource names, is okay for value based resources + assertTrue(ResourceNameValidator.create(true, ResourceFolderType.VALUES) + .isValid("_foo") == null); + assertTrue(ResourceNameValidator.create(true, ResourceFolderType.LAYOUT) + .isValid("_foo") != null); + assertTrue(ResourceNameValidator.create(true, ResourceFolderType.DRAWABLE) + .isValid("_foo") != null); + } + + public void testIds() throws Exception { + ResourceNameValidator validator = ResourceNameValidator.create(false, (IProject) null, + ResourceType.ID); + assertTrue(validator.isValid("foo") == null); + assertTrue(validator.isValid(" foo") != null); + assertTrue(validator.isValid("foo ") != null); + assertTrue(validator.isValid("foo@") != null); + } + + public void testUniqueOrExists() throws Exception { + Set<String> existing = new HashSet<String>(); + existing.add("foo1"); + existing.add("foo2"); + existing.add("foo3"); + + ResourceNameValidator validator = ResourceNameValidator.create(true, existing, + ResourceType.ID); + validator.unique(); + + assertNull(validator.isValid("foo")); // null: ok (no error message) + assertNull(validator.isValid("foo4")); + assertNotNull(validator.isValid("foo1")); + assertNotNull(validator.isValid("foo2")); + assertNotNull(validator.isValid("foo3")); + + validator.exist(); + assertNotNull(validator.isValid("foo")); + assertNotNull(validator.isValid("foo4")); + assertNull(validator.isValid("foo1")); + assertNull(validator.isValid("foo2")); + assertNull(validator.isValid("foo3")); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContextTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContextTest.java new file mode 100644 index 000000000..3104c8593 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContextTest.java @@ -0,0 +1,22 @@ +/* + * 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.eclipse.adt.internal.resources.manager; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class IdeScanningContextTest extends TestCase { +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/sdk/AndroidJarLoaderTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/sdk/AndroidJarLoaderTest.java new file mode 100644 index 000000000..5f7de429b --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/sdk/AndroidJarLoaderTest.java @@ -0,0 +1,166 @@ +/* + * 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.eclipse.adt.internal.sdk; + +import com.android.ide.eclipse.adt.internal.sdk.AndroidJarLoader; +import com.android.ide.eclipse.adt.internal.sdk.IAndroidClassLoader.IClassDescriptor; +import com.android.ide.eclipse.tests.AdtTestData; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; + +import junit.framework.TestCase; + +/** + * Unit Test for {@link AndroidJarLoader}. + * + * Uses the classes jar.example.Class1/Class2 stored in tests/data/jar_example.jar. + */ +public class AndroidJarLoaderTest extends TestCase { + + private AndroidJarLoader mFrameworkClassLoader; + + /** Creates an instance of {@link AndroidJarLoader} on our test data JAR */ + @Override + public void setUp() throws Exception { + String jarfilePath = AdtTestData.getInstance().getTestFilePath( + "com/android/ide/eclipse/testdata/jar_example.jar"); //$NON-NLS-1$ + mFrameworkClassLoader = new AndroidJarLoader(jarfilePath); + } + + @Override + public void tearDown() throws Exception { + mFrameworkClassLoader = null; + System.gc(); + } + + /** Preloads classes. They should load just fine. */ + public final void testPreLoadClasses() throws Exception { + mFrameworkClassLoader.preLoadClasses("jar.example.", null, null); //$NON-NLS-1$ + HashMap<String, Class<?>> map = getPrivateClassCache(); + assertEquals(0, map.size()); + HashMap<String,byte[]> data = getPrivateEntryCache(); + assertTrue(data.containsKey("jar.example.Class1")); //$NON-NLS-1$ + assertTrue(data.containsKey("jar.example.Class2")); //$NON-NLS-1$ + assertTrue(data.containsKey("jar.example.Class1$InnerStaticClass1")); //$NON-NLS-1$ + assertTrue(data.containsKey("jar.example.Class1$InnerClass2")); //$NON-NLS-1$ + assertEquals(4, data.size()); + } + + /** Preloads a class not in the JAR. Preloading does nothing in this case. */ + public final void testPreLoadClasses_classNotFound() throws Exception { + mFrameworkClassLoader.preLoadClasses("not.a.package.", null, null); //$NON-NLS-1$ + HashMap<String, Class<?>> map = getPrivateClassCache(); + assertEquals(0, map.size()); + HashMap<String,byte[]> data = getPrivateEntryCache(); + assertEquals(0, data.size()); + } + + /** Finds a class we just preloaded. It should work. */ + public final void testFindClass_classFound() throws Exception { + Class<?> c = _findClass(mFrameworkClassLoader, "jar.example.Class2"); //$NON-NLS-1$ + assertEquals("jar.example.Class2", c.getName()); //$NON-NLS-1$ + HashMap<String, Class<?>> map = getPrivateClassCache(); + assertTrue(map.containsKey("jar.example.Class1")); //$NON-NLS-1$ + assertTrue(map.containsKey("jar.example.Class2")); //$NON-NLS-1$ + assertEquals(2, map.size()); + } + + /** call the protected method findClass */ + private Class<?> _findClass(AndroidJarLoader jarLoader, String name) throws Exception { + Method findClassMethod = AndroidJarLoader.class.getDeclaredMethod( + "findClass", String.class); //$NON-NLS-1$ + findClassMethod.setAccessible(true); + try { + return (Class<?>)findClassMethod.invoke(jarLoader, name); + } + catch (InvocationTargetException e) { + throw (Exception)e.getCause(); + } + } + + /** Trying to find a class that we fail to preload should throw a CNFE. */ + public final void testFindClass_classNotFound() throws Exception { + try { + // Will throw ClassNotFoundException + _findClass(mFrameworkClassLoader, "not.a.valid.ClassName"); //$NON-NLS-1$ + } catch (ClassNotFoundException e) { + // check the message in the CNFE + assertEquals("not.a.valid.ClassName", e.getMessage()); //$NON-NLS-1$ + return; + } + // Exception not thrown - this is a failure + fail("Expected ClassNotFoundException not thrown"); + } + + public final void testFindClassesDerivingFrom() throws Exception { + HashMap<String, ArrayList<IClassDescriptor>> found = + mFrameworkClassLoader.findClassesDerivingFrom("jar.example.", new String[] { //$NON-NLS-1$ + "jar.example.Class1", //$NON-NLS-1$ + "jar.example.Class2" }); //$NON-NLS-1$ + + assertTrue(found.containsKey("jar.example.Class1")); //$NON-NLS-1$ + assertTrue(found.containsKey("jar.example.Class2")); //$NON-NLS-1$ + assertEquals(2, found.size()); + // Only Class2 derives from Class1.. + // Class1 and Class1$InnerStaticClass1 derive from Object and are thus ignored. + // Class1$InnerClass2 should never be seen either. + assertEquals("jar.example.Class2", //$NON-NLS-1$ + found.get("jar.example.Class1").get(0).getFullClassName()); //$NON-NLS-1$ + assertEquals(1, found.get("jar.example.Class1").size()); //$NON-NLS-1$ + assertEquals(0, found.get("jar.example.Class2").size()); //$NON-NLS-1$ + } + + // --- Utilities --- + + /** + * Retrieves the private mFrameworkClassLoader.mClassCache field using reflection. + * + * @throws NoSuchFieldException + * @throws SecurityException + * @throws IllegalAccessException + * @throws IllegalArgumentException + */ + @SuppressWarnings("unchecked") + private HashMap<String, Class<?> > getPrivateClassCache() + throws SecurityException, NoSuchFieldException, + IllegalArgumentException, IllegalAccessException { + Field field = AndroidJarLoader.class.getDeclaredField("mClassCache"); //$NON-NLS-1$ + field.setAccessible(true); + return (HashMap<String, Class<?>>) field.get(mFrameworkClassLoader); + } + + /** + * Retrieves the private mFrameworkClassLoader.mEntryCache field using reflection. + * + * @throws NoSuchFieldException + * @throws SecurityException + * @throws IllegalAccessException + * @throws IllegalArgumentException + */ + @SuppressWarnings("unchecked") + private HashMap<String,byte[]> getPrivateEntryCache() + throws SecurityException, NoSuchFieldException, + IllegalArgumentException, IllegalAccessException { + Field field = AndroidJarLoader.class.getDeclaredField("mEntryCache"); //$NON-NLS-1$ + field.setAccessible(true); + return (HashMap<String, byte[]>) field.get(mFrameworkClassLoader); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/sdk/LayoutParamsParserTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/sdk/LayoutParamsParserTest.java new file mode 100644 index 000000000..c89dd0649 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/sdk/LayoutParamsParserTest.java @@ -0,0 +1,182 @@ +/* + * 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.eclipse.adt.internal.sdk; + +import com.android.ide.common.resources.platform.AttrsXmlParser; +import com.android.ide.common.resources.platform.ViewClassInfo; +import com.android.ide.common.resources.platform.ViewClassInfo.LayoutParamsInfo; +import com.android.ide.eclipse.adt.internal.sdk.AndroidJarLoader.ClassWrapper; +import com.android.ide.eclipse.adt.internal.sdk.IAndroidClassLoader.IClassDescriptor; +import com.android.ide.eclipse.mock.TestLogger; +import com.android.ide.eclipse.tests.AdtTestData; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.TreeMap; + +import junit.framework.TestCase; + +/** + * Test the inner private methods of PlatformDataParser. + * + * Convention: method names that start with an underscore are actually local wrappers + * that call private methods from {@link AndroidTargetParser} using reflection. + * This is inspired by the Python coding rule which mandates underscores prefixes for + * "private" methods. + */ +public class LayoutParamsParserTest extends TestCase { + + private static final String MOCK_DATA_PATH = + "com/android/ide/eclipse/testdata/mock_attrs.xml"; //$NON-NLS-1$ + + private static class MockFrameworkClassLoader extends AndroidJarLoader { + MockFrameworkClassLoader() { + super(null /* osFrameworkLocation */); + } + + @Override + public HashMap<String, ArrayList<IClassDescriptor>> findClassesDerivingFrom( + String rootPackage, String[] superClasses) throws ClassFormatError { + return new HashMap<String, ArrayList<IClassDescriptor>>(); + } + } + + private static class MockLayoutParamsParser extends LayoutParamsParser { + public MockLayoutParamsParser() { + super(new MockFrameworkClassLoader(), + new AttrsXmlParser( + AdtTestData.getInstance().getTestFilePath(MOCK_DATA_PATH), + new TestLogger(), 100).preload()); + + mTopViewClass = new ClassWrapper(mock_android.view.View.class); + mTopGroupClass = new ClassWrapper(mock_android.view.ViewGroup.class); + mTopLayoutParamsClass = new ClassWrapper(mock_android.view.ViewGroup.LayoutParams.class); + + mViewList = new ArrayList<IClassDescriptor>(); + mGroupList = new ArrayList<IClassDescriptor>(); + mViewMap = new TreeMap<String, ExtViewClassInfo>(); + mGroupMap = new TreeMap<String, ExtViewClassInfo>(); + mLayoutParamsMap = new HashMap<String, LayoutParamsInfo>(); + } + } + + private MockLayoutParamsParser mParser; + + @Override + public void setUp() throws Exception { + mParser = new MockLayoutParamsParser(); + } + + @Override + public void tearDown() throws Exception { + } + + public final void testFindLayoutParams() throws Exception { + assertEquals(mock_android.view.ViewGroup.LayoutParams.class, + ((ClassWrapper)_findLayoutParams(mock_android.view.ViewGroup.class)).wrappedClass()); + + assertEquals(mock_android.widget.LinearLayout.LayoutParams.class, + ((ClassWrapper)_findLayoutParams(mock_android.widget.LinearLayout.class)).wrappedClass()); + + assertEquals(mock_android.widget.TableLayout.LayoutParams.class, + ((ClassWrapper)_findLayoutParams(mock_android.widget.TableLayout.class)).wrappedClass()); + } + + public final void testGetLayoutParamsInfo() throws Exception { + LayoutParamsInfo info1 = _getLayoutParamsInfo( + mock_android.view.ViewGroup.LayoutParams.class); + assertNotNull(info1); + // ViewGroup.LayoutData has Object for superClass, which we don't map + assertNull(info1.getSuperClass()); + + LayoutParamsInfo info2 = _getLayoutParamsInfo( + mock_android.widget.LinearLayout.LayoutParams.class); + assertNotNull(info2); + // LinearLayout.LayoutData links to ViewGroup.LayoutParams + assertSame(info1, info2.getSuperClass()); + + LayoutParamsInfo info3 = _getLayoutParamsInfo( + mock_android.widget.TableLayout.LayoutParams.class); + assertNotNull(info3); + // TableLayout.LayoutData does not link to ViewGroup.LayoutParams nor + // LinearLayout.LayoutParams + assertNotSame(info1, info3.getSuperClass()); + assertNotSame(info2, info3.getSuperClass()); + // TableLayout.LayoutParams => ViewGroup.MarginLayoutParams => ViewGroup.LayoutParams + assertSame(info1, info3.getSuperClass().getSuperClass()); + } + + public final void testGetLayoutClasses() throws Exception { + // _getLayoutClasses(); + } + + //---- access to private methods + + /** Calls the private constructor of the parser */ + @SuppressWarnings("unused") + private AndroidTargetParser _Constructor(String osJarPath) throws Exception { + Constructor<AndroidTargetParser> constructor = + AndroidTargetParser.class.getDeclaredConstructor(String.class); + constructor.setAccessible(true); + return constructor.newInstance(osJarPath); + } + + /** calls the private getLayoutClasses() of the parser */ + @SuppressWarnings("unused") + private void _getLayoutClasses() throws Exception { + Method method = AndroidTargetParser.class.getDeclaredMethod("getLayoutClasses"); //$NON-NLS-1$ + method.setAccessible(true); + method.invoke(mParser); + } + + /** calls the private addGroup() of the parser */ + @SuppressWarnings("unused") + private ViewClassInfo _addGroup(Class<?> groupClass) throws Exception { + Method method = LayoutParamsParser.class.getDeclaredMethod("addGroup", //$NON-NLS-1$ + IClassDescriptor.class); + method.setAccessible(true); + return (ViewClassInfo) method.invoke(mParser, new ClassWrapper(groupClass)); + } + + /** calls the private addLayoutParams() of the parser */ + @SuppressWarnings("unused") + private LayoutParamsInfo _addLayoutParams(Class<?> groupClass) throws Exception { + Method method = LayoutParamsParser.class.getDeclaredMethod("addLayoutParams", //$NON-NLS-1$ + IClassDescriptor.class); + method.setAccessible(true); + return (LayoutParamsInfo) method.invoke(mParser, new ClassWrapper(groupClass)); + } + + /** calls the private getLayoutParamsInfo() of the parser */ + private LayoutParamsInfo _getLayoutParamsInfo(Class<?> layoutParamsClass) throws Exception { + Method method = LayoutParamsParser.class.getDeclaredMethod("getLayoutParamsInfo", //$NON-NLS-1$ + IClassDescriptor.class); + method.setAccessible(true); + return (LayoutParamsInfo) method.invoke(mParser, new ClassWrapper(layoutParamsClass)); + } + + /** calls the private findLayoutParams() of the parser */ + private IClassDescriptor _findLayoutParams(Class<?> groupClass) throws Exception { + Method method = LayoutParamsParser.class.getDeclaredMethod("findLayoutParams", //$NON-NLS-1$ + IClassDescriptor.class); + method.setAccessible(true); + return (IClassDescriptor) method.invoke(mParser, new ClassWrapper(groupClass)); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmActivityToLayoutMethodTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmActivityToLayoutMethodTest.java new file mode 100644 index 000000000..9caeab25e --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmActivityToLayoutMethodTest.java @@ -0,0 +1,50 @@ +/* + * 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.eclipse.adt.internal.wizards.templates; + +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class FmActivityToLayoutMethodTest extends TestCase { + @SuppressWarnings("rawtypes") + private void check(String s, String expected) throws TemplateModelException { + FmActivityToLayoutMethod method = new FmActivityToLayoutMethod(); + List list = Collections.singletonList(new SimpleScalar(s)); + assertEquals(expected, ((SimpleScalar) method.exec(list)).getAsString()); + } + + public void test1() throws Exception { + check("FooActivity", "activity_foo"); + } + + public void test2() throws Exception { + check("FooActiv", "activity_foo"); + } + + public void test3() throws Exception { + check("Foo", "activity_foo"); + } + + public void test4() throws Exception { + check("", ""); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmCamelCaseToUnderscoreMethodTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmCamelCaseToUnderscoreMethodTest.java new file mode 100644 index 000000000..dee0ce6bf --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmCamelCaseToUnderscoreMethodTest.java @@ -0,0 +1,58 @@ +/* + * 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.eclipse.adt.internal.wizards.templates; + +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class FmCamelCaseToUnderscoreMethodTest extends TestCase { + @SuppressWarnings("rawtypes") + private void check(String s, String expected) throws TemplateModelException { + FmCamelCaseToUnderscoreMethod method = new FmCamelCaseToUnderscoreMethod(); + List list = Collections.singletonList(new SimpleScalar(s)); + assertEquals(expected, ((SimpleScalar) method.exec(list)).getAsString()); + } + + public void test1() throws Exception { + check("", ""); + } + + public void test2() throws Exception { + check("foo", "foo"); + } + + public void test3() throws Exception { + check("Foo", "foo"); + } + + public void test4() throws Exception { + check("FooBar", "foo_bar"); + } + + public void test5() throws Exception { + check("testXML", "test_xml"); + } + + public void test6() throws Exception { + check("testFoo", "test_foo"); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmClassNameToResourceMethodTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmClassNameToResourceMethodTest.java new file mode 100644 index 000000000..ff90ea0ed --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmClassNameToResourceMethodTest.java @@ -0,0 +1,66 @@ +/* + * 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.eclipse.adt.internal.wizards.templates; + +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class FmClassNameToResourceMethodTest extends TestCase { + @SuppressWarnings("rawtypes") + private void check(String s, String expected) throws TemplateModelException { + FmClassNameToResourceMethod method = new FmClassNameToResourceMethod(); + List list = Collections.singletonList(new SimpleScalar(s)); + assertEquals(expected, ((SimpleScalar) method.exec(list)).getAsString()); + } + + public void test1() throws Exception { + check("FooActivity", "foo"); + } + + public void test2() throws Exception { + check("FooActiv", "foo"); + } + + public void test3() throws Exception { + check("Foo", "foo"); + } + + public void test4() throws Exception { + check("", ""); + } + + public void test5() throws Exception { + check("FooFragment", "foo"); + } + + public void test6() throws Exception { + check("FooService", "foo"); + } + + public void test7() throws Exception { + check("FooProvider", "foo"); + } + + public void test8() throws Exception { + check("FooBar", "foo_bar"); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlAttributeMethodTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlAttributeMethodTest.java new file mode 100644 index 000000000..eb1e94940 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlAttributeMethodTest.java @@ -0,0 +1,50 @@ +/* + * 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.eclipse.adt.internal.wizards.templates; + +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class FmEscapeXmlAttributeMethodTest extends TestCase { + @SuppressWarnings("rawtypes") + private void check(String s, String expected) throws TemplateModelException { + FmEscapeXmlAttributeMethod method = new FmEscapeXmlAttributeMethod(); + List list = Collections.singletonList(new SimpleScalar(s)); + assertEquals(expected, ((SimpleScalar) method.exec(list)).getAsString()); + } + + public void test1() throws Exception { + check("", ""); + } + + public void test2() throws Exception { + check("foo", "foo"); + } + + public void test3() throws Exception { + check("<\"'>&", "<"'>&"); + } + + public void test4() throws Exception { + check("foo>bar", "foo>bar"); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlStringMethodTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlStringMethodTest.java new file mode 100644 index 000000000..1a4289a03 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlStringMethodTest.java @@ -0,0 +1,66 @@ +/* + * 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.eclipse.adt.internal.wizards.templates; + +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class FmEscapeXmlStringMethodTest extends TestCase { + @SuppressWarnings("rawtypes") + private void check(String s, String expected) throws TemplateModelException { + FmEscapeXmlStringMethod method = new FmEscapeXmlStringMethod(); + List list = Collections.singletonList(new SimpleScalar(s)); + assertEquals(expected, ((SimpleScalar) method.exec(list)).getAsString()); + } + + public void test1() throws Exception { + check("", ""); + } + + public void test2() throws Exception { + check("foo", "foo"); + } + + public void test3() throws Exception { + check(" Foo Bar ", "\" Foo Bar \""); + } + + public void test4() throws Exception { + check("@foo", "\\@foo"); + } + + public void test5() throws Exception { + check("Hello\nWorld", "Hello\\nWorld"); + } + + public void test6() throws Exception { + check("A & B", "A & B"); + } + + public void test7() throws Exception { + check("Foo's Bar", "Foo\\'s Bar"); + } + + public void test8() throws Exception { + check("'\"\\", "\\'\\\"\\\\"); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlTextMethodTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlTextMethodTest.java new file mode 100644 index 000000000..c08b834e9 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlTextMethodTest.java @@ -0,0 +1,46 @@ +/* + * 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.eclipse.adt.internal.wizards.templates; + +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class FmEscapeXmlTextMethodTest extends TestCase { + @SuppressWarnings("rawtypes") + private void check(String s, String expected) throws TemplateModelException { + FmEscapeXmlTextMethod method = new FmEscapeXmlTextMethod(); + List list = Collections.singletonList(new SimpleScalar(s)); + assertEquals(expected, ((SimpleScalar) method.exec(list)).getAsString()); + } + + public void test1() throws Exception { + check("", ""); + } + + public void test2() throws Exception { + check("foo", "foo"); + } + + public void test3() throws Exception { + check("<\"'>&", "<\"'>&"); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmExtractLettersMethodTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmExtractLettersMethodTest.java new file mode 100644 index 000000000..b1d3cee13 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmExtractLettersMethodTest.java @@ -0,0 +1,46 @@ +/* + * 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.eclipse.adt.internal.wizards.templates; + +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class FmExtractLettersMethodTest extends TestCase { + @SuppressWarnings("rawtypes") + private void check(String s, String expected) throws TemplateModelException { + FmExtractLettersMethod method = new FmExtractLettersMethod(); + List list = Collections.singletonList(new SimpleScalar(s)); + assertEquals(expected, ((SimpleScalar) method.exec(list)).getAsString()); + } + + public void test1() throws Exception { + check("", ""); + } + + public void test2() throws Exception { + check("foo", "foo"); + } + + public void test3() throws Exception { + check("<\"'>&foo ", "foo"); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmLayoutToActivityMethodTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmLayoutToActivityMethodTest.java new file mode 100644 index 000000000..af0a1dbf5 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmLayoutToActivityMethodTest.java @@ -0,0 +1,54 @@ +/* + * 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.eclipse.adt.internal.wizards.templates; + +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class FmLayoutToActivityMethodTest extends TestCase { + @SuppressWarnings("rawtypes") + private void check(String s, String expected) throws TemplateModelException { + FmLayoutToActivityMethod method = new FmLayoutToActivityMethod(); + List list = Collections.singletonList(new SimpleScalar(s)); + assertEquals(expected, ((SimpleScalar) method.exec(list)).getAsString()); + } + + public void test1() throws Exception { + check("foo", "FooActivity"); + } + + public void test2() throws Exception { + check("activity_foo", "FooActivity"); + } + + public void test3() throws Exception { + check("activity_", "MyActivity"); + } + + public void test4() throws Exception { + check("activ", "ActivActivity"); + } + + public void test5() throws Exception { + check("", "MyActivity"); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmSlashedPackageNameMethodTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmSlashedPackageNameMethodTest.java new file mode 100644 index 000000000..439110237 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmSlashedPackageNameMethodTest.java @@ -0,0 +1,50 @@ +/* + * 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.eclipse.adt.internal.wizards.templates; + +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class FmSlashedPackageNameMethodTest extends TestCase { + @SuppressWarnings("rawtypes") + private void check(String s, String expected) throws TemplateModelException { + FmSlashedPackageNameMethod method = new FmSlashedPackageNameMethod(); + List list = Collections.singletonList(new SimpleScalar(s)); + assertEquals(expected, ((SimpleScalar) method.exec(list)).getAsString()); + } + + public void test1() throws Exception { + check("", ""); + } + + public void test2() throws Exception { + check("foo", "foo"); + } + + public void test3() throws Exception { + check("foo.bar.baz", "foo/bar/baz"); + } + + public void test4() throws Exception { + check("foo/bar/baz", "foo/bar/baz"); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmUnderscoreToCamelCaseMethodTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmUnderscoreToCamelCaseMethodTest.java new file mode 100644 index 000000000..4955dae79 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmUnderscoreToCamelCaseMethodTest.java @@ -0,0 +1,58 @@ +/* + * 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.eclipse.adt.internal.wizards.templates; + +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateModelException; + +import java.util.Collections; +import java.util.List; + +import junit.framework.TestCase; + +@SuppressWarnings("javadoc") +public class FmUnderscoreToCamelCaseMethodTest extends TestCase { + @SuppressWarnings("rawtypes") + private void check(String s, String expected) throws TemplateModelException { + FmUnderscoreToCamelCaseMethod method = new FmUnderscoreToCamelCaseMethod(); + List list = Collections.singletonList(new SimpleScalar(s)); + assertEquals(expected, ((SimpleScalar) method.exec(list)).getAsString()); + } + + public void test1() throws Exception { + check("", ""); + } + + public void test2() throws Exception { + check("_", ""); + } + + public void test3() throws Exception { + check("foo", "Foo"); + } + + public void test4() throws Exception { + check("foo_bar", "FooBar"); + } + + public void test5() throws Exception { + check("foo__bar", "FooBar"); + } + + public void test6() throws Exception { + check("foo_", "Foo"); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/Mocks.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/Mocks.java new file mode 100644 index 000000000..65e2144be --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/Mocks.java @@ -0,0 +1,189 @@ +/* + * 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.eclipse.mock; + +import static org.easymock.EasyMock.capture; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.eq; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.isA; +import static org.easymock.EasyMock.replay; + +import com.android.io.IAbstractFolder; +import com.android.io.IAbstractResource; + +import org.easymock.Capture; +import org.easymock.EasyMock; +import org.easymock.IAnswer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; + +import java.util.Map; + +public class Mocks { + public static IJavaProject createProject(IClasspathEntry[] entries, IPath outputLocation) + throws Exception { + IJavaProject javaProject = createMock(IJavaProject.class); + final Capture<IClasspathEntry[]> capturedEntries = new Capture<IClasspathEntry[]>(); + Capture<IPath> capturedOutput = new Capture<IPath>(); + capturedEntries.setValue(entries); + capturedOutput.setValue(outputLocation); + + IProject project = createProject(); + expect(javaProject.getProject()).andReturn(project).anyTimes(); + expect(javaProject.getOutputLocation()).andReturn(capturedOutput.getValue()).anyTimes(); + + expect(javaProject.getRawClasspath()).andAnswer(new IAnswer<IClasspathEntry[]>() { + @Override + public IClasspathEntry[] answer() throws Throwable { + return capturedEntries.getValue(); + } + }).anyTimes(); + + javaProject.setRawClasspath(capture(capturedEntries), isA(IProgressMonitor.class)); + expectLastCall().anyTimes(); + + javaProject.setRawClasspath(capture(capturedEntries), capture(capturedOutput), + isA(IProgressMonitor.class)); + expectLastCall().anyTimes(); + + final Capture<String> capturedCompliance = new Capture<String>(); + capturedCompliance.setValue("1.4"); + final Capture<String> capturedSource = new Capture<String>(); + capturedSource.setValue("1.4"); + final Capture<String> capturedTarget = new Capture<String>(); + capturedTarget.setValue("1.4"); + + expect(javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true)).andAnswer( + new IAnswer<String>() { + @Override + public String answer() throws Throwable { + return capturedCompliance.getValue(); + } + }); + expect(javaProject.getOption(JavaCore.COMPILER_SOURCE, true)).andAnswer( + new IAnswer<String>() { + @Override + public String answer() throws Throwable { + return capturedSource.getValue(); + } + }); + expect(javaProject.getOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, true)).andAnswer( + new IAnswer<String>() { + @Override + public String answer() throws Throwable { + return capturedTarget.getValue(); + } + }); + + javaProject.setOption(eq(JavaCore.COMPILER_COMPLIANCE), capture(capturedCompliance)); + expectLastCall().anyTimes(); + javaProject.setOption(eq(JavaCore.COMPILER_SOURCE), capture(capturedSource)); + expectLastCall().anyTimes(); + javaProject.setOption(eq(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM), + capture(capturedTarget)); + expectLastCall().anyTimes(); + + replay(javaProject); + + return javaProject; + } + + /** + * Creates a mock implementation of {@link IFile}. + * <p/> + * Supported methods: + * <ul> + * <li>IFile#getName()</li> + * <li>IFile#getLocation()</li> + * </ul> + */ + public static IFile createFile(String fileName) { + IFile file = createNiceMock(IFile.class); + expect(file.getName()).andReturn(fileName).anyTimes(); + expect(file.getLocation()).andReturn(new Path(fileName)).anyTimes(); + replay(file); + return file; + } + + /** + * Creates a mock implementation of {@link IFolder}. + * <p/> + * Supported methods: + * <ul> + * <li>{@link IFolder#getName()}</li> + * <li>{@link IFolder#members()}</li> + * </ul> + */ + public static IFolder createFolder(String name, IResource[] members) throws Exception { + IFolder file = createNiceMock(IFolder.class); + expect(file.getName()).andReturn(name).anyTimes(); + // expect(file.getLocation()).andReturn(new Path(name)).anyTimes(); + expect(file.members()).andReturn(members).anyTimes(); + replay(file); + return file; + } + + public static IAbstractFolder createAbstractFolder(String name, IAbstractResource[] members) { + IAbstractFolder folder = createNiceMock(IAbstractFolder.class); + expect(folder.getName()).andReturn(name).anyTimes(); + // expect(file.getLocation()).andReturn(new Path(name)).anyTimes(); + expect(folder.listMembers()).andReturn(members).anyTimes(); + replay(folder); + + return folder; + } + + /** + * Mock implementation of {@link IProject}. + * <p/> + * Supported methods: + * <ul> + * <li>{@link IProject#build(int kind, IProgressMonitor monitor)}</li> + * <li> + * {@link IProject#build(int kind, String builderName, Map args, IProgressMonitor monitor)} + * </li> + * </ul> + */ + public static IProject createProject() { + IProject project = EasyMock.createNiceMock(IProject.class); + replay(project); + return project; + } + + /** + * Creates a mock implementation of an {@link IClasspathEntry}, which supports + * {@link IClasspathEntry#getEntryKind} and {@link IClasspathEntry#getPath}. + */ + public static IClasspathEntry createClasspathEntry(IPath path, int kind) { + IClasspathEntry entry = createNiceMock(IClasspathEntry.class); + expect(entry.getEntryKind()).andReturn(kind).anyTimes(); + expect(entry.getPath()).andReturn(path).anyTimes(); + replay(entry); + return entry; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/TestLogger.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/TestLogger.java new file mode 100644 index 000000000..f8bf4b31a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/TestLogger.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.eclipse.org/org/documents/epl-v10.php + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ide.eclipse.mock; + + +import com.android.utils.ILogger; + +import junit.framework.Assert; + +/** + * Implementation of {@link ILogger} suitable for test use; will fail the current test if + * {@link #error} is called, and prints everything else to standard error. + */ +public class TestLogger implements ILogger { + + @Override + public void error(Throwable t, String errorFormat, Object... args) { + String message = String.format(errorFormat, args); + if (t != null) { + message = t.toString() + ":" + message; //$NON-NLS-1$ + } + Assert.fail(message); + } + + @Override + public void info(String msgFormat, Object... args) { + System.out.println(String.format(msgFormat, args)); + } + + @Override + public void verbose(String msgFormat, Object... args) { + info(msgFormat, args); + } + + @Override + public void warning(String warningFormat, Object... args) { + System.err.println(String.format(warningFormat, args)); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area-one-dot.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area-one-dot.9.png Binary files differnew file mode 100644 index 000000000..690d76efc --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area-one-dot.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area-two-dots.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area-two-dots.9.png Binary files differnew file mode 100644 index 000000000..c3d3e4c29 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area-two-dots.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area.9.png Binary files differnew file mode 100644 index 000000000..6a7a62c59 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched1.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched1.9.png Binary files differnew file mode 100644 index 000000000..9e5ffbf46 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched1.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched2.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched2.9.png Binary files differnew file mode 100644 index 000000000..cfd9c1d64 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched2.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched3.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched3.9.png Binary files differnew file mode 100644 index 000000000..0f2c3eba5 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched3.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched4.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched4.9.png Binary files differnew file mode 100644 index 000000000..ce7321032 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched4.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched5.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched5.9.png Binary files differnew file mode 100644 index 000000000..97d9cd860 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched5.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched6.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched6.9.png Binary files differnew file mode 100644 index 000000000..f9b8a4870 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched6.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched7.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched7.9.png Binary files differnew file mode 100644 index 000000000..bbbb3ad8c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched7.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched8.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched8.9.png Binary files differnew file mode 100644 index 000000000..35f04b547 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched8.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/layout-bounds-only.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/layout-bounds-only.9.png Binary files differnew file mode 100644 index 000000000..6f7047318 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/layout-bounds-only.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/no-patched-interlace.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/no-patched-interlace.png Binary files differnew file mode 100644 index 000000000..a6d0e94af --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/no-patched-interlace.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/no-patched.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/no-patched.png Binary files differnew file mode 100644 index 000000000..742b1e816 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/no-patched.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched-with-badpatches.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched-with-badpatches.9.png Binary files differnew file mode 100644 index 000000000..1a38972cd --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched-with-badpatches.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched1.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched1.9.png Binary files differnew file mode 100644 index 000000000..6a7a62c59 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched1.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched2.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched2.9.png Binary files differnew file mode 100644 index 000000000..11a1a6d12 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched2.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched3.9.png b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched3.9.png Binary files differnew file mode 100644 index 000000000..8d288c903 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched3.9.png diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/jar_example.jar b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/jar_example.jar Binary files differnew file mode 100644 index 000000000..f95b59588 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/jar_example.jar diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/jar_example.jardesc b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/jar_example.jardesc new file mode 100644 index 000000000..14cd44f5a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/jar_example.jardesc @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?> +<jardesc> + <jar path="jar_example.jar"/> + <options buildIfNeeded="true" compress="true" descriptionLocation="/common/tests/data/jar_example.jardesc" exportErrors="false" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/> + <storedRefactorings deprecationInfo="true" structuralOnly="false"/> + <selectedProjects/> + <manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true"> + <sealing sealJar="false"> + <packagesToSeal/> + <packagesToUnSeal/> + </sealing> + </manifest> + <selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false"> + <javaElement handleIdentifier="=common/tests<jar.example"/> + </selectedElements> +</jardesc> diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_attrs.xml b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_attrs.xml new file mode 100644 index 000000000..db77e1749 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_attrs.xml @@ -0,0 +1,340 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ +--> +<resources> + <!-- WARNING !!! THIS IS A MOCK FILE. DO NOT USE FOR DOCUMENTATION PURPOSES. + This file has been trimmed down to only extract a number of interesting cases + for unit tests. + + What this contains: + - View + - ViewGroup + - some attributes which format are defined in Theme + - orientation, gravity and layout_gravity defined before they are used + - ViewGroup_Layout + - ViewGroup_MarginLayout + - LinearLayout + - LinearLayout_Layout + - TableLayout + + Note that TableLayout does not have a TableLayout_Layout definition here + where these is a class TableLayout.LayoutData. + --> + + <!-- These are the standard attributes that make up a complete theme. --> + <declare-styleable name="Theme"> + + <!-- Defines the scrollbars size. --> + <attr name="scrollbarSize" format="dimension" /> + + </declare-styleable> + + + <!-- 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> + + <!-- Specifies how to place an object, both + its x and y axis, within a larger containing object. --> + <attr name="gravity"> + <!-- Push object to the top of its container, not changing its size. --> + <flag name="top" value="0x30" /> + <!-- Push object to the bottom of its container, not changing its size. --> + <flag name="bottom" value="0x50" /> + <!-- Push object to the left of its container, not changing its size. --> + <flag name="left" value="0x03" /> + <!-- Push object to the right of its container, not changing its size. --> + <flag name="right" value="0x05" /> + <!-- Place object in the vertical center of its container, not changing its size. --> + <flag name="center_vertical" value="0x10" /> + <!-- Grow the vertical size of the object if needed so it completely fills its container. --> + <flag name="fill_vertical" value="0x70" /> + <!-- Place object in the horizontal center of its container, not changing its size. --> + <flag name="center_horizontal" value="0x01" /> + <!-- Grow the horizontal size of the object if needed so it completely fills its container. --> + <flag name="fill_horizontal" value="0x07" /> + <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. --> + <flag name="center" value="0x11" /> + <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. --> + <flag name="fill" value="0x77" /> + </attr> + + <!-- Standard gravity constant that a child can supply to its parent. + Defines how to place an object, both + its x and y axis, within a larger containing object. --> + <attr name="layout_gravity"> + <!-- Push object to the top of its container, not changing its size. --> + <flag name="top" value="0x30" /> + <!-- Push object to the bottom of its container, not changing its size. --> + <flag name="bottom" value="0x50" /> + <!-- Push object to the left of its container, not changing its size. --> + <flag name="left" value="0x03" /> + <!-- Push object to the right of its container, not changing its size. --> + <flag name="right" value="0x05" /> + <!-- Place object in the vertical center of its container, not changing its size. --> + <flag name="center_vertical" value="0x10" /> + <!-- Grow the vertical size of the object if needed so it completely fills its container. --> + <flag name="fill_vertical" value="0x70" /> + <!-- Place object in the horizontal center of its container, not changing its size. --> + <flag name="center_horizontal" value="0x01" /> + <!-- Grow the horizontal size of the object if needed so it completely fills its container. --> + <flag name="fill_horizontal" value="0x07" /> + <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. --> + <flag name="center" value="0x11" /> + <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. --> + <flag name="fill" value="0x77" /> + </attr> + + <declare-styleable name="View"> + <!-- NOTE: View does not have a javadoc. Do not place a comment BEFORE View to make sure it + is NOT interpreted as Javadoc --> + + <!-- Supply an identifier name for this view, to later retrieve it + with {@link android.view.View#findViewById View.findViewById()} or + {@link android.app.Activity#findViewById Activity.findViewById()}. + This must be a + resource reference; typically you set this using the + <code>@+</code> syntax to create a new ID resources. + For example: <code>android:id="@+id/my_id"</code> which + allows you to later retrieve the view + with <code>findViewById(R.id.my_id)</code>. --> + <attr name="id" format="reference" /> + + <!-- Supply a tag for this view containing a String, to be retrieved + later with {@link android.view.View#getTag View.getTag()} or + searched for with {@link android.view.View#findViewWithTag + View.findViewWithTag()}. It is generally preferable to use + IDs (through the android:id attribute) instead of tags because + they are faster and allow for compile-time type checking. --> + <attr name="tag" format="string" /> + + <!-- The initial horizontal scroll offset, in pixels.--> + <attr name="scrollX" format="dimension" /> + + <!-- The initial vertical scroll offset, in pixels. --> + <attr name="scrollY" format="dimension" /> + + <!-- A drawable to use as the background. This can be either a reference + to a full drawable resource (such as a PNG image, 9-patch, + XML state list description, etc), or a solid color such as "#ff000000" + (black). --> + <attr name="background" format="reference|color" /> + + <!-- Boolean that controls whether a view can take focus. By default the user can not + move focus to a view; by setting this attribute to true the view is + allowed to take focus. This value does not impact the behavior of + directly calling {@link android.view.View#requestFocus}, which will + always request focus regardless of this view. It only impacts where + focus navigation will try to move focus. --> + <attr name="focusable" format="boolean" /> + + <!-- Sets the circumstances under which this view will take focus. There are + two choices: "weak" or "normal". The default value is "normal" for + any focusable views. The focus type only applies if the view + has been set to be focusable. --> + <attr name="focusType"> + <!-- This view is focusable, but only if none of its descendants are already focused. --> + <enum name="normal" value="0" /> + <!-- This view will always claim to be focusable. --> + <enum name="weak" value="1" /> + </attr> + + <!-- Controls the initial visibility of the view. --> + <attr name="visibility"> + <!-- Visible on screen; the default value. --> + <enum name="visible" value="0" /> + <!-- Not displayed, but taken into account during layout (space is left for it). --> + <enum name="invisible" value="1" /> + <!-- Completely hidden, as if the view had not been added. --> + <enum name="gone" value="2" /> + </attr> + + <!-- Defines which scrollbars should be displayed on scrolling or not. --> + <attr name="scrollbars"> + <!-- No scrollbar is displayed. --> + <flag name="none" value="0x00000000" /> + <!-- Displays horizontal scrollbar only. --> + <flag name="horizontal" value="0x00000100" /> + <!-- Displays vertical scrollbar only. --> + <flag name="vertical" value="0x00000200" /> + </attr> + + <!-- Sets the width of vertical scrollbars and height of horizontal scrollbars. --> + <attr name="scrollbarSize" /> + + <!-- Text to display. (copied from TextView for the extra localization) --> + <attr name="text" format="string" localization="suggested" /> + + </declare-styleable> + + <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any + of its subclasses. Also see {@link #ViewGroup_Layout} for + attributes that this class processes in its children. --> + <declare-styleable name="ViewGroup"> + <!-- Defines whether a child is limited to draw inside of its bounds or not. + This is useful with animations that scale the size of the children to more + than 100% for instance. In such a case, this property should be set to false + to allow the children to draw outside of their bounds. The default value of + this property is true. --> + <attr name="clipChildren" format="boolean" /> + <!-- Defines the layout animation to use the first time the ViewGroup is laid out. + Layout animations can also be started manually after the first layout. --> + <attr name="layoutAnimation" format="reference" /> + <!-- Defines whether a child's animation should be kept when it is over. Keeping + the animations is useful with animation whose final state is different from + the initial state of the View. This is particularly useful with animation + whose fillAfter property is enabled. This property is set to false by default. --> + <attr name="persistentDrawingCache"> + <!-- The drawing cache is not persisted after use. --> + <flag name="none" value="0x0" /> + <!-- The drawing cache is persisted after a layout animation. --> + <flag name="animation" value="0x1" /> + <!-- The drawing cache is persisted after a scroll. --> + <flag name="scrolling" value="0x2" /> + <!-- The drawing cache is always persisted. --> + <flag name="all" value="0x3" /> + </attr> + </declare-styleable> + + <!-- This is the basic set of layout attributes that are common to all + layout managers. These attributes are specified with the rest of + a view's normal attributes (such as {@link android.R.attr#background}, + but will be parsed by the view's parent and ignored by the child. + <p>The values defined here correspond to the base layout attribute + class {@link android.view.ViewGroup.LayoutParams}. --> + <declare-styleable name="ViewGroup_Layout"> + <!-- Specifies the basic width of the view. This is a required attribute + for any view inside of a containing layout manager. Its value may + be a dimension (such as "12dip") for a constant width or one of + the special constants. --> + <attr name="layout_width" format="dimension"> + <!-- The view should be as big as its parent (minus padding). --> + <enum name="match_parent" value="-1" /> + <!-- The view should be only big enough to enclose its content (plus padding). --> + <enum name="wrap_content" value="-2" /> + </attr> + + <!-- Specifies the basic height of the view. This is a required attribute + for any view inside of a containing layout manager. Its value may + be a dimension (such as "12dip") for a constant height or one of + the special constants. --> + <attr name="layout_height" format="dimension"> + <!-- The view should be as big as its parent (minus padding). --> + <enum name="match_parent" value="-1" /> + <!-- The view should be only big enough to enclose its content (plus padding). --> + <enum name="wrap_content" value="-2" /> + </attr> + </declare-styleable> + + <!-- This is the basic set of layout attributes for layout managers that + wish to place margins around their child views. + These attributes are specified with the rest of + a view's normal attributes (such as {@link android.R.attr#background}, + but will be parsed by the view's parent and ignored by the child. + <p>The values defined here correspond to the base layout attribute + class {@link android.view.ViewGroup.MarginLayoutParams}. --> + <declare-styleable name="ViewGroup_MarginLayout"> + <attr name="layout_width" /> + <attr name="layout_height" /> + <!-- Specifies extra space on the left side of this view. + This space is outside this view's bounds. --> + <attr name="layout_marginLeft" format="dimension" /> + <!-- Specifies extra space on the top side of this view. + This space is outside this view's bounds. --> + <attr name="layout_marginTop" format="dimension" /> + <!-- Specifies extra space on the right side of this view. + This space is outside this view's bounds. --> + <attr name="layout_marginRight" format="dimension" /> + <!-- Specifies extra space on the bottom side of this view. + This space is outside this view's bounds. --> + <attr name="layout_marginBottom" format="dimension" /> + </declare-styleable> + + <!-- This is a linear layout. --> + <declare-styleable name="LinearLayout"> + <!-- Should the layout be a column or a row? Use "horizontal" + for a row, "vertical" for a column. The default is + horizontal. --> + <attr name="orientation" /> + <attr name="baselineAligned" format="boolean|reference" /> + <!-- When a linear layout is part of another layout that is baseline + aligned, it can specify which of its children to baseline align to + (i.e which child TextView).--> + <attr name="baselineAlignedChildIndex" format="integer|color" min="0"/> + <!-- Defines the maximum weight sum. If unspecified, the sum is computed + by adding the layout_weight of all of the children. This can be + used for instance to give a single child 50% of the total available + space by giving it a layout_weight of 0.5 and setting the weightSum + to 1.0. --> + <attr name="weightSum" format="float" /> + </declare-styleable> + + <declare-styleable name="LinearLayout_Layout"> + <attr name="layout_width" /> + <attr name="layout_height" /> + <attr name="layout_weight" format="float" /> + <attr name="layout_gravity" /> + </declare-styleable> + + <declare-styleable name="TableLayout"> + <!-- The 0 based index of the columns to stretch. The column indices + must be separated by a comma: 1, 2, 5. Illegal and duplicate + indices are ignored. You can stretch all columns by using the + value "*" instead. Note that a column can be marked stretchable + and shrinkable at the same time. --> + <attr name="stretchColumns" format="string" /> + <!-- The 0 based index of the columns to shrink. The column indices + must be separated by a comma: 1, 2, 5. Illegal and duplicate + indices are ignored. You can shrink all columns by using the + value "*" instead. Note that a column can be marked stretchable + and shrinkable at the same time. --> + <attr name="shrinkColumns" format="string" /> + <!-- The 0 based index of the columns to collapse. The column indices + must be separated by a comma: 1, 2, 5. Illegal and duplicate + indices are ignored. --> + <attr name="collapseColumns" format="string" /> + </declare-styleable> + + <!-- Test for deprecated attributes. --> + <declare-styleable name="DeprecatedTest"> + <!-- Deprecated comments using delimiters. + Ignored. {@deprecated In-line deprecated.} {@ignore Ignored}. + --> + <attr name="deprecated-inline" /> + + <!-- Deprecated comments on their own line. + @deprecated Multi-line version of deprecated + that works till the next tag. + @ignore This tag must be ignored + --> + <attr name="deprecated-multiline" /> + + <!-- This attribute is not deprecated. --> + <attr name="deprecated-not" /> + + <!-- {@deprecated There is no other javadoc here. } --> + <attr name="deprecated-no-javadoc" format="boolean" /> + + </declare-styleable> + +</resources> + diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_manifest_attrs.xml b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_manifest_attrs.xml new file mode 100755 index 000000000..2335d257a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_manifest_attrs.xml @@ -0,0 +1,180 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ +--> +<resources> + <!-- WARNING !!! THIS IS A MOCK FILE. DO NOT USE FOR DOCUMENTATION PURPOSES. + This file has been trimmed down to only extract a number of interesting cases + for unit tests. + + --> + + <!-- **************************************************************** --> + <!-- These are the attributes used in AndroidManifest.xml. --> + <!-- **************************************************************** --> + <eat-comment /> + + <!-- The overall theme to use for an activity. Use with either the + application tag (to supply a default theme for all activities) or + the activity tag (to supply a specific theme for that activity). + + <p>This automatically sets + your activity's Context to use this theme, and may also be used + for "starting" animations prior to the activity being launched (to + better match what the activity actually looks like). It is a reference + to a style resource defining the theme. If not set, the default + system theme will be used. --> + <attr name="theme" format="reference" /> + + <!-- A user-legible name for the given item. Use with the + application tag (to supply a default label for all application + components), or with the activity, receiver, service, or instrumentation + tag (to supply a specific label for that component). It may also be + used with the intent-filter tag to supply a label to show to the + user when an activity is being selected based on a particular Intent. + + <p>The given label will be used wherever the user sees information + about its associated component; for example, as the name of a + main activity that is displayed in the launcher. You should + generally set this to a reference to a string resource, so that + it can be localized, however it is also allowed to supply a plain + string for quick and dirty programming. --> + <attr name="label" format="reference|string" /> + + <!-- A Drawable resource providing a graphical representation of its + associated item. Use with the + application tag (to supply a default icon for all application + components), or with the activity, receiver, service, or instrumentation + tag (to supply a specific icon for that component). It may also be + used with the intent-filter tag to supply an icon to show to the + user when an activity is being selected based on a particular Intent. + + <p>The given icon will be used to display to the user a graphical + representation of its associated component; for example, as the icon + for main activity that is displayed in the launcher. This must be + a reference to a Drawable resource containing the image definition. --> + <attr name="icon" format="reference" /> + + <!-- A unique name for the given item. This must use a Java-style naming + convention to ensure the name is unique, for example + "com.mycompany.MyName". --> + <attr name="name" format="string" /> + + <!-- Internal version code. This is the number used to determine whether + one version is more recent than another: it has no other meaning than + that higher numbers are more recent. You could use this number to + encode a "x.y" in the lower and upper 16 bits, make it a build + number, simply increase it by one each time a new version is + released, or define it however else you want, as long as each + successive version has a higher number. This is not a version + number generally shown to the user, that is usually supplied + with {@link android.R.attr#versionName}. --> + <attr name="versionCode" format="integer" /> + + <!-- The text shown to the user to indicate the version they have. This + is used for no other purpose than display to the user; the actual + significant version number is given by {@link android.R.attr#versionCode}. --> + <attr name="versionName" format="string" /> + + <!-- .............. --> + + <!-- The <code>manifest</code> tag is the root of an + <code>AndroidManifest.xml</code> file, + describing the contents of an Android package (.apk) file. One + attribute must always be supplied: <code>package</code> gives a + unique name for the package, using a Java-style naming convention + to avoid name collisions. For example, applications published + by Google could have names of the form + <code>com.google.app.<em>appname</em></code> --> + <declare-styleable name="AndroidManifest"> + <attr name="versionCode" /> + <attr name="versionName" /> + </declare-styleable> + + <!-- The <code>application</code> tag describes application-level components + contained in the package, as well as general application + attributes. Many of the attributes you can supply here (such + as theme, label, icon, permission, process, taskAffinity, + and allowTaskReparenting) serve + as default values for the corresponding attributes of components + declared inside of the application. + + <p>Inside of this element you specify what the application contains, + using the elements {@link #AndroidManifestProvider provider}, + {@link #AndroidManifestService service}, + {@link #AndroidManifestReceiver receiver}, + {@link #AndroidManifestActivity activity}, + {@link #AndroidManifestActivityAlias activity-alias}, and + {@link #AndroidManifestUsesLibrary uses-library}. The application tag + appears as a child of the root {@link #AndroidManifest manifest} tag. --> + <declare-styleable name="AndroidManifestApplication" parent="AndroidManifest"> + <attr name="name" /> + <attr name="theme" /> + <attr name="label" /> + <attr name="icon" /> + <attr name="cantSaveState" format="boolean" /> + </declare-styleable> + + <!-- The <code>permission</code> tag declares a security permission that can be + used to control access from other packages to specific components or + features in your package (or other packages). See the + <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> + document for more information on permissions. + + <p>This appears as a child tag of the root + {@link #AndroidManifest manifest} tag. --> + <declare-styleable name="AndroidManifestPermission" parent="AndroidManifest"> + <!-- Required public name of the permission, which other components and + packages will use when referring to this permission. This is a string using + Java-style scoping to ensure it is unique. The prefix will often + be the same as our overall package name, for example + "com.mycompany.android.myapp.SomePermission". --> + <attr name="name" /> + <attr name="label" /> + <attr name="icon" /> + </declare-styleable> + + + <!-- The <code>activity-alias</code> tag declares a new + name for an existing {@link #AndroidManifestActivity activity} + tag. + + <p>Zero or more {@link #AndroidManifestIntentFilter intent-filter} + tags can be included inside of an activity-alias, to specify the Intents + that it can handle. If none are specified, the activity can + only be started through direct specification of its class name. + The activity-alias tag appears as a child tag of the + {@link #AndroidManifestApplication application} tag. --> + <declare-styleable name="AndroidManifestActivityAlias" parent="AndroidManifestApplication"> + <!-- Required name of the class implementing the activity, deriving from + {@link android.app.Activity}. This is a fully + qualified class name (for example, com.mycompany.myapp.MyActivity); as a + short-hand if the first character of the class + is a period then it is appended to your package name. --> + <attr name="name" /> + <attr name="label" /> + <attr name="icon" /> + </declare-styleable> + + <declare-styleable name="AndroidManifestNewParentNewElement" + parent="AndroidManifest.AndroidManifestNewParent"> + <attr name="name" /> + <attr name="label" /> + <attr name="icon" /> + </declare-styleable> + +</resources> diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/view/View.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/view/View.java new file mode 100644 index 000000000..a80a98daf --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/view/View.java @@ -0,0 +1,21 @@ +/* + * 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 mock_android.view; + +public class View { + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/view/ViewGroup.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/view/ViewGroup.java new file mode 100644 index 000000000..466470fc1 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/view/ViewGroup.java @@ -0,0 +1,29 @@ +/* + * 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 mock_android.view; + +public class ViewGroup extends View { + + public class MarginLayoutParams extends LayoutParams { + + } + + public class LayoutParams { + + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/widget/LinearLayout.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/widget/LinearLayout.java new file mode 100644 index 000000000..3870a63d9 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/widget/LinearLayout.java @@ -0,0 +1,27 @@ +/* + * 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 mock_android.widget; + +import mock_android.view.ViewGroup; + +public class LinearLayout extends ViewGroup { + + public class LayoutParams extends mock_android.view.ViewGroup.LayoutParams { + + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/widget/TableLayout.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/widget/TableLayout.java new file mode 100644 index 000000000..e455e7d61 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/mock_android/widget/TableLayout.java @@ -0,0 +1,27 @@ +/* + * 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 mock_android.widget; + +import mock_android.view.ViewGroup; + +public class TableLayout extends ViewGroup { + + public class LayoutParams extends MarginLayoutParams { + + } + +} |