/* * Copyright (C) 2014 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.intellij.android.designer.model; import com.android.SdkConstants; import com.intellij.android.designer.designSurface.AndroidDesignerEditorPanel; import com.intellij.android.designer.designSurface.LayoutEditorTestBase; import com.intellij.android.designer.designSurface.RootView; import com.intellij.designer.model.MetaManager; import com.intellij.designer.model.MetaModel; import com.intellij.designer.model.RadComponent; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.psi.XmlElementFactory; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import org.jetbrains.annotations.NotNull; import java.awt.*; import java.util.Arrays; /** * Functional tests for {@linkplain com.intellij.android.designer.model.RadComponentOperations} */ public class RadComponentOperationsTest extends LayoutEditorTestBase { private MetaManager myMetaManager; private XmlElementFactory myXmlElementFactory; @Override public void setUp() throws Exception { super.setUp(); myMetaManager = ViewsMetaManager.getInstance(getProject()); myXmlElementFactory = XmlElementFactory.getInstance(getProject()); } private static RadViewComponent getLayoutRoot(RadViewComponent modelRoot) { return (RadViewComponent)modelRoot.getChildren().get(0); } public void testSimpleConstruction() throws Exception { AndroidDesignerEditorPanel editor = createLayoutEditor(getTestFile("simple.xml")); @SuppressWarnings("ConstantConditions") RadViewComponent rootComponent = getLayoutRoot(editor.getRootViewComponent()); assertEquals(0, rootComponent.getChildren().size()); assertEquals("LinearLayout", rootComponent.getTag().getName()); assertEquals("fill_parent", rootComponent.getAttribute("android:layout_width", null)); assertEquals("fill_parent", rootComponent.getAttribute("android:layout_height", null)); } public void testMediumConstruction() throws Exception { AndroidDesignerEditorPanel editor = createLayoutEditor(getTestFile("layout.xml")); @SuppressWarnings("ConstantConditions") RadViewComponent rootComponent = getLayoutRoot(editor.getRootViewComponent()); assertEquals("LinearLayout(" + "LinearLayout(ImageView, View, ImageView, ImageButton, ProgressBar, ImageView, ImageButton, ), " + // RequestFocus' not in the model //"LinearLayout(EditText, EditText(requestFocus, ), ), " + "LinearLayout(EditText, EditText, ), " + "LinearLayout(Button, Button, ), " + "), ", toString(rootComponent)); assertEquals("vertical", rootComponent.getAttribute("android:orientation", null)); RadComponent lastLayoutChild = rootComponent.getChildren().get(2); RadComponent shouldBeAButton = lastLayoutChild.getChildren().get(0); RadViewComponent buttonView = (RadViewComponent)shouldBeAButton; assertEquals("Button", buttonView.getTag().getName()); assertEquals("0dip", buttonView.getAttribute("android:layout_width", null)); // Depends on render: //assertEquals(0, buttonView.getPaddedBounds().width); assertEquals("wrap_content", buttonView.getAttribute("android:layout_height", null)); // These actual coordinates may be brittle (depend on bounds of widgets?) so we may need to just assert list.size() == 2 instead // if this breaks on some platforms RootView rootView = editor.getCurrentRootView(); assertEquals(Arrays.asList(new Rectangle(754, 100, 14, 14), new Rectangle(754, 100, 14, 14)), rootView.getEmptyRegions()); } /** * Get a string representation of the given RadComponent tree */ private static String toString(@NotNull RadViewComponent root) { StringBuilder sb = new StringBuilder(); sb.append(root.getTag().getName()); if (!root.getChildren().isEmpty()) { sb.append('('); for (RadComponent child : root.getChildren()) { sb.append(toString((RadViewComponent)child)); } sb.append(')'); } sb.append(", "); return sb.toString(); } /** * Get a string representation of the given RadComponent tree with bounds. * Each node is of the form NodeName[x, y, width, height](Children...), */ private static String toBoundsString(RadViewComponent root, Component relativeto) { StringBuilder sb = new StringBuilder(); sb.append(root.getTag().getName()); sb.append(root.getBounds(relativeto).toString().substring(Rectangle.class.getName().length())); if (!root.getChildren().isEmpty()) { sb.append('('); for (RadComponent child : root.getChildren()) { sb.append(toBoundsString((RadViewComponent)child, relativeto)); } sb.append(')'); } sb.append(", "); return sb.toString(); } public void testCreateAndAddSpace() throws Exception { AndroidDesignerEditorPanel editor = createLayoutEditor(getTestFile("simple.xml")); @SuppressWarnings("ConstantConditions") final RadViewComponent rootComponent = getLayoutRoot(editor.getRootViewComponent()); XmlFile psiFile = editor.getXmlFile(); MetaModel spaceModel = myMetaManager.getModelByTag(SdkConstants.SPACE); assertNotNull(spaceModel); XmlTag spaceTag = myXmlElementFactory.createTagFromText(spaceModel.getCreation()); assertNotNull(spaceTag); final RadViewComponent spaceComponent = RadComponentOperations.createComponent(spaceTag, spaceModel); assertNotNull(spaceComponent); assertNotNull(spaceComponent.getTag()); assertEquals("Space", spaceComponent.getTag().getName()); (new WriteCommandAction<Void>(getProject(), "Add Tag", psiFile) { @Override protected void run(@NotNull Result<Void> result) throws Throwable { RadComponentOperations.addComponent(rootComponent, spaceComponent, null /* add before */); } }).execute(); assertEquals("LinearLayout(Space, ), ", toString(rootComponent)); assertEquals("<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + " android:layout_width=\"fill_parent\"\n" + " android:layout_height=\"fill_parent\">\n" + " <Space\n" + " android:layout_width=\"20px\"\n" + " android:layout_height=\"20px\"\n" + " />\n" + "</LinearLayout>", rootComponent.getTag().getText()); } public void testMoveComponent() throws Exception { } public void testAddComponent() throws Exception { } public void testPasteComponent() throws Exception { } public void testAddComponentTag() throws Exception { } public void testCheckTag() throws Exception { } public void testDeleteAttribute() throws Exception { } public void testUpdateRootComponent() throws Exception { AndroidDesignerEditorPanel editor = createLayoutEditor(getTestFile("simple.xml")); @SuppressWarnings("ConstantConditions") final RadViewComponent rootComponent = editor.getRootViewComponent(); XmlFile psiFile = editor.getXmlFile(); assertNotNull(psiFile); assertNotNull(rootComponent); assertEquals("LinearLayout(LinearLayout, ), ", toString(rootComponent)); RootView rootView = editor.getCurrentRootView(); assertEquals("LinearLayout[x=0,y=0,width=768,height=1280](LinearLayout[x=0,y=100,width=768,height=1084], ), ", toBoundsString(rootComponent, rootView)); assertEquals("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + " android:layout_width=\"fill_parent\"\n" + " android:layout_height=\"fill_parent\">\n" + "</LinearLayout>\n", psiFile.getText()); // Add a button MetaModel buttonModel = myMetaManager.getModelByTag("Button"); assertNotNull(buttonModel); editor.requestImmediateRender(); final RadViewComponent newRoot = editor.getRootViewComponent(); final RadViewComponent layoutRoot = getLayoutRoot(newRoot); final RadViewComponent buttonComponent = RadComponentOperations.createComponent(null, buttonModel); assertNotNull(buttonComponent); (new WriteCommandAction<Void>(getProject(), "Add Tag", psiFile) { @Override protected void run(@NotNull Result<Void> result) throws Throwable { RadComponentOperations.addComponent(layoutRoot, buttonComponent, null /* add before */); } }).execute(); assertNotNull(buttonComponent.getTag()); assertEquals("Button", buttonComponent.getTag().getName()); // Ensure tag was properly added assertEquals("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + " android:layout_width=\"fill_parent\"\n" + " android:layout_height=\"fill_parent\">\n" + " <Button\n" + " android:layout_width=\"wrap_content\"\n" + " android:layout_height=\"wrap_content\"\n" + " android:text=\"New Button\"\n" + " android:id=\"@+id/button\"/>\n" + "</LinearLayout>\n", psiFile.getText()); assertEquals("LinearLayout(LinearLayout(Button, ), ), ", toString(newRoot)); editor.requestImmediateRender(); final RadViewComponent root = editor.getRootViewComponent(); assertNotNull(root); assertEquals("LinearLayout[x=0,y=0,width=768,height=1280](" + "LinearLayout[x=0,y=100,width=768,height=1084](Button[x=0,y=100,width=191,height=96], ), ), ", toBoundsString(root, rootView)); } }