/* * Copyright (C) 2012 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.checks; import com.android.tools.lint.detector.api.Detector; @SuppressWarnings("javadoc") public class NamespaceDetectorTest extends AbstractCheckTest { @Override protected Detector getDetector() { return new NamespaceDetector(); } public void testCustom() throws Exception { assertEquals( "res/layout/customview.xml:5: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" + " xmlns:foo=\"http://schemas.android.com/apk/res/foo\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintProject( "multiproject/library-manifest.xml=>AndroidManifest.xml", "multiproject/library.properties=>project.properties", "res/layout/customview.xml" )); } public void testGradle() throws Exception { assertEquals("" + "res/layout/customview.xml:5: Error: In Gradle projects, always use http://schemas.android.com/apk/res-auto for custom attributes [ResAuto]\n" + " xmlns:foo=\"http://schemas.android.com/apk/res/foo\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintProject( "multiproject/library.properties=>build.gradle", // dummy; only name counts "res/layout/customview.xml" )); } public void testCustomOk() throws Exception { assertEquals( "No warnings.", lintProject( "multiproject/library-manifest.xml=>AndroidManifest.xml", // Use a standard project properties instead: no warning since it's // not a library project: //"multiproject/library.properties=>project.properties", "res/layout/customview.xml" )); } public void testCustomOk2() throws Exception { assertEquals( "No warnings.", lintProject( "multiproject/library-manifest.xml=>AndroidManifest.xml", "multiproject/library.properties=>project.properties", // This project already uses the res-auto package "res/layout/customview2.xml" )); } public void testTypo() throws Exception { assertEquals( "res/layout/wrong_namespace.xml:2: Error: Unexpected namespace URI bound to the \"android\" prefix, was http://schemas.android.com/apk/res/andriod, expected http://schemas.android.com/apk/res/android [NamespaceTypo]\n" + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/andriod\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintProject("res/layout/wrong_namespace.xml")); } public void testTypo2() throws Exception { assertEquals( "res/layout/wrong_namespace2.xml:2: Error: URI is case sensitive: was \"http://schemas.android.com/apk/res/Android\", expected \"http://schemas.android.com/apk/res/android\" [NamespaceTypo]\n" + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/Android\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintProject("res/layout/wrong_namespace2.xml")); } public void testTypo3() throws Exception { assertEquals( "res/layout/wrong_namespace3.xml:2: Error: Unexpected namespace URI bound to the \"android\" prefix, was http://schemas.android.com/apk/res/androi, expected http://schemas.android.com/apk/res/android [NamespaceTypo]\n" + "<LinearLayout xmlns:a=\"http://schemas.android.com/apk/res/androi\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintProject("res/layout/wrong_namespace3.xml")); } public void testTypo4() throws Exception { assertEquals( "res/layout/wrong_namespace5.xml:2: Error: Suspicious namespace: should start with http:// [NamespaceTypo]\n" + " xmlns:noturi=\"tp://schems.android.com/apk/res/com.my.package\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "res/layout/wrong_namespace5.xml:3: Error: Possible typo in URL: was \"http://schems.android.com/apk/res/com.my.package\", should probably be \"http://schemas.android.com/apk/res/com.my.package\" [NamespaceTypo]\n" + " xmlns:typo1=\"http://schems.android.com/apk/res/com.my.package\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "res/layout/wrong_namespace5.xml:4: Error: Possible typo in URL: was \"http://schems.android.comm/apk/res/com.my.package\", should probably be \"http://schemas.android.com/apk/res/com.my.package\" [NamespaceTypo]\n" + " xmlns:typo2=\"http://schems.android.comm/apk/res/com.my.package\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "3 errors, 0 warnings\n", lintProject("res/layout/wrong_namespace5.xml")); } public void testMisleadingPrefix() throws Exception { assertEquals("" + "res/layout/layout.xml:3: Error: Suspicious namespace and prefix combination [NamespaceTypo]\n" + " xmlns:app=\"http://schemas.android.com/tools\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "res/layout/layout.xml:4: Error: Suspicious namespace and prefix combination [NamespaceTypo]\n" + " xmlns:tools=\"http://schemas.android.com/apk/res/android\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "2 errors, 0 warnings\n", lintProject( xml("res/layout/layout.xml", "" + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + " xmlns:app=\"http://schemas.android.com/tools\"\n" + " xmlns:tools=\"http://schemas.android.com/apk/res/android\"\n" + " android:layout_width=\"match_parent\"\n" + " android:layout_height=\"match_parent\"\n" + " android:orientation=\"vertical\"\n" + " app:foo=\"true\"\n" + " tools:bar=\"true\" />\n"))); } public void testTypoOk() throws Exception { assertEquals( "No warnings.", lintProject("res/layout/wrong_namespace4.xml")); } public void testUnused() throws Exception { assertEquals( "res/layout/unused_namespace.xml:3: Warning: Unused namespace unused1 [UnusedNamespace]\n" + " xmlns:unused1=\"http://schemas.android.com/apk/res/unused1\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "res/layout/unused_namespace.xml:4: Warning: Unused namespace unused2 [UnusedNamespace]\n" + " xmlns:unused2=\"http://schemas.android.com/apk/res/unused1\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "0 errors, 2 warnings\n" + "", lintProject("res/layout/unused_namespace.xml")); } public void testUnusedOk() throws Exception { assertEquals( "No warnings.", lintProject("res/layout/layout1.xml")); } public void testLayoutAttributesOk() throws Exception { assertEquals( "No warnings.", lintFiles("res/layout/namespace3.xml")); } public void testLayoutAttributesOk2() throws Exception { assertEquals( "No warnings.", lintFiles("res/layout/namespace4.xml")); } public void testLayoutAttributes() throws Exception { assertEquals( "res/layout/namespace3.xml:2: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" + " xmlns:app=\"http://schemas.android.com/apk/res/com.example.apicalltest\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintFiles("res/layout/namespace3.xml", "multiproject/library-manifest.xml=>AndroidManifest.xml", "multiproject/library.properties=>project.properties")); } public void testLayoutAttributes2() throws Exception { assertEquals( "res/layout/namespace4.xml:3: Error: When using a custom namespace attribute in a library project, use the namespace \"http://schemas.android.com/apk/res-auto\" instead. [LibraryCustomView]\n" + " xmlns:app=\"http://schemas.android.com/apk/res/com.example.apicalltest\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintFiles("res/layout/namespace4.xml", "multiproject/library-manifest.xml=>AndroidManifest.xml", "multiproject/library.properties=>project.properties")); } public void testWrongResAutoUrl() throws Exception { assertEquals( "res/layout/namespace5.xml:3: Error: Suspicious namespace: Did you mean http://schemas.android.com/apk/res-auto? [ResAuto]\n" + " xmlns:app=\"http://schemas.android.com/apk/auto-res/\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintFiles("res/layout/namespace5.xml", "multiproject/library-manifest.xml=>AndroidManifest.xml", "multiproject/library.properties=>project.properties")); } public void testWrongResUrl() throws Exception { assertEquals("" + "AndroidManifest.xml:2: Error: Suspicious namespace: should start with http:// [NamespaceTypo]\n" + "<manifest xmlns:android=\"https://schemas.android.com/apk/res/android\"\n" + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + "1 errors, 0 warnings\n", lintFiles("https_namespace.xml=>AndroidManifest.xml")); } }