/* * Copyright (C) 2011 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.tools.lint; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.tools.lint.checks.BuiltinIssueRegistry; import com.android.tools.lint.client.api.LintClient; import com.android.tools.lint.client.api.LintDriver; import com.android.tools.lint.detector.api.Context; import com.android.tools.lint.detector.api.Issue; import com.android.tools.lint.detector.api.Location; import com.android.tools.lint.detector.api.Location.Handle; import com.android.tools.lint.detector.api.Position; import com.android.tools.lint.detector.api.Project; import com.android.tools.lint.detector.api.Severity; import com.android.tools.lint.detector.api.TextFormat; import com.android.tools.lint.detector.api.XmlContext; import junit.framework.TestCase; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.Writer; @SuppressWarnings("javadoc") public class LintCliXmlParserTest extends TestCase { public void test() 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=\"wrap_content\"\n" + " android:orientation=\"vertical\" >\n" + "\n" + " <Button\n" + " android:id=\"@+id/button1\"\n" + " android:layout_width=\"wrap_content\"\n" + " android:layout_height=\"wrap_content\"\n" + " android:text=\"Button\" />\n" + "\n" + " <Button\n" + " android:id=\"@+id/button2\"\n" + " android:layout_width=\"wrap_content\"\n" + " android:layout_height=\"wrap_content\"\n" + " android:text=\"Button\" />\n" + "\n" + "</LinearLayout>\n"; LintCliXmlParser parser = new LintCliXmlParser(); File file = File.createTempFile("parsertest", ".xml"); //noinspection IOResourceOpenedButNotSafelyClosed Writer fw = new BufferedWriter(new FileWriter(file)); fw.write(xml); fw.close(); LintClient client = new TestClient(); LintDriver driver = new LintDriver(new BuiltinIssueRegistry(), client); Project project = Project.create(client, file.getParentFile(), file.getParentFile()); XmlContext context = new XmlContext(driver, project, null, file, null, parser); Document document = parser.parseXml(context); assertNotNull(document); // Basic parsing heart beat tests Element linearLayout = (Element) document.getElementsByTagName("LinearLayout").item(0); assertNotNull(linearLayout); NodeList buttons = document.getElementsByTagName("Button"); assertEquals(2, buttons.getLength()); final String ANDROID_URI = "http://schemas.android.com/apk/res/android"; assertEquals("wrap_content", linearLayout.getAttributeNS(ANDROID_URI, "layout_height")); // Check attribute positions Attr attr = linearLayout.getAttributeNodeNS(ANDROID_URI, "layout_width"); assertNotNull(attr); Location location = parser.getLocation(context, attr); Position start = location.getStart(); Position end = location.getEnd(); assertNotNull(start); assertNotNull(end); assertEquals(2, start.getLine()); assertEquals(xml.indexOf("android:layout_width"), start.getOffset()); assertEquals(2, end.getLine()); String target = "android:layout_width=\"match_parent\""; assertEquals(xml.indexOf(target) + target.length(), end.getOffset()); // Check attribute name positions location = parser.getNameLocation(context, attr); start = location.getStart(); end = location.getEnd(); assertNotNull(start); assertNotNull(end); target = "android:layout_width"; assertEquals(target, xml.substring(start.getOffset(), end.getOffset())); assertEquals(xml.indexOf(target) + target.length(), end.getOffset()); // Check attribute value positions location = parser.getValueLocation(context, attr); start = location.getStart(); end = location.getEnd(); assertNotNull(start); assertNotNull(end); target = "match_parent"; assertEquals(target, xml.substring(start.getOffset(), end.getOffset())); assertEquals(xml.indexOf(target) + target.length(), end.getOffset()); // Check element positions Element button = (Element) buttons.item(0); location = parser.getLocation(context, button); start = location.getStart(); end = location.getEnd(); assertNotNull(start); assertNotNull(end); assertEquals(6, start.getLine()); assertEquals(xml.indexOf("<Button"), start.getOffset()); assertEquals(xml.indexOf("/>") + 2, end.getOffset()); assertEquals(10, end.getLine()); int button1End = end.getOffset(); // Check element name positions location = parser.getNameLocation(context, button); start = location.getStart(); end = location.getEnd(); assertNotNull(start); assertNotNull(end); target = "Button"; assertEquals(target, xml.substring(start.getOffset(), end.getOffset())); assertEquals(xml.indexOf(target) + target.length(), end.getOffset()); Handle handle = parser.createLocationHandle(context, button); Location location2 = handle.resolve(); assertSame(location.getFile(), location.getFile()); assertNotNull(location2.getStart()); assertNotNull(location2.getEnd()); assertEquals(6, location2.getStart().getLine()); assertEquals(10, location2.getEnd().getLine()); Element button2 = (Element) buttons.item(1); location = parser.getLocation(context, button2); start = location.getStart(); end = location.getEnd(); assertNotNull(start); assertNotNull(end); assertEquals(12, start.getLine()); assertEquals(xml.indexOf("<Button", button1End), start.getOffset()); assertEquals(xml.indexOf("/>", start.getOffset()) + 2, end.getOffset()); assertEquals(16, end.getLine()); parser.dispose(context, document); //noinspection ResultOfMethodCallIgnored file.delete(); } public void testLineEndings() throws Exception { // Test for http://code.google.com/p/android/issues/detail?id=22925 String xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + "<LinearLayout>\r\n" + "\r" + "<LinearLayout></LinearLayout>\r\n" + "</LinearLayout>\r\n"; LintCliXmlParser parser = new LintCliXmlParser(); File file = File.createTempFile("parsertest2", ".xml"); //noinspection IOResourceOpenedButNotSafelyClosed Writer fw = new BufferedWriter(new FileWriter(file)); fw.write(xml); fw.close(); LintClient client = new TestClient(); LintDriver driver = new LintDriver(new BuiltinIssueRegistry(), client); Project project = Project.create(client, file.getParentFile(), file.getParentFile()); XmlContext context = new XmlContext(driver, project, null, file, null, parser); Document document = parser.parseXml(context); assertNotNull(document); //noinspection ResultOfMethodCallIgnored file.delete(); } private static class TestClient extends LintCliClient { @Override public void report( @NonNull Context context, @NonNull Issue issue, @NonNull Severity severity, @Nullable Location location, @NonNull String message, @NonNull TextFormat format) { System.out.println(location + ":" + message); } } }