blob: b63daffd97e1633b948fb589d65bdf0496f17b23 [file] [log] [blame]
/*
* 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.detector.api
import com.android.SdkConstants.ANDROID_URI
import com.android.SdkConstants.ATTR_NAME
import com.android.SdkConstants.DOT_JAR
import com.android.SdkConstants.DOT_JAVA
import com.android.SdkConstants.DOT_KT
import com.android.builder.model.ApiVersion
import com.android.ide.common.repository.GradleVersion
import com.android.resources.ResourceFolderType
import com.android.sdklib.AndroidVersion
import com.android.sdklib.IAndroidTarget
import com.android.testutils.TestUtils
import com.android.tools.lint.LintCliClient
import com.android.tools.lint.checks.infrastructure.ClassName
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
import com.android.tools.lint.checks.infrastructure.TestFile
import com.android.tools.lint.checks.infrastructure.TestFiles
import com.android.tools.lint.checks.infrastructure.TestFiles.java
import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin
import com.android.tools.lint.checks.infrastructure.TestIssueRegistry
import com.android.tools.lint.checks.infrastructure.TestLintClient
import com.android.tools.lint.checks.infrastructure.TestLintTask
import com.android.tools.lint.checks.infrastructure.findKotlinStdlibPath
import com.android.tools.lint.client.api.LintClient.Companion.CLIENT_UNIT_TESTS
import com.android.tools.lint.client.api.LintDriver
import com.android.tools.lint.client.api.LintRequest
import com.android.tools.lint.client.api.TYPE_BOOLEAN
import com.android.tools.lint.client.api.TYPE_BOOLEAN_WRAPPER
import com.android.tools.lint.client.api.TYPE_BYTE
import com.android.tools.lint.client.api.TYPE_BYTE_WRAPPER
import com.android.tools.lint.client.api.TYPE_CHAR
import com.android.tools.lint.client.api.TYPE_CHARACTER_WRAPPER
import com.android.tools.lint.client.api.TYPE_DOUBLE
import com.android.tools.lint.client.api.TYPE_DOUBLE_WRAPPER
import com.android.tools.lint.client.api.TYPE_FLOAT
import com.android.tools.lint.client.api.TYPE_FLOAT_WRAPPER
import com.android.tools.lint.client.api.TYPE_INT
import com.android.tools.lint.client.api.TYPE_INTEGER_WRAPPER
import com.android.tools.lint.client.api.TYPE_LONG
import com.android.tools.lint.client.api.TYPE_LONG_WRAPPER
import com.android.tools.lint.client.api.TYPE_SHORT
import com.android.tools.lint.client.api.TYPE_SHORT_WRAPPER
import com.android.tools.lint.client.api.XmlParser
import com.android.utils.Pair
import com.android.utils.SdkUtils.escapePropertyValue
import com.android.utils.XmlUtils
import com.google.common.base.Charsets
import com.google.common.collect.Iterables
import com.google.common.io.Files
import com.google.common.truth.Truth.assertThat
import com.intellij.openapi.Disposable
import junit.framework.TestCase
import org.intellij.lang.annotations.Language
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.w3c.dom.Document
import org.w3c.dom.Element
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.OutputStreamWriter
import java.util.Arrays
import java.util.Locale
class LintUtilsTest : TestCase() {
fun testPrintList() {
assertEquals("bar, baz, foo", formatList(Arrays.asList("foo", "bar", "baz"), 3))
assertEquals(
"foo, bar, baz",
formatList(Arrays.asList("foo", "bar", "baz"), 3, false)
)
assertEquals(
"foo, bar, baz",
formatList(Arrays.asList("foo", "bar", "baz"), 5, false)
)
assertEquals(
"foo, bar, baz... (3 more)",
formatList(Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 3, false)
)
assertEquals(
"foo... (5 more)",
formatList(Arrays.asList("foo", "bar", "baz", "4", "5", "6"), 1, false)
)
assertEquals(
"foo, bar, baz",
formatList(Arrays.asList("foo", "bar", "baz"), 0, false)
)
assertEquals(
"foo, bar and baz",
formatList(Arrays.asList("foo", "bar", "baz"), 0, false, true)
)
}
fun testIsDataBindingExpression() {
assertTrue(isDataBindingExpression("@{foo}"))
assertTrue(isDataBindingExpression("@={foo}"))
assertFalse(isDataBindingExpression("@string/foo"))
assertFalse(isDataBindingExpression("?string/foo"))
assertFalse(isDataBindingExpression(""))
assertFalse(isDataBindingExpression("foo"))
}
fun testDescribeCounts() {
assertThat(describeCounts(0, 0, true, true)).isEqualTo("No errors or warnings")
assertThat(describeCounts(0, 0, true, false)).isEqualTo("no errors or warnings")
assertThat(describeCounts(0, 1, true, true)).isEqualTo("1 warning")
assertThat(describeCounts(1, 0, true, true)).isEqualTo("1 error")
assertThat(describeCounts(0, 2, true, true)).isEqualTo("2 warnings")
assertThat(describeCounts(2, 0, true, true)).isEqualTo("2 errors")
assertThat(describeCounts(2, 1, false, true)).isEqualTo("2 errors and 1 warning")
assertThat(describeCounts(1, 2, false, true)).isEqualTo("1 error and 2 warnings")
assertThat(describeCounts(5, 4, false, true)).isEqualTo("5 errors and 4 warnings")
assertThat(describeCounts(2, 1, true, true)).isEqualTo("2 errors, 1 warning")
assertThat(describeCounts(1, 2, true, true)).isEqualTo("1 error, 2 warnings")
assertThat(describeCounts(5, 4, true, true)).isEqualTo("5 errors, 4 warnings")
}
fun testEndsWith() {
assertTrue(endsWith("Foo", ""))
assertTrue(endsWith("Foo", "o"))
assertTrue(endsWith("Foo", "oo"))
assertTrue(endsWith("Foo", "Foo"))
assertTrue(endsWith("Foo", "FOO"))
assertTrue(endsWith("Foo", "fOO"))
assertFalse(endsWith("Foo", "f"))
}
fun testStartsWith() {
assertTrue(startsWith("FooBar", "Bar", 3))
assertTrue(startsWith("FooBar", "BAR", 3))
assertTrue(startsWith("FooBar", "Foo", 0))
assertFalse(startsWith("FooBar", "Foo", 2))
}
fun testIsXmlFile() {
assertTrue(isXmlFile(File("foo.xml")))
assertTrue(isXmlFile(File("foo.Xml")))
assertTrue(isXmlFile(File("foo.XML")))
assertFalse(isXmlFile(File("foo.png")))
assertFalse(isXmlFile(File("xml")))
assertFalse(isXmlFile(File("xml.png")))
}
fun testEditDistance() {
assertEquals(0, editDistance("kitten", "kitten"))
// editing kitten to sitting has edit distance 3:
// replace k with s
// replace e with i
// append g
assertEquals(3, editDistance("kitten", "sitting"))
assertEquals(3, editDistance("saturday", "sunday"))
assertEquals(1, editDistance("button", "bitton"))
assertEquals(6, editDistance("radiobutton", "bitton"))
assertEquals(6, editDistance("radiobutton", "bitton", 10))
assertEquals(6, editDistance("radiobutton", "bitton", 6))
assertEquals(Integer.MAX_VALUE, editDistance("radiobutton", "bitton", 3))
assertTrue(isEditableTo("radiobutton", "bitton", 10))
assertTrue(isEditableTo("radiobutton", "bitton", 6))
assertFalse(isEditableTo("radiobutton", "bitton", 3))
}
fun testSplitPath() {
assertTrue(
Arrays.equals(
arrayOf("/foo", "/bar", "/baz"),
Iterables.toArray(splitPath("/foo:/bar:/baz"), String::class.java)
)
)
assertTrue(
Arrays.equals(
arrayOf("/foo", "/bar"),
Iterables.toArray(splitPath("/foo;/bar"), String::class.java)
)
)
assertTrue(
Arrays.equals(
arrayOf("/foo", "/bar:baz"),
Iterables.toArray(splitPath("/foo;/bar:baz"), String::class.java)
)
)
assertTrue(
Arrays.equals(
arrayOf("\\foo\\bar", "\\bar\\foo"),
Iterables.toArray(splitPath("\\foo\\bar;\\bar\\foo"), String::class.java)
)
)
assertTrue(
Arrays.equals(
arrayOf("\${sdk.dir}\\foo\\bar", "\\bar\\foo"),
Iterables.toArray(
splitPath("\${sdk.dir}\\foo\\bar;\\bar\\foo"), String::class.java
)
)
)
assertTrue(
Arrays.equals(
arrayOf("\${sdk.dir}/foo/bar", "/bar/foo"),
Iterables.toArray(splitPath("\${sdk.dir}/foo/bar:/bar/foo"), String::class.java)
)
)
assertTrue(
Arrays.equals(
arrayOf("C:\\foo", "/bar"),
Iterables.toArray(splitPath("C:\\foo:/bar"), String::class.java)
)
)
}
fun testCommonParen1() {
assertEquals(File("/a"), getCommonParent(File("/a/b/c/d/e"), File("/a/c")))
assertEquals(File("/a"), getCommonParent(File("/a/c"), File("/a/b/c/d/e")))
assertEquals(File("/"), getCommonParent(File("/foo/bar"), File("/bar/baz")))
assertEquals(File("/"), getCommonParent(File("/foo/bar"), File("/")))
assertNull(getCommonParent(File("C:\\Program Files"), File("F:\\")))
assertNull(getCommonParent(File("C:/Program Files"), File("F:/")))
assertEquals(
File("/foo/bar/baz"),
getCommonParent(File("/foo/bar/baz"), File("/foo/bar/baz"))
)
assertEquals(
File("/foo/bar"),
getCommonParent(File("/foo/bar/baz"), File("/foo/bar"))
)
assertEquals(
File("/foo/bar"),
getCommonParent(File("/foo/bar/baz"), File("/foo/bar/foo"))
)
assertEquals(File("/foo"), getCommonParent(File("/foo/bar"), File("/foo/baz")))
assertEquals(File("/foo"), getCommonParent(File("/foo/bar"), File("/foo/baz")))
assertEquals(
File("/foo/bar"),
getCommonParent(File("/foo/bar"), File("/foo/bar/baz"))
)
}
fun testCommonParent2() {
assertEquals(
File("/"),
getCommonParent(Arrays.asList(File("/foo/bar"), File("/bar/baz")))
)
assertEquals(
File("/"), getCommonParent(Arrays.asList(File("/foo/bar"), File("/")))
)
assertNull(getCommonParent(Arrays.asList(File("C:\\Program Files"), File("F:\\"))))
assertNull(getCommonParent(Arrays.asList(File("C:/Program Files"), File("F:/"))))
assertEquals(
File("/foo"),
getCommonParent(Arrays.asList(File("/foo/bar"), File("/foo/baz")))
)
assertEquals(
File("/foo"),
getCommonParent(
Arrays.asList(
File("/foo/bar"),
File("/foo/baz"),
File("/foo/baz/f")
)
)
)
assertEquals(
File("/foo/bar"),
getCommonParent(
Arrays.asList(
File("/foo/bar"),
File("/foo/bar/baz"),
File("/foo/bar/foo2/foo3")
)
)
)
}
fun testStripIdPrefix() {
assertEquals("foo", stripIdPrefix("@+id/foo"))
assertEquals("foo", stripIdPrefix("@id/foo"))
assertEquals("foo", stripIdPrefix("foo"))
}
fun testIdReferencesMatch() {
assertTrue(idReferencesMatch("@+id/foo", "@+id/foo"))
assertTrue(idReferencesMatch("@id/foo", "@id/foo"))
assertTrue(idReferencesMatch("@id/foo", "@+id/foo"))
assertTrue(idReferencesMatch("@+id/foo", "@id/foo"))
assertFalse(idReferencesMatch("@+id/foo", "@+id/bar"))
assertFalse(idReferencesMatch("@id/foo", "@+id/bar"))
assertFalse(idReferencesMatch("@+id/foo", "@id/bar"))
assertFalse(idReferencesMatch("@+id/foo", "@+id/bar"))
assertFalse(idReferencesMatch("@+id/foo", "@+id/foo1"))
assertFalse(idReferencesMatch("@id/foo", "@id/foo1"))
assertFalse(idReferencesMatch("@id/foo", "@+id/foo1"))
assertFalse(idReferencesMatch("@+id/foo", "@id/foo1"))
assertFalse(idReferencesMatch("@+id/foo1", "@+id/foo"))
assertFalse(idReferencesMatch("@id/foo1", "@id/foo"))
assertFalse(idReferencesMatch("@id/foo1", "@+id/foo"))
assertFalse(idReferencesMatch("@+id/foo1", "@id/foo"))
}
@Throws(Exception::class)
fun testGetEncodedString() {
checkEncoding("utf-8", false /*bom*/, "\n")
checkEncoding("UTF-8", false /*bom*/, "\n")
checkEncoding("UTF_16", false /*bom*/, "\n")
checkEncoding("UTF-16", false /*bom*/, "\n")
checkEncoding("UTF_16LE", false /*bom*/, "\n")
// Try BOM's
checkEncoding("utf-8", true /*bom*/, "\n")
checkEncoding("UTF-8", true /*bom*/, "\n")
checkEncoding("UTF_16", true /*bom*/, "\n")
checkEncoding("UTF-16", true /*bom*/, "\n")
checkEncoding("UTF_16LE", true /*bom*/, "\n")
checkEncoding("UTF_32", true /*bom*/, "\n")
checkEncoding("UTF_32LE", true /*bom*/, "\n")
// Make sure this works for \r and \r\n as well
checkEncoding("UTF-16", false /*bom*/, "\r")
checkEncoding("UTF_16LE", false /*bom*/, "\r")
checkEncoding("UTF-16", false /*bom*/, "\r\n")
checkEncoding("UTF_16LE", false /*bom*/, "\r\n")
checkEncoding("UTF-16", true /*bom*/, "\r")
checkEncoding("UTF_16LE", true /*bom*/, "\r")
checkEncoding("UTF_32", true /*bom*/, "\r")
checkEncoding("UTF_32LE", true /*bom*/, "\r")
checkEncoding("UTF-16", true /*bom*/, "\r\n")
checkEncoding("UTF_16LE", true /*bom*/, "\r\n")
checkEncoding("UTF_32", true /*bom*/, "\r\n")
checkEncoding("UTF_32LE", true /*bom*/, "\r\n")
}
fun testGetLocale() {
assertNull(getLocale(""))
assertNull(getLocale("values"))
assertNull(getLocale("values-xlarge-port"))
assertEquals("en", getLocale("values-en")!!.language)
assertEquals("pt", getLocale("values-pt-rPT-nokeys")!!.language)
assertEquals("pt", getLocale("values-b+pt+PT-nokeys")!!.language)
assertEquals("zh", getLocale("values-zh-rCN-keyshidden")!!.language)
}
@Suppress("JoinDeclarationAndAssignment")
fun testGetLocale2() {
var xml: LintDetectorTest.TestFile
var context: XmlContext
xml = TestFiles.xml("res/values/strings.xml", "<resources>\n</resources>\n")
context = createXmlContext(xml.getContents(), File(xml.targetPath))
assertNull(getLocale(context))
xml = TestFiles.xml("res/values-no/strings.xml", "<resources>\n</resources>\n")
context = createXmlContext(xml.getContents(), File(xml.targetPath))
assertEquals("no", getLocale(context)!!.language)
xml = TestFiles.xml(
"res/values/strings.xml",
"" +
"<resources tools:locale=\"nb\" xmlns:tools=\"http://schemas.android.com/tools\">\n" +
"</resources>\n"
)
context = createXmlContext(xml.getContents(), File(xml.targetPath))
assertEquals("nb", getLocale(context)!!.language)
// tools:locale wins over folder location
xml = TestFiles.xml(
"res/values-fr/strings.xml",
"" +
"<resources tools:locale=\"nb\" xmlns:tools=\"http://schemas.android.com/tools\">\n" +
"</resources>\n"
)
context = createXmlContext(xml.getContents(), File(xml.targetPath))
assertEquals("nb", getLocale(context)!!.language)
}
fun testGetLocaleAndRegion() {
assertNull(getLocaleAndRegion(""))
assertNull(getLocaleAndRegion("values"))
assertNull(getLocaleAndRegion("values-xlarge-port"))
assertEquals("en", getLocaleAndRegion("values-en"))
assertEquals("pt-rPT", getLocaleAndRegion("values-pt-rPT-nokeys"))
assertEquals("b+pt+PT", getLocaleAndRegion("values-b+pt+PT-nokeys"))
assertEquals("zh-rCN", getLocaleAndRegion("values-zh-rCN-keyshidden"))
assertEquals("ms", getLocaleAndRegion("values-ms-keyshidden"))
}
fun testComputeResourceName() {
assertEquals("", computeResourceName("", "", null))
assertEquals("foo", computeResourceName("", "foo", null))
assertEquals("foo", computeResourceName("foo", "", null))
assertEquals("prefix_name", computeResourceName("prefix_", "name", null))
assertEquals("prefixName", computeResourceName("prefix", "name", null))
assertEquals("PrefixName", computeResourceName("prefix", "Name", null))
assertEquals("PrefixName", computeResourceName("prefix_", "Name", null))
assertEquals("MyPrefixName", computeResourceName("myPrefix", "Name", null))
assertEquals(
"my_prefix_name",
computeResourceName("myPrefix", "name", ResourceFolderType.LAYOUT)
)
assertEquals(
"UnitTestPrefixContentFrame",
computeResourceName(
"unit_test_prefix_", "ContentFrame", ResourceFolderType.VALUES
)
)
assertEquals(
"MyPrefixMyStyle",
computeResourceName("myPrefix_", "MyStyle", ResourceFolderType.VALUES)
)
}
fun testConvertVersion() {
assertEquals(
AndroidVersion(5, null), convertVersion(DefaultApiVersion(5, null), null)
)
assertEquals(
AndroidVersion(19, null),
convertVersion(DefaultApiVersion(19, null), null)
)
assertEquals(
AndroidVersion(18, "KITKAT"), // a preview platform API level is not final
convertVersion(DefaultApiVersion(0, "KITKAT"), null)
)
}
fun testIsModelOlderThan() {
var project = mock(Project::class.java)
`when`<GradleVersion>(project.gradleModelVersion).thenReturn(GradleVersion.parse("0.10.4"))
assertTrue(isModelOlderThan(project, 0, 10, 5))
assertTrue(isModelOlderThan(project, 0, 11, 0))
assertTrue(isModelOlderThan(project, 0, 11, 4))
assertTrue(isModelOlderThan(project, 1, 0, 0))
project = mock(Project::class.java)
`when`<GradleVersion>(project.gradleModelVersion).thenReturn(GradleVersion.parse("0.11.0"))
assertTrue(isModelOlderThan(project, 1, 0, 0))
assertFalse(isModelOlderThan(project, 0, 11, 0))
assertFalse(isModelOlderThan(project, 0, 10, 4))
project = mock(Project::class.java)
`when`<GradleVersion>(project.gradleModelVersion).thenReturn(GradleVersion.parse("0.11.5"))
assertTrue(isModelOlderThan(project, 1, 0, 0))
assertFalse(isModelOlderThan(project, 0, 11, 0))
project = mock(Project::class.java)
`when`<GradleVersion>(project.gradleModelVersion).thenReturn(GradleVersion.parse("1.0.0"))
assertTrue(isModelOlderThan(project, 1, 0, 1))
assertFalse(isModelOlderThan(project, 1, 0, 0))
assertFalse(isModelOlderThan(project, 0, 11, 0))
project = mock(Project::class.java)
assertTrue(isModelOlderThan(project, 0, 0, 0, true))
assertFalse(isModelOlderThan(project, 0, 0, 0, false))
}
private class DefaultApiVersion(private val mApiLevel: Int, private val mCodename: String?) :
ApiVersion {
override fun getApiLevel(): Int {
return mApiLevel
}
override fun getCodename(): String? {
return mCodename
}
override fun getApiString(): String {
fail("Not needed in this test")
return "<invalid>"
}
}
fun testFindSubstring() {
assertEquals("foo", findSubstring("foo", null, null))
assertEquals("foo", findSubstring("foo ", null, " "))
assertEquals("foo", findSubstring(" foo", " ", null))
assertEquals("foo", findSubstring("[foo]", "[", "]"))
}
fun testGetFormattedParameters() {
assertEquals(
Arrays.asList("foo", "bar"),
getFormattedParameters(
"Prefix %1\$s Divider %2\$s Suffix", "Prefix foo Divider bar Suffix"
)
)
}
fun testEscapePropertyValue() {
assertEquals("foo", escapePropertyValue("foo"))
assertEquals("\\ foo ", escapePropertyValue(" foo "))
assertEquals("c\\:/foo/bar", escapePropertyValue("c:/foo/bar"))
assertEquals("\\!\\#\\:\\\\a\\\\b\\\\c", escapePropertyValue("!#:\\a\\b\\c"))
assertEquals(
"foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo\\#foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo",
escapePropertyValue(
"foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo#foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo"
)
)
}
fun testGetAutoBoxedType() {
assertEquals(TYPE_INTEGER_WRAPPER, getAutoBoxedType(TYPE_INT))
assertEquals(TYPE_INT, getPrimitiveType(TYPE_INTEGER_WRAPPER))
val pairs = arrayOf(
TYPE_BOOLEAN,
TYPE_BOOLEAN_WRAPPER,
TYPE_BYTE,
TYPE_BYTE_WRAPPER,
TYPE_CHAR,
TYPE_CHARACTER_WRAPPER,
TYPE_DOUBLE,
TYPE_DOUBLE_WRAPPER,
TYPE_FLOAT,
TYPE_FLOAT_WRAPPER,
TYPE_INT,
TYPE_INTEGER_WRAPPER,
TYPE_LONG,
TYPE_LONG_WRAPPER,
TYPE_SHORT,
TYPE_SHORT_WRAPPER
)
var i = 0
while (i < pairs.size) {
val primitive = pairs[i]
val autoBoxed = pairs[i + 1]
assertEquals(autoBoxed, getAutoBoxedType(primitive))
assertEquals(primitive, getPrimitiveType(autoBoxed))
i += 2
}
}
fun testResolveManifestName() {
assertEquals(
"test.pkg.TestActivity",
resolveManifestName(
getElementWithNameValue(
"" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
" package=\"test.pkg\">\n" +
" <application>\n" +
" <activity android:name=\".TestActivity\" />\n" +
" </application>\n" +
"</manifest>\n",
".TestActivity"
)
)
)
assertEquals(
"test.pkg.TestActivity",
resolveManifestName(
getElementWithNameValue(
"" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
" package=\"test.pkg\">\n" +
" <application>\n" +
" <activity android:name=\"TestActivity\" />\n" +
" </application>\n" +
"</manifest>\n",
"TestActivity"
)
)
)
assertEquals(
"test.pkg.TestActivity",
resolveManifestName(
getElementWithNameValue(
"" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
" package=\"test.pkg\">\n" +
" <application>\n" +
" <activity android:name=\"test.pkg.TestActivity\" />\n" +
" </application>\n" +
"</manifest>\n",
"test.pkg.TestActivity"
)
)
)
assertEquals(
"test.pkg.TestActivity.Bar",
resolveManifestName(
getElementWithNameValue(
"" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
" package=\"test.pkg\">\n" +
" <application>\n" +
" <activity android:name=\"test.pkg.TestActivity\$Bar\" />\n" +
" </application>\n" +
"</manifest>\n",
"test.pkg.TestActivity\$Bar"
)
)
)
}
fun testGetFileNameWithParent() {
assertThat(
getFileNameWithParent(
TestLintClient(),
File(
"tmp" +
File.separator +
"foo" +
File.separator +
"bar.baz"
)
)
)
.isEqualTo("foo/bar.baz")
assertThat(
getFileNameWithParent(
LintCliClient(CLIENT_UNIT_TESTS),
File(
"tmp" +
File.separator +
"foo" +
File.separator +
"bar.baz"
)
)
)
.isEqualTo(if (File.separatorChar == '/') "foo/bar.baz" else "foo\\\\bar.baz")
}
fun testJavaKeyword() {
assertThat(isJavaKeyword("")).isFalse()
assertThat(isJavaKeyword("iff")).isFalse()
assertThat(isJavaKeyword("if")).isTrue()
assertThat(isJavaKeyword("true")).isTrue()
assertThat(isJavaKeyword("false")).isTrue()
}
private class JavaTestContext(
driver: LintDriver,
project: Project,
private val mJavaSource: String,
file: File
) : JavaContext(driver, project, null, file) {
override fun getContents(): String? {
return mJavaSource
}
}
private class XmlTestContext(
driver: LintDriver,
project: Project,
private val xmlSource: String,
file: File,
type: ResourceFolderType,
parser: XmlParser,
document: Document
) : XmlContext(driver, project, null, file, type, parser, xmlSource, document) {
override fun getContents(): String? {
return xmlSource
}
}
companion object {
@Throws(Exception::class)
private fun checkEncoding(encoding: String, writeBom: Boolean, lineEnding: String) {
val sb = StringBuilder()
// Norwegian extra vowel characters such as "latin small letter a with ring above"
val value = "\u00e6\u00d8\u00e5"
val expected = ("First line." +
lineEnding +
"Second line." +
lineEnding +
"Third line." +
lineEnding +
value +
lineEnding)
sb.append(expected)
val file = File.createTempFile("getEncodingTest$encoding$writeBom", ".txt")
file.deleteOnExit()
val stream = BufferedOutputStream(FileOutputStream(file))
val writer = OutputStreamWriter(stream, encoding)
if (writeBom) {
val normalized = encoding.toLowerCase(Locale.US).replace("-", "_")
when (normalized) {
"utf_8" -> {
stream.write(0xef)
stream.write(0xbb)
stream.write(0xbf)
}
"utf_16" -> {
stream.write(0xfe)
stream.write(0xff)
}
"utf_16le" -> {
stream.write(0xff)
stream.write(0xfe)
}
"utf_32" -> {
stream.write(0x0)
stream.write(0x0)
stream.write(0xfe)
stream.write(0xff)
}
"utf_32le" -> {
stream.write(0xff)
stream.write(0xfe)
stream.write(0x0)
stream.write(0x0)
}
else -> fail("Can't write BOM for encoding $encoding")
}
}
writer.write(sb.toString())
writer.close()
val s = getEncodedString(LintCliClient(CLIENT_UNIT_TESTS), file, true).toString()
assertEquals(expected, s)
val seq = getEncodedString(LintCliClient(CLIENT_UNIT_TESTS), file, false)
if (encoding.equals("utf-8", ignoreCase = true)) {
assertFalse(seq is String)
}
assertEquals(expected, seq.toString())
}
private fun createTestProjectForFile(
dir: File,
relativePath: File,
source: String,
libs: List<File> = emptyList()
): Project {
val fullPath = File(dir, relativePath.path)
fullPath.parentFile.mkdirs()
try {
Files.asCharSink(fullPath, Charsets.UTF_8).write(source)
} catch (e: IOException) {
fail(e.message)
}
val client = object : LintCliClient(CLIENT_UNIT_TESTS) {
override fun readFile(file: File): CharSequence {
return if (file.path == fullPath.path) {
source
} else super.readFile(file)
}
override fun getCompileTarget(project: Project): IAndroidTarget? {
val targets = getTargets()
for (i in targets.indices.reversed()) {
val target = targets[i]
if (target.isPlatform) {
return target
}
}
return super.getCompileTarget(project)
}
override fun getSdkHome(): File {
return TestUtils.getSdk()
}
override fun getJavaLibraries(
project: Project,
includeProvided: Boolean
): List<File> {
return libs + findKotlinStdlibPath().map { File(it) }
}
}
val project = client.getProject(dir, dir)
client.initializeProjects(listOf(project))
return project
}
fun createXmlContext(@Language("XML") xml: String?, relativePath: File): XmlContext {
val dir = File(System.getProperty("java.io.tmpdir"))
val fullPath = File(dir, relativePath.path)
val project = createTestProjectForFile(dir, relativePath, xml!!)
val client = project.getClient() as LintCliClient
val request = LintRequest(client, listOf(fullPath))
val driver = LintDriver(TestIssueRegistry(), LintCliClient(CLIENT_UNIT_TESTS), request)
driver.scope = Scope.JAVA_FILE_SCOPE
val folderType = ResourceFolderType.getFolderType(relativePath.parentFile.name)
val parser = client.xmlParser
val document = parser.parseXml(xml, fullPath)
return XmlTestContext(driver, project, xml, fullPath, folderType!!, parser, document!!)
}
@JvmStatic
fun parse(
@Language("JAVA") javaSource: String,
relativePath: File?
): Pair<JavaContext, Disposable> {
var path = relativePath
if (path == null) {
val className = ClassName(javaSource)
val pkg = className.packageName
val name = className.className
assert(pkg != null)
assert(name != null)
path = File(
"src" +
File.separatorChar +
pkg!!.replace('.', File.separatorChar) +
File.separatorChar +
name +
DOT_JAVA
)
}
return parse(java(path.path, javaSource))
}
@JvmStatic
fun parseKotlin(
@Language("Kt") kotlinSource: String,
relativePath: File?
): Pair<JavaContext, Disposable> {
var path = relativePath
if (path == null) {
val className = ClassName(kotlinSource)
val pkg = className.packageName
val name = className.className
assert(pkg != null)
assert(name != null)
path = File(
"src" +
File.separatorChar +
pkg!!.replace('.', File.separatorChar) +
File.separatorChar +
name +
DOT_KT
)
}
return parse(kotlin(path.path, kotlinSource))
}
@JvmStatic
fun parse(vararg testFiles: TestFile): Pair<JavaContext, Disposable> {
val dir = Files.createTempDir()
val libs: List<File> = if (testFiles.size > 1) {
val projects = TestLintTask().files(*testFiles).createProjects(dir)
testFiles.filter { it.targetRelativePath.endsWith(DOT_JAR) }
.map { File(projects[0], it.targetRelativePath) }
} else {
emptyList()
}
val primary = testFiles[0]
val relativePath = File(primary.targetRelativePath)
val source = primary.getContents()!!
val fullPath = File(dir, relativePath.path)
val project = createTestProjectForFile(dir, relativePath, source, libs)
val client = project.getClient() as LintCliClient
val request = LintRequest(client, listOf(fullPath))
val driver = LintDriver(TestIssueRegistry(), LintCliClient(CLIENT_UNIT_TESTS), request)
driver.scope = Scope.JAVA_FILE_SCOPE
val context = JavaTestContext(driver, project, source, fullPath)
val uastParser = client.getUastParser(project)
assertNotNull(uastParser)
context.uastParser = uastParser
uastParser.prepare(listOf(context), emptyList())
val uFile = uastParser.parse(context)
context.uastFile = uFile
assert(uFile != null)
context.setJavaFile(uFile!!.psi)
val disposable = Disposable {
client.disposeProjects(listOf(project))
dir.deleteRecursively()
}
return Pair.of<JavaContext, Disposable>(context, disposable)
}
private fun getElementWithNameValue(
@Language("XML") xml: String,
activityName: String
): Element {
val document = XmlUtils.parseDocumentSilently(xml, true)
assertNotNull(document)
val root = document!!.documentElement
assertNotNull(root)
for (application in getChildren(root)) {
for (element in getChildren(application)) {
val name = element.getAttributeNS(ANDROID_URI, ATTR_NAME)
if (activityName == name) {
return element
}
}
}
fail("Didn't find $activityName")
throw AssertionError("Didn't find $activityName")
}
}
}