/*
 * 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"));
    }
}