LayoutTestBase.java revision cb3758e42389a933203262f9109454c091574d30
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.eclipse.org/org/documents/epl-v10.php
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package com.android.ide.common.layout;
19import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
20import static com.android.util.XmlUtils.ANDROID_URI;
21
22import com.android.annotations.NonNull;
23import com.android.annotations.Nullable;
24import com.android.ide.common.api.DropFeedback;
25import com.android.ide.common.api.IClientRulesEngine;
26import com.android.ide.common.api.IDragElement;
27import com.android.ide.common.api.INode;
28import com.android.ide.common.api.IValidator;
29import com.android.ide.common.api.IViewMetadata;
30import com.android.ide.common.api.IViewRule;
31import com.android.ide.common.api.Margins;
32import com.android.ide.common.api.Point;
33import com.android.ide.common.api.Rect;
34import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
35
36import java.util.ArrayList;
37import java.util.Collection;
38import java.util.Collections;
39import java.util.List;
40import java.util.Map;
41
42import junit.framework.TestCase;
43
44/**
45 * Common layout helpers from LayoutRule tests
46 */
47@SuppressWarnings("javadoc")
48public class LayoutTestBase extends TestCase {
49    /**
50     * Helper function used by tests to drag a button into a canvas containing
51     * the given children.
52     *
53     * @param rule The rule to test on
54     * @param targetNode The target layout node to drag into
55     * @param dragBounds The (original) bounds of the dragged item
56     * @param dropPoint The drag point we should drag to and drop
57     * @param secondDropPoint An optional second drag point to drag to before
58     *            drawing graphics and dropping (or null if not applicable)
59     * @param insertIndex The expected insert position we end up with after
60     *            dropping at the dropPoint
61     * @param currentIndex If the dragged widget is already in the canvas this
62     *            should be its child index; if not, pass in -1
63     * @param graphicsFragments This is a varargs array of String fragments
64     *            we expect to see in the graphics output on the drag over
65     *            event.
66     * @return The inserted node
67     */
68    protected INode dragInto(IViewRule rule, INode targetNode, Rect dragBounds, Point dropPoint,
69            Point secondDropPoint, int insertIndex, int currentIndex,
70            String... graphicsFragments) {
71
72        String draggedButtonId = (currentIndex == -1) ? "@+id/DraggedButton" : targetNode
73                .getChildren()[currentIndex].getStringAttr(ANDROID_URI, ATTR_ID);
74
75        IDragElement[] elements = TestDragElement.create(TestDragElement.create(
76                "android.widget.Button", dragBounds).id(draggedButtonId));
77
78        // Enter target
79        DropFeedback feedback = rule.onDropEnter(targetNode, null/*targetView*/, elements);
80        assertNotNull(feedback);
81        assertFalse(feedback.invalidTarget);
82        assertNotNull(feedback.painter);
83
84        if (currentIndex != -1) {
85            feedback.sameCanvas = true;
86        }
87
88        // Move near top left corner of the target
89        feedback = rule.onDropMove(targetNode, elements, feedback, dropPoint);
90        assertNotNull(feedback);
91
92        if (secondDropPoint != null) {
93            feedback = rule.onDropMove(targetNode, elements, feedback, secondDropPoint);
94            assertNotNull(feedback);
95        }
96
97        if (insertIndex == -1) {
98            assertTrue(feedback.invalidTarget);
99        } else {
100            assertFalse(feedback.invalidTarget);
101        }
102
103        // Paint feedback and make sure it's what we expect
104        TestGraphics graphics = new TestGraphics();
105        assertNotNull(feedback.painter);
106        feedback.painter.paint(graphics, targetNode, feedback);
107        String drawn = graphics.getDrawn().toString();
108
109        // Check that each graphics fragment is drawn
110        for (String fragment : graphicsFragments) {
111            if (!drawn.contains(fragment)) {
112                // Get drawn-output since unit test truncates message in below
113                // contains-assertion
114                System.out.println("Could not find: " + fragment);
115                System.out.println("Full graphics output: " + drawn);
116            }
117            assertTrue(fragment + " not found; full=" + drawn, drawn.contains(fragment));
118        }
119
120        // Attempt a drop?
121        if (insertIndex == -1) {
122            // No, not expected to succeed (for example, when drop point is over an
123            // invalid region in RelativeLayout) - just return.
124            return null;
125        }
126        int childrenCountBefore = targetNode.getChildren().length;
127        rule.onDropped(targetNode, elements, feedback, dropPoint);
128
129        if (currentIndex == -1) {
130            // Inserting new from outside
131            assertEquals(childrenCountBefore+1, targetNode.getChildren().length);
132        } else {
133            // Moving from existing; must remove in old position first
134            ((TestNode) targetNode).removeChild(currentIndex);
135
136            assertEquals(childrenCountBefore, targetNode.getChildren().length);
137        }
138        // Ensure that it's inserted in the right place
139        String actualId = targetNode.getChildren()[insertIndex].getStringAttr(
140                ANDROID_URI, ATTR_ID);
141        if (!draggedButtonId.equals(actualId)) {
142            // Using assertEquals instead of fail to get nice diff view on test
143            // failure
144            List<String> childrenIds = new ArrayList<String>();
145            for (INode child : targetNode.getChildren()) {
146                childrenIds.add(child.getStringAttr(ANDROID_URI, ATTR_ID));
147            }
148            int index = childrenIds.indexOf(draggedButtonId);
149            String message = "Button found at index " + index + " instead of " + insertIndex
150                    + " among " + childrenIds;
151            System.out.println(message);
152            assertEquals(message, draggedButtonId, actualId);
153        }
154
155
156        return targetNode.getChildren()[insertIndex];
157    }
158
159    /**
160     * Utility method for asserting that two collections contain exactly the
161     * same elements (regardless of order)
162     * @param expected expected collection
163     * @param actual  actual collection
164     */
165    public static void assertContainsSame(Collection<String> expected, Collection<String> actual) {
166        if (expected.size() != actual.size()) {
167            fail("Collection sizes differ; expected " + expected.size() + " but was "
168                    + actual.size());
169        }
170
171        // Sort prior to comparison to ensure we have the same elements
172        // regardless of order
173        List<String> expectedList = new ArrayList<String>(expected);
174        Collections.sort(expectedList);
175        List<String> actualList = new ArrayList<String>(actual);
176        Collections.sort(actualList);
177        // Instead of just assertEquals(expectedList, actualList);
178        // we iterate one element at a time so we can show the first
179        // -difference-.
180        for (int i = 0; i < expectedList.size(); i++) {
181            String expectedElement = expectedList.get(i);
182            String actualElement = actualList.get(i);
183            if (!expectedElement.equals(actualElement)) {
184                System.out.println("Expected items: " + expectedList);
185                System.out.println("Actual items  : " + actualList);
186            }
187            assertEquals("Collections differ; first difference:", expectedElement, actualElement);
188        }
189    }
190
191    protected void initialize(IViewRule rule, String fqn) {
192        rule.onInitialize(fqn, new TestRulesEngine(fqn));
193    }
194
195    private static class TestRulesEngine implements IClientRulesEngine {
196        private final String mFqn;
197
198        protected TestRulesEngine(String fqn) {
199            mFqn = fqn;
200        }
201
202        @Override
203        public void debugPrintf(@NonNull String msg, Object... params) {
204            fail("Not supported in tests yet");
205        }
206
207        @Override
208        public void displayAlert(@NonNull String message) {
209            fail("Not supported in tests yet");
210        }
211
212        @Override
213        public String displayInput(@NonNull String message, @Nullable String value,
214                @Nullable IValidator filter) {
215            fail("Not supported in tests yet");
216            return null;
217        }
218
219        @Override
220        public @NonNull String getFqcn() {
221            return mFqn;
222        }
223
224        @Override
225        public @NonNull IViewMetadata getMetadata(final @NonNull String fqcn) {
226            return new IViewMetadata() {
227                @Override
228                public @NonNull String getDisplayName() {
229                    // This also works when there is no "."
230                    return fqcn.substring(fqcn.lastIndexOf('.') + 1);
231                }
232
233                @Override
234                public @NonNull FillPreference getFillPreference() {
235                    return ViewMetadataRepository.get().getFillPreference(fqcn);
236                }
237
238                @Override
239                public @NonNull Margins getInsets() {
240                    return null;
241                }
242
243                @Override
244                public @NonNull List<String> getTopAttributes() {
245                    return ViewMetadataRepository.get().getTopAttributes(fqcn);
246                }
247            };
248        }
249
250        @Override
251        public int getMinApiLevel() {
252            return 8;
253        }
254
255        @Override
256        public IViewRule loadRule(@NonNull String fqcn) {
257            fail("Not supported in tests yet");
258            return null;
259        }
260
261        @Override
262        public String displayReferenceInput(String currentValue) {
263            fail("Not supported in tests yet");
264            return null;
265        }
266
267        @Override
268        public IValidator getResourceValidator(String resourceTypeName, boolean uniqueInProject,
269                boolean uniqueInLayout, boolean exists, String... allowed) {
270            fail("Not supported in tests yet");
271            return null;
272        }
273
274        @Override
275        public String displayResourceInput(@NonNull String resourceTypeName,
276                @Nullable String currentValue) {
277            fail("Not supported in tests yet");
278            return null;
279        }
280
281        @Override
282        public String[] displayMarginInput(@Nullable String all, @Nullable String left,
283                @Nullable String right, @Nullable String top, @Nullable String bottom) {
284            fail("Not supported in tests yet");
285            return null;
286        }
287
288        @Override
289        public String displayIncludeSourceInput() {
290            fail("Not supported in tests yet");
291            return null;
292        }
293
294        @Override
295        public void select(@NonNull Collection<INode> nodes) {
296            fail("Not supported in tests yet");
297        }
298
299        @Override
300        public String displayFragmentSourceInput() {
301            fail("Not supported in tests yet");
302            return null;
303        }
304
305        @Override
306        public void layout() {
307            fail("Not supported in tests yet");
308        }
309
310        @Override
311        public void redraw() {
312            fail("Not supported in tests yet");
313        }
314
315        @Override
316        public Map<INode, Rect> measureChildren(@NonNull INode parent,
317                @Nullable AttributeFilter filter) {
318            return null;
319        }
320
321        @Override
322        public int pxToDp(int px) {
323            fail("Not supported in tests yet");
324            return px;
325        }
326
327        @Override
328        public @NonNull String getUniqueId(@NonNull String prefix) {
329            fail("Not supported in tests yet");
330            return null;
331        }
332
333        @Override
334        public int screenToLayout(int pixels) {
335            fail("Not supported in tests yet");
336            return 0;
337        }
338
339        @Override
340        public int dpToPx(int dp) {
341            fail("Not supported in tests yet");
342            return 0;
343        }
344
345        @Override
346        public @NonNull String getAppNameSpace() {
347            fail("Not supported in tests yet");
348            return null;
349        }
350    }
351
352    public void testDummy() {
353        // To avoid JUnit warning that this class contains no tests, even though
354        // this is an abstract class and JUnit shouldn't try
355    }
356}
357