aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android')
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/PointTest.java123
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java301
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java77
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseLayoutRuleTest.java245
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/BaseViewRuleTest.java42
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/FrameLayoutRuleTest.java63
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/GravityHelperTest.java127
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/GridLayoutRuleTest.java23
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LayoutTestBase.java375
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/LinearLayoutRuleTest.java466
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/RelativeLayoutRuleTest.java167
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttribute.java61
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestAttributeInfo.java81
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestColor.java36
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestDragElement.java158
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestGraphics.java181
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/TestNode.java477
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/ZoomControlsRuleTest.java50
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/grid/GridModelTest.java852
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/relative/DeletionHandlerTest.java445
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttributeInfoTest.java382
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttrsXmlParserManifestTest.java94
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/resources/platform/AttrsXmlParserTest.java120
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtPluginTest.java49
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java127
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/build/BaseBuilderTest.java37
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/AndroidDoubleClickStrategyTest.java71
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtilsTest.java297
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/draw9patch/graphics/GraphicsUtilitiesTest.java313
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/draw9patch/graphics/NinePatchedImageTest.java914
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategyTest.java335
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/ExplodeRenderingHelperTest.java256
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParserTest.java256
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationTest.java143
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/FlagManagerTest.java185
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LocaleTest.java99
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfoTest.java797
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ControlPointTest.java73
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DomUtilitiesTest.java206
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ImageUtilsTest.java368
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeFinderTest.java128
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/IncludeOverlayTest.java75
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutPointTest.java64
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PointTestCases.java107
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/RenderLoggerTest.java82
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionManagerTest.java120
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleAttributeTest.java109
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SimpleElementTest.java343
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java343
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/MockNodeProxy.java68
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactoryTest.java123
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngineTest.java37
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadataRepositoryTest.java77
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/properties/ValueCompleterTest.java151
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/manifest/model/UiElementNodeTest.java284
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockNamedNodeMap.java112
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockNodeList.java62
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/mock/MockXmlNode.java319
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/resources/manager/ConfigMatchTest.java297
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/project/ProjectHelperTest.java66
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeMoveParticipantTest.java135
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/AndroidTypeRenameParticipantTest.java160
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/refactorings/core/RenameResourceXmlTextActionTest.java110
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java183
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java101
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContextTest.java22
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/sdk/AndroidJarLoaderTest.java166
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/sdk/LayoutParamsParserTest.java182
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmActivityToLayoutMethodTest.java50
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmCamelCaseToUnderscoreMethodTest.java58
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmClassNameToResourceMethodTest.java66
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlAttributeMethodTest.java50
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlStringMethodTest.java66
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmEscapeXmlTextMethodTest.java46
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmExtractLettersMethodTest.java46
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmLayoutToActivityMethodTest.java54
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmSlashedPackageNameMethodTest.java50
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/wizards/templates/FmUnderscoreToCamelCaseMethodTest.java58
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/Mocks.java189
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/TestLogger.java54
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area-one-dot.9.pngbin0 -> 206 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area-two-dots.9.pngbin0 -> 214 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/content-area.9.pngbin0 -> 210 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched1.9.pngbin0 -> 2923 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched2.9.pngbin0 -> 2913 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched3.9.pngbin0 -> 2914 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched4.9.pngbin0 -> 2914 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched5.9.pngbin0 -> 2929 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched6.9.pngbin0 -> 2920 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched7.9.pngbin0 -> 2916 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/invalid-patched8.9.pngbin0 -> 2923 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/layout-bounds-only.9.pngbin0 -> 1466 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/no-patched-interlace.pngbin0 -> 3138 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/no-patched.pngbin0 -> 2906 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched-with-badpatches.9.pngbin0 -> 214 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched1.9.pngbin0 -> 210 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched2.9.pngbin0 -> 217 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/draw9patch/patched3.9.pngbin0 -> 214 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/jar_example.jarbin0 -> 1829 bytes
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/jar_example.jardesc16
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_attrs.xml340
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/testdata/mock_manifest_attrs.xml180
102 files changed, 14821 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 &lt;permission&gt;}"));
+
+ 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 &lt;permission&gt;}",
+ 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("<\"'>&", "&lt;&quot;&apos;>&amp;");
+ }
+
+ 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 &amp; 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("<\"'>&", "&lt;\"'>&amp;");
+ }
+}
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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
new 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
Binary files differ
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&lt;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>