1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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 package com.android.layoutlib.bridge.impl;
18 
19 import org.junit.Test;
20 import org.kxml2.io.KXmlParser;
21 import org.xmlpull.v1.XmlPullParser;
22 import org.xmlpull.v1.XmlPullParserException;
23 
24 import java.io.StringReader;
25 
26 import static com.android.SdkConstants.NS_RESOURCES;
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertNotSame;
29 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
30 import static org.xmlpull.v1.XmlPullParser.END_TAG;
31 import static org.xmlpull.v1.XmlPullParser.START_TAG;
32 
33 
34 public class LayoutParserWrapperTest {
35     @Test
36     @SuppressWarnings("StatementWithEmptyBody")  // some for loops need to be empty statements.
testDataBindingLayout()37     public void testDataBindingLayout() throws Exception {
38         LayoutParserWrapper parser = getParserFromString(sDataBindingLayout);
39         parser.peekTillLayoutStart();
40         assertEquals("Expected START_TAG", START_TAG, parser.next());
41         assertEquals("RelativeLayout", parser.getName());
42         for (int next = parser.next(); next != START_TAG && next != END_DOCUMENT;
43              next = parser.next());
44         assertEquals("Expected START_TAG", START_TAG, parser.getEventType());
45         assertEquals("TextView", parser.getName());
46         assertEquals("layout_width incorrect for first text view.", "wrap_content",
47                 parser.getAttributeValue(NS_RESOURCES, "layout_width"));
48         // Ensure that data-binding part is stripped.
49         assertEquals("Bound attribute android:text incorrect", "World",
50                 parser.getAttributeValue(NS_RESOURCES, "text"));
51         assertEquals("resource attribute 'id' for first text view incorrect.", "@+id/first",
52                 parser.getAttributeValue(NS_RESOURCES, "id"));
53         for (int next = parser.next();
54              (next != END_TAG || !"RelativeLayout".equals(parser.getName())) && next != END_DOCUMENT;
55              next = parser.next());
56         assertNotSame("Unexpected end of document", END_DOCUMENT, parser.getEventType());
57         assertEquals("Document didn't end when expected.", END_DOCUMENT, parser.next());
58     }
59 
60     @Test
61     @SuppressWarnings("StatementWithEmptyBody")
testNonDataBindingLayout()62     public void testNonDataBindingLayout() throws Exception {
63         LayoutParserWrapper parser = getParserFromString(sNonDataBindingLayout);
64         parser.peekTillLayoutStart();
65         assertEquals("Expected START_TAG", START_TAG, parser.next());
66         assertEquals("RelativeLayout", parser.getName());
67         for (int next = parser.next(); next != START_TAG && next != END_DOCUMENT;
68              next = parser.next());
69         assertEquals("Expected START_TAG", START_TAG, parser.getEventType());
70         assertEquals("TextView", parser.getName());
71         assertEquals("layout_width incorrect for first text view.", "wrap_content",
72                 parser.getAttributeValue(NS_RESOURCES, "layout_width"));
73         // Ensure that value isn't modified.
74         assertEquals("Bound attribute android:text incorrect", "@{user.firstName,default=World}",
75                 parser.getAttributeValue(NS_RESOURCES, "text"));
76         assertEquals("resource attribute 'id' for first text view incorrect.", "@+id/first",
77                 parser.getAttributeValue(NS_RESOURCES, "id"));
78         for (int next = parser.next();
79              (next != END_TAG || !"RelativeLayout".equals(parser.getName())) && next != END_DOCUMENT;
80              next = parser.next());
81         assertNotSame("Unexpected end of document", END_DOCUMENT, parser.getEventType());
82         assertEquals("Document didn't end when expected.", END_DOCUMENT, parser.next());
83     }
84 
getParserFromString(String layoutContent)85     private static LayoutParserWrapper getParserFromString(String layoutContent) throws
86             XmlPullParserException {
87         XmlPullParser parser = new KXmlParser();
88         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
89         parser.setInput(new StringReader(layoutContent));
90         return new LayoutParserWrapper(parser);
91     }
92 
93     private static final String sDataBindingLayout =
94             //language=XML
95             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
96                     "<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
97                     "        xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n" +
98                     "        xmlns:tools=\"http://schemas.android.com/tools\"\n" +
99                     "        tools:context=\".MainActivity\"\n" +
100                     "        tools:showIn=\"@layout/activity_main\">\n" +
101                     "\n" +
102                     "    <data>\n" +
103                     "\n" +
104                     "        <variable\n" +
105                     "            name=\"user\"\n" +
106                     "            type=\"com.example.User\" />\n" +
107                     "        <variable\n" +
108                     "            name=\"activity\"\n" +
109                     "            type=\"com.example.MainActivity\" />\n" +
110                     "    </data>\n" +
111                     "\n" +
112                     "    <RelativeLayout\n" +
113                     "        android:layout_width=\"match_parent\"\n" +
114                     "        android:layout_height=\"match_parent\"\n" +
115                     "        android:paddingBottom=\"@dimen/activity_vertical_margin\"\n" +
116                     "        android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" +
117                     "        android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" +
118                     "        android:paddingTop=\"@dimen/activity_vertical_margin\"\n" +
119                     "        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n" +
120                     "    >\n" +
121                     "\n" +
122                     "        <TextView\n" +
123                     "            android:id=\"@+id/first\"\n" +
124                     "            android:layout_width=\"wrap_content\"\n" +
125                     "            android:layout_alignParentStart=\"true\"\n" +
126                     "            android:layout_alignParentLeft=\"true\"\n" +
127                     "            android:layout_height=\"wrap_content\"\n" +
128                     "            android:text=\"@{user.firstName,default=World}\" />\n" +
129                     "\n" +
130                     "        <TextView\n" +
131                     "            android:id=\"@+id/last\"\n" +
132                     "            android:layout_width=\"wrap_content\"\n" +
133                     "            android:layout_height=\"wrap_content\"\n" +
134                     "            android:layout_toEndOf=\"@id/first\"\n" +
135                     "            android:layout_toRightOf=\"@id/first\"\n" +
136                     "            android:text=\"@{user.lastName,default=Hello}\" />\n" +
137                     "\n" +
138                     "        <Button\n" +
139                     "            android:layout_width=\"wrap_content\"\n" +
140                     "            android:layout_height=\"wrap_content\"\n" +
141                     "            android:layout_below=\"@id/last\"\n" +
142                     "            android:text=\"Submit\"\n" +
143                     "            android:onClick=\"@{activity.onClick}\"/>\n" +
144                     "    </RelativeLayout>\n" +
145                     "</layout>";
146 
147     private static final String sNonDataBindingLayout =
148             //language=XML
149             "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
150                     "    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n" +
151                     "    android:layout_width=\"match_parent\"\n" +
152                     "    android:layout_height=\"match_parent\"\n" +
153                     "    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n" +
154                     "    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" +
155                     "    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" +
156                     "    android:paddingTop=\"@dimen/activity_vertical_margin\"\n" +
157                     "    app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n" +
158                     ">\n" +
159                     "\n" +
160                     "    <TextView\n" +
161                     "        android:id=\"@+id/first\"\n" +
162                     "        android:layout_width=\"wrap_content\"\n" +
163                     "        android:layout_alignParentStart=\"true\"\n" +
164                     "        android:layout_alignParentLeft=\"true\"\n" +
165                     "        android:layout_height=\"wrap_content\"\n" +
166                     "        android:text=\"@{user.firstName,default=World}\" />\n" +
167                     "\n" +
168                     "    <TextView\n" +
169                     "        android:id=\"@+id/last\"\n" +
170                     "        android:layout_width=\"wrap_content\"\n" +
171                     "        android:layout_height=\"wrap_content\"\n" +
172                     "        android:layout_toEndOf=\"@id/first\"\n" +
173                     "        android:layout_toRightOf=\"@id/first\"\n" +
174                     "        android:text=\"@{user.lastName,default=Hello}\" />\n" +
175                     "\n" +
176                     "    <Button\n" +
177                     "        android:layout_width=\"wrap_content\"\n" +
178                     "        android:layout_height=\"wrap_content\"\n" +
179                     "        android:layout_below=\"@id/last\"\n" +
180                     "        android:text=\"Submit\"\n" +
181                     "        android:onClick=\"@{activity.onClick}\"/>\n" +
182                     "</RelativeLayout>";
183 }
184