From 00a03ab0c16e21726ca1261de351aadae65aa0e4 Mon Sep 17 00:00:00 2001 From: kimdowoo Date: Mon, 9 Oct 2023 02:39:47 +0900 Subject: [PATCH 01/20] Apply consistent use of waitAndFindObject extension --- .../samples/apps/nowinandroid/interests/InterestsActions.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsActions.kt b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsActions.kt index d9c563ebc..c359ae87e 100644 --- a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsActions.kt +++ b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsActions.kt @@ -20,6 +20,7 @@ import androidx.benchmark.macro.MacrobenchmarkScope import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import com.google.samples.apps.nowinandroid.flingElementDownUp +import com.google.samples.apps.nowinandroid.waitAndFindObject fun MacrobenchmarkScope.goToInterestsScreen() { device.findObject(By.text("Interests")).click() @@ -34,7 +35,7 @@ fun MacrobenchmarkScope.goToInterestsScreen() { } fun MacrobenchmarkScope.interestsScrollTopicsDownUp() { - val topicsList = device.wait(Until.findObject(By.res("interests:topics")), 2_000) + val topicsList = device.waitAndFindObject(By.res("interests:topics"), 2_000) device.flingElementDownUp(topicsList) } From bf36edeed2d24ace68c06923b24c85babaa0563b Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Mon, 14 Aug 2023 18:59:23 +0200 Subject: [PATCH 02/20] Migrate `DesignSystemIssueRegistry` into generic `NiaIssueRegistry` --- ...ystemIssueRegistry.kt => NiaIssueRegistry.kt} | 16 ++++++++-------- ...m.android.tools.lint.client.api.IssueRegistry | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) rename lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/{designsystem/DesignSystemIssueRegistry.kt => NiaIssueRegistry.kt} (73%) diff --git a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/designsystem/DesignSystemIssueRegistry.kt b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt similarity index 73% rename from lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/designsystem/DesignSystemIssueRegistry.kt rename to lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt index bb7e971e3..333462770 100644 --- a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/designsystem/DesignSystemIssueRegistry.kt +++ b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2023 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. @@ -14,18 +14,18 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.lint.designsystem +package com.google.samples.apps.nowinandroid.lint import com.android.tools.lint.client.api.IssueRegistry import com.android.tools.lint.client.api.Vendor import com.android.tools.lint.detector.api.CURRENT_API +import com.google.samples.apps.nowinandroid.lint.designsystem.DesignSystemDetector -/** - * An issue registry that checks for incorrect usages of Compose Material APIs over equivalents in - * the Now in Android design system module. - */ -class DesignSystemIssueRegistry : IssueRegistry() { - override val issues = listOf(DesignSystemDetector.ISSUE) +class NiaIssueRegistry : IssueRegistry() { + + override val issues = listOf( + DesignSystemDetector.ISSUE, + ) override val api: Int = CURRENT_API diff --git a/lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry b/lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry index 4b8002da2..e673c27ff 100644 --- a/lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry +++ b/lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry @@ -14,4 +14,4 @@ # limitations under the License. # -com.google.samples.apps.nowinandroid.lint.designsystem.DesignSystemIssueRegistry +com.google.samples.apps.nowinandroid.lint.NiaIssueRegistry From df893558f92e7fd3320dad31cc2096fc7f9777cc Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Mon, 14 Aug 2023 13:29:50 +0200 Subject: [PATCH 03/20] Add missing test dependencies --- gradle/libs.versions.toml | 2 ++ lint/build.gradle.kts | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 60d827c0e..1d1aa90e0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -122,6 +122,8 @@ kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-cor kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "lint" } +lint-checks = { group = "com.android.tools.lint", name = "lint-checks", version.ref = "lint" } +lint-tests = { group = "com.android.tools.lint", name = "lint-tests", version.ref = "lint" } okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" } protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" } protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" } diff --git a/lint/build.gradle.kts b/lint/build.gradle.kts index c7032cf40..5f90d1e45 100644 --- a/lint/build.gradle.kts +++ b/lint/build.gradle.kts @@ -29,7 +29,7 @@ java { targetCompatibility = JavaVersion.VERSION_11 } -tasks.withType().configureEach { +tasks.withType { kotlinOptions { jvmTarget = JavaVersion.VERSION_11.toString() } @@ -38,4 +38,7 @@ tasks.withType().configureEach { dependencies { compileOnly(libs.kotlin.stdlib) compileOnly(libs.lint.api) + testImplementation(libs.lint.checks) + testImplementation(libs.lint.tests) + testImplementation(kotlin("test")) } From 66cc677353fd732175d149cc48bd0eb31530c601 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Mon, 14 Aug 2023 15:25:51 +0200 Subject: [PATCH 04/20] Add `TestMethodDetector` with tests --- .../nowinandroid/lint/NiaIssueRegistry.kt | 3 + .../nowinandroid/lint/TestMethodDetector.kt | 160 ++++++++++++++++++ .../lint/TestMethodDetectorTest.kt | 155 +++++++++++++++++ 3 files changed, 318 insertions(+) create mode 100644 lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetector.kt create mode 100644 lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt diff --git a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt index 333462770..f402b4039 100644 --- a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt +++ b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt @@ -25,6 +25,9 @@ class NiaIssueRegistry : IssueRegistry() { override val issues = listOf( DesignSystemDetector.ISSUE, + TestMethodDetector.UNDERSCORE, + TestMethodDetector.FORMAT, + TestMethodDetector.PREFIX, ) override val api: Int = CURRENT_API diff --git a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetector.kt b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetector.kt new file mode 100644 index 000000000..c7989542e --- /dev/null +++ b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetector.kt @@ -0,0 +1,160 @@ +/* + * Copyright 2023 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 + * + * https://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.google.samples.apps.nowinandroid.lint + +import com.android.tools.lint.detector.api.AnnotationInfo +import com.android.tools.lint.detector.api.AnnotationUsageInfo +import com.android.tools.lint.detector.api.Category.Companion.TESTING +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.LintFix +import com.android.tools.lint.detector.api.Scope.JAVA_FILE +import com.android.tools.lint.detector.api.Scope.TEST_SOURCES +import com.android.tools.lint.detector.api.Severity.WARNING +import com.android.tools.lint.detector.api.SourceCodeScanner +import com.android.tools.lint.detector.api.TextFormat.RAW +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.UElement +import java.util.EnumSet +import kotlin.io.path.Path + +/** + * A detector that checks for common patterns in naming the test methods: + * - [detectPrefix] removes unnecessary "test" prefix in all unit test. + * - [detectUnderscore] removes underscores in JVM unit test (and add backticks if necessary). + * - [detectFormat] Checks the `given_when_then` format of Android instrumented tests (backticks are not supported). + */ +class TestMethodDetector : Detector(), SourceCodeScanner { + + override fun applicableAnnotations() = listOf("org.junit.Test") + + override fun visitAnnotationUsage( + context: JavaContext, + element: UElement, + annotationInfo: AnnotationInfo, + usageInfo: AnnotationUsageInfo, + ) { + val method = usageInfo.referenced as? PsiMethod ?: return + + method.detectPrefix(context, usageInfo) + method.detectFormat(context, usageInfo) + method.detectUnderscore(context, usageInfo) + } + + private fun JavaContext.isAndroidTest() = Path("androidTest") in file.toPath() + + private fun PsiMethod.detectPrefix( + context: JavaContext, + usageInfo: AnnotationUsageInfo, + ) { + if (!name.startsWith("test")) return + context.report( + issue = PREFIX, + scope = usageInfo.usage, + location = context.getNameLocation(this), + message = PREFIX.getBriefDescription(RAW), + quickfixData = LintFix.create() + .name("Remove prefix") + .replace().pattern("""test[\s_]*""") + .with("") + .autoFix() + .build(), + ) + } + + private fun PsiMethod.detectFormat( + context: JavaContext, + usageInfo: AnnotationUsageInfo, + ) { + if (!context.isAndroidTest()) return + if ("""^[^\W_]+_[^\W_]+_[^\W_]+$""".toRegex().matches(name)) return + context.report( + issue = FORMAT, + scope = usageInfo.usage, + location = context.getNameLocation(this), + message = FORMAT.getBriefDescription(RAW), + ) + } + + private fun PsiMethod.detectUnderscore( + context: JavaContext, + usageInfo: AnnotationUsageInfo, + ) { + if (context.isAndroidTest()) return + if ("_" !in name) return + context.report( + issue = UNDERSCORE, + scope = usageInfo.usage, + location = context.getNameLocation(this), + message = UNDERSCORE.getBriefDescription(RAW), + quickfixData = LintFix.create() + .name("Replace underscores with spaces") + .replace() + .range(context.getNameLocation(this)) + .with( + name.replace("_", " ") + .removeSurrounding("`") + .let { """`$it`""" }, + ) + .autoFix() + .build(), + ) + } + + companion object { + + private fun issue( + id: String, + briefDescription: String, + explanation: String, + ): Issue = Issue.create( + id = id, + briefDescription = briefDescription, + explanation = explanation, + category = TESTING, + priority = 5, + severity = WARNING, + implementation = Implementation( + TestMethodDetector::class.java, + EnumSet.of(JAVA_FILE, TEST_SOURCES), + ), + ) + + @JvmField + val UNDERSCORE: Issue = issue( + id = "TestMethodUnderscore", + briefDescription = "Test method contains underscores", + explanation = "Test methods should not contains underscores.", + ) + + @JvmField + val PREFIX: Issue = issue( + id = "TestMethodPrefix", + briefDescription = "Test method starts with `test`", + explanation = "Test method should not start with `test`.", + ) + + @JvmField + val FORMAT: Issue = issue( + id = "TestMethodFormat", + briefDescription = "Test method does not follow the `given_when_then` format", + explanation = "Test method should follow the `given_when_then` format.", + ) + } +} diff --git a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt new file mode 100644 index 000000000..215efebf8 --- /dev/null +++ b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt @@ -0,0 +1,155 @@ +/* + * Copyright 2023 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 + * + * https://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.google.samples.apps.nowinandroid.lint + +import com.android.tools.lint.checks.infrastructure.TestFile +import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin +import com.android.tools.lint.checks.infrastructure.TestLintTask.lint +import com.google.samples.apps.nowinandroid.lint.TestMethodDetector.Companion.FORMAT +import com.google.samples.apps.nowinandroid.lint.TestMethodDetector.Companion.PREFIX +import org.junit.Test + +class TestMethodDetectorTest { + + @Test + fun `detect prefix`() { + lint().issues(PREFIX) + .files( + JUNIT_TEST_STUB, + kotlin( + """ + import org.junit.Test + class Test { + @Test + fun foo() = Unit + @Test + fun test_foo() = Unit + @Test + fun `test foo`() = Unit + } + """, + ).indented(), + ) + .run() + .expect( + """ + src/Test.kt:6: Warning: Test method starts with test [TestMethodWithTestPrefix] + fun test_foo() = Unit + ~~~~~~~~ + src/Test.kt:8: Warning: Test method starts with test [TestMethodWithTestPrefix] + fun `test foo`() = Unit + ~~~~~~~~~~ + 0 errors, 2 warnings + """.trimIndent(), + ) + .expectFixDiffs( + """ + Fix for src/Test.kt line 6: Remove underscores: + @@ -6 +6 + - fun test_foo() = Unit + + fun foo() = Unit + Fix for src/Test.kt line 8: Remove underscores: + @@ -8 +8 + - fun `test foo`() = Unit + + fun `foo`() = Unit + """.trimIndent(), + ) + } + + @Test + fun `detect format`() { + lint().issues(FORMAT) + .files( + JUNIT_TEST_STUB, + kotlin( + "src/androidTest/com/example/Test.kt", + """ + import org.junit.Test + class Test { + @Test + fun given_when_then() = Unit + @Test + fun given_foo_when_bar_then_baz() = Unit + } + """, + ).indented(), + ) + .run() + .expect( + """ + src/androidTest/com/example/Test.kt:6: Warning: Test method does not follow the given_when_then format [TestMethodGivenWhenThenFormatTest] + fun given_foo_when_bar_then_baz() = Unit + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 0 errors, 1 warnings + """.trimIndent(), + ) + } + + @Test + fun `detect underscores`() { + lint().issues(UNDERSCORES) + .files( + JUNIT_TEST_STUB, + kotlin( + """ + import org.junit.Test + class Test { + @Test + fun foo() = Unit + @Test + fun foo_bar_baz() = Unit + @Test + fun `foo_bar_baz`() = Unit + } + """, + ).indented(), + ) + .run() + .expect( + """ + src/Test.kt:6: Warning: Test method contains underscores [TestMethodContainsUnderscore] + fun foo_bar_baz() = Unit + ~~~~~~~~~~~ + src/Test.kt:8: Warning: Test method contains underscores [TestMethodContainsUnderscore] + fun `foo_bar_baz`() = Unit + ~~~~~~~~~~~~~ + 0 errors, 2 warnings + """.trimIndent(), + ) + .expectFixDiffs( + """ + Autofix for src/Test.kt line 6: Replace underscores with spaces: + @@ -6 +6 + - fun foo_bar_baz() = Unit + + fun `foo bar baz`() = Unit + Autofix for src/Test.kt line 8: Replace underscores with spaces: + @@ -8 +8 + - fun `foo_bar_baz`() = Unit + + fun `foo bar baz`() = Unit + """.trimIndent(), + ) + } + + private companion object { + private val JUNIT_TEST_STUB: TestFile = kotlin( + """ + package org.junit + annotation class Test + """, + ).indented() + } +} From f26b497bb21a27a63b849fcfc28f9473e41027d1 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Mon, 14 Aug 2023 16:18:51 +0200 Subject: [PATCH 05/20] Add `:lint:test` task to CI builds --- .github/workflows/Build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index d5d98bf36..4a4320590 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -47,7 +47,7 @@ jobs: path: '**/build/outputs/apk/**/*.apk' - name: Run local tests - run: ./gradlew testDemoDebug testProdDebug + run: ./gradlew testDemoDebug testProdDebug :lint:test test: runs-on: ubuntu-latest From 8f1652fa00525bf838d8dcbd2097260637297cf8 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Thu, 24 Aug 2023 19:25:21 +0200 Subject: [PATCH 06/20] Add missing import --- .../samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt index 215efebf8..1ab2de2d5 100644 --- a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt +++ b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt @@ -21,6 +21,7 @@ import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin import com.android.tools.lint.checks.infrastructure.TestLintTask.lint import com.google.samples.apps.nowinandroid.lint.TestMethodDetector.Companion.FORMAT import com.google.samples.apps.nowinandroid.lint.TestMethodDetector.Companion.PREFIX +import com.google.samples.apps.nowinandroid.lint.TestMethodDetector.Companion.UNDERSCORE import org.junit.Test class TestMethodDetectorTest { @@ -101,7 +102,7 @@ class TestMethodDetectorTest { @Test fun `detect underscores`() { - lint().issues(UNDERSCORES) + lint().issues(UNDERSCORE) .files( JUNIT_TEST_STUB, kotlin( From 437f36ed8b65dc2ffcfc7e215378e5c8aa8aa6d6 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Thu, 24 Aug 2023 19:28:31 +0200 Subject: [PATCH 07/20] Fix test --- .../nowinandroid/lint/TestMethodDetectorTest.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt index 1ab2de2d5..ed24b5ccc 100644 --- a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt +++ b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt @@ -48,10 +48,10 @@ class TestMethodDetectorTest { .run() .expect( """ - src/Test.kt:6: Warning: Test method starts with test [TestMethodWithTestPrefix] + src/Test.kt:6: Warning: Test method starts with test [TestMethodPrefix] fun test_foo() = Unit ~~~~~~~~ - src/Test.kt:8: Warning: Test method starts with test [TestMethodWithTestPrefix] + src/Test.kt:8: Warning: Test method starts with test [TestMethodPrefix] fun `test foo`() = Unit ~~~~~~~~~~ 0 errors, 2 warnings @@ -59,11 +59,11 @@ class TestMethodDetectorTest { ) .expectFixDiffs( """ - Fix for src/Test.kt line 6: Remove underscores: + Autofix for src/Test.kt line 6: Remove prefix: @@ -6 +6 - fun test_foo() = Unit + fun foo() = Unit - Fix for src/Test.kt line 8: Remove underscores: + Autofix for src/Test.kt line 8: Remove prefix: @@ -8 +8 - fun `test foo`() = Unit + fun `foo`() = Unit @@ -92,7 +92,7 @@ class TestMethodDetectorTest { .run() .expect( """ - src/androidTest/com/example/Test.kt:6: Warning: Test method does not follow the given_when_then format [TestMethodGivenWhenThenFormatTest] + src/androidTest/com/example/Test.kt:6: Warning: Test method does not follow the given_when_then format [TestMethodFormat] fun given_foo_when_bar_then_baz() = Unit ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 0 errors, 1 warnings @@ -122,10 +122,10 @@ class TestMethodDetectorTest { .run() .expect( """ - src/Test.kt:6: Warning: Test method contains underscores [TestMethodContainsUnderscore] + src/Test.kt:6: Warning: Test method contains underscores [TestMethodUnderscore] fun foo_bar_baz() = Unit ~~~~~~~~~~~ - src/Test.kt:8: Warning: Test method contains underscores [TestMethodContainsUnderscore] + src/Test.kt:8: Warning: Test method contains underscores [TestMethodUnderscore] fun `foo_bar_baz`() = Unit ~~~~~~~~~~~~~ 0 errors, 2 warnings From 28acb57ca06b6a7345798369b6727f2e992dc456 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Fri, 13 Oct 2023 23:05:38 +0200 Subject: [PATCH 08/20] Update regex to match expected format --- .../nowinandroid/lint/TestMethodDetector.kt | 6 ++--- .../lint/TestMethodDetectorTest.kt | 23 +++++++++++++++---- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetector.kt b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetector.kt index c7989542e..bcb75ffd5 100644 --- a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetector.kt +++ b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetector.kt @@ -83,7 +83,7 @@ class TestMethodDetector : Detector(), SourceCodeScanner { usageInfo: AnnotationUsageInfo, ) { if (!context.isAndroidTest()) return - if ("""^[^\W_]+_[^\W_]+_[^\W_]+$""".toRegex().matches(name)) return + if ("""[^\W_]+(_[^\W_]+){1,2}""".toRegex().matches(name)) return context.report( issue = FORMAT, scope = usageInfo.usage, @@ -153,8 +153,8 @@ class TestMethodDetector : Detector(), SourceCodeScanner { @JvmField val FORMAT: Issue = issue( id = "TestMethodFormat", - briefDescription = "Test method does not follow the `given_when_then` format", - explanation = "Test method should follow the `given_when_then` format.", + briefDescription = "Test method does not follow the `given_when_then` or `when_then` format", + explanation = "Test method should follow the `given_when_then` or `when_then` format.", ) } } diff --git a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt index ed24b5ccc..592b4f8fb 100644 --- a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt +++ b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt @@ -81,10 +81,17 @@ class TestMethodDetectorTest { """ import org.junit.Test class Test { + @Test + fun when_then() = Unit @Test fun given_when_then() = Unit + + @Test + fun foo() = Unit + @Test + fun foo_bar_baz_qux() = Unit @Test - fun given_foo_when_bar_then_baz() = Unit + fun `foo bar baz`() = Unit } """, ).indented(), @@ -92,10 +99,16 @@ class TestMethodDetectorTest { .run() .expect( """ - src/androidTest/com/example/Test.kt:6: Warning: Test method does not follow the given_when_then format [TestMethodFormat] - fun given_foo_when_bar_then_baz() = Unit - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 0 errors, 1 warnings + src/androidTest/com/example/Test.kt:9: Warning: Test method does not follow the given_when_then or when_then format [TestMethodFormat] + fun foo() = Unit + ~~~ + src/androidTest/com/example/Test.kt:11: Warning: Test method does not follow the given_when_then or when_then format [TestMethodFormat] + fun foo_bar_baz_qux() = Unit + ~~~~~~~~~~~~~~~ + src/androidTest/com/example/Test.kt:13: Warning: Test method does not follow the given_when_then or when_then format [TestMethodFormat] + fun `foo bar baz`() = Unit + ~~~~~~~~~~~~~ + 0 errors, 3 warnings """.trimIndent(), ) } From b4ee73026309e909142dbf3a5747d114950d8b56 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Fri, 13 Oct 2023 23:07:21 +0200 Subject: [PATCH 09/20] Rename `TestMethodDetector` to `TestMethodNameDetector` as suggested --- .../samples/apps/nowinandroid/lint/NiaIssueRegistry.kt | 6 +++--- .../{TestMethodDetector.kt => TestMethodNameDetector.kt} | 4 ++-- ...ethodDetectorTest.kt => TestMethodNameDetectorTest.kt} | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) rename lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/{TestMethodDetector.kt => TestMethodNameDetector.kt} (98%) rename lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/{TestMethodDetectorTest.kt => TestMethodNameDetectorTest.kt} (95%) diff --git a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt index f402b4039..08783dfc3 100644 --- a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt +++ b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt @@ -25,9 +25,9 @@ class NiaIssueRegistry : IssueRegistry() { override val issues = listOf( DesignSystemDetector.ISSUE, - TestMethodDetector.UNDERSCORE, - TestMethodDetector.FORMAT, - TestMethodDetector.PREFIX, + TestMethodNameDetector.UNDERSCORE, + TestMethodNameDetector.FORMAT, + TestMethodNameDetector.PREFIX, ) override val api: Int = CURRENT_API diff --git a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetector.kt b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetector.kt similarity index 98% rename from lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetector.kt rename to lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetector.kt index bcb75ffd5..01791b2d1 100644 --- a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetector.kt +++ b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetector.kt @@ -40,7 +40,7 @@ import kotlin.io.path.Path * - [detectUnderscore] removes underscores in JVM unit test (and add backticks if necessary). * - [detectFormat] Checks the `given_when_then` format of Android instrumented tests (backticks are not supported). */ -class TestMethodDetector : Detector(), SourceCodeScanner { +class TestMethodNameDetector : Detector(), SourceCodeScanner { override fun applicableAnnotations() = listOf("org.junit.Test") @@ -131,7 +131,7 @@ class TestMethodDetector : Detector(), SourceCodeScanner { priority = 5, severity = WARNING, implementation = Implementation( - TestMethodDetector::class.java, + TestMethodNameDetector::class.java, EnumSet.of(JAVA_FILE, TEST_SOURCES), ), ) diff --git a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetectorTest.kt similarity index 95% rename from lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt rename to lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetectorTest.kt index 592b4f8fb..bc84b2b4a 100644 --- a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodDetectorTest.kt +++ b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetectorTest.kt @@ -19,12 +19,12 @@ package com.google.samples.apps.nowinandroid.lint import com.android.tools.lint.checks.infrastructure.TestFile import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin import com.android.tools.lint.checks.infrastructure.TestLintTask.lint -import com.google.samples.apps.nowinandroid.lint.TestMethodDetector.Companion.FORMAT -import com.google.samples.apps.nowinandroid.lint.TestMethodDetector.Companion.PREFIX -import com.google.samples.apps.nowinandroid.lint.TestMethodDetector.Companion.UNDERSCORE +import com.google.samples.apps.nowinandroid.lint.TestMethodNameDetector.Companion.FORMAT +import com.google.samples.apps.nowinandroid.lint.TestMethodNameDetector.Companion.PREFIX +import com.google.samples.apps.nowinandroid.lint.TestMethodNameDetector.Companion.UNDERSCORE import org.junit.Test -class TestMethodDetectorTest { +class TestMethodNameDetectorTest { @Test fun `detect prefix`() { From 8c9fe64ac0f77b0024764e0e94e5533eb9d32a0c Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Fri, 13 Oct 2023 23:08:22 +0200 Subject: [PATCH 10/20] Restore lazy API call --- lint/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lint/build.gradle.kts b/lint/build.gradle.kts index 5f90d1e45..acb540c3b 100644 --- a/lint/build.gradle.kts +++ b/lint/build.gradle.kts @@ -29,7 +29,7 @@ java { targetCompatibility = JavaVersion.VERSION_11 } -tasks.withType { +tasks.withType().configureEach { kotlinOptions { jvmTarget = JavaVersion.VERSION_11.toString() } From 8c7055f8ce2b837072f421ab682f70fc45912520 Mon Sep 17 00:00:00 2001 From: TM Date: Mon, 16 Oct 2023 00:31:36 -0700 Subject: [PATCH 11/20] Create different icons for each build variant Added color values for DEBUG and BENCHMARK build variants. --- app/src/benchmark/res/values-night/colors.xml | 20 ++++++++++++++++ app/src/benchmark/res/values/colors.xml | 23 +++++++++++++++++++ app/src/debug/res/values-night/colors.xml | 20 ++++++++++++++++ app/src/debug/res/values/colors.xml | 23 +++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 app/src/benchmark/res/values-night/colors.xml create mode 100644 app/src/benchmark/res/values/colors.xml create mode 100644 app/src/debug/res/values-night/colors.xml create mode 100644 app/src/debug/res/values/colors.xml diff --git a/app/src/benchmark/res/values-night/colors.xml b/app/src/benchmark/res/values-night/colors.xml new file mode 100644 index 000000000..e2c4fc0b1 --- /dev/null +++ b/app/src/benchmark/res/values-night/colors.xml @@ -0,0 +1,20 @@ + + + + #2296F3 + #FFFFFF + diff --git a/app/src/benchmark/res/values/colors.xml b/app/src/benchmark/res/values/colors.xml new file mode 100644 index 000000000..7da2e4639 --- /dev/null +++ b/app/src/benchmark/res/values/colors.xml @@ -0,0 +1,23 @@ + + + + + #4D000000 + + #2296F3 + #000000 + diff --git a/app/src/debug/res/values-night/colors.xml b/app/src/debug/res/values-night/colors.xml new file mode 100644 index 000000000..62ae86f86 --- /dev/null +++ b/app/src/debug/res/values-night/colors.xml @@ -0,0 +1,20 @@ + + + + #FFFFFF + #2296F3 + diff --git a/app/src/debug/res/values/colors.xml b/app/src/debug/res/values/colors.xml new file mode 100644 index 000000000..b07b3c4ce --- /dev/null +++ b/app/src/debug/res/values/colors.xml @@ -0,0 +1,23 @@ + + + + + #4D000000 + + #000000 + #2296F3 + From cd87a5ad7036b5eee70aa8be5ea98f23000e037c Mon Sep 17 00:00:00 2001 From: TM Date: Tue, 17 Oct 2023 11:03:46 -0700 Subject: [PATCH 12/20] Use colors from design palette in build variant icons --- app/src/benchmark/res/values-night/colors.xml | 6 ++++-- app/src/benchmark/res/values/colors.xml | 5 +++-- app/src/debug/res/values-night/colors.xml | 4 +++- app/src/debug/res/values/colors.xml | 3 ++- app/src/main/res/values/themes.xml | 1 + 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/benchmark/res/values-night/colors.xml b/app/src/benchmark/res/values-night/colors.xml index e2c4fc0b1..8d39d7588 100644 --- a/app/src/benchmark/res/values-night/colors.xml +++ b/app/src/benchmark/res/values-night/colors.xml @@ -15,6 +15,8 @@ limitations under the License. --> - #2296F3 - #FFFFFF + #FF006780 + + #FFFFFF + @color/blue40 diff --git a/app/src/benchmark/res/values/colors.xml b/app/src/benchmark/res/values/colors.xml index 7da2e4639..184477d97 100644 --- a/app/src/benchmark/res/values/colors.xml +++ b/app/src/benchmark/res/values/colors.xml @@ -17,7 +17,8 @@ #4D000000 + #FF006780 - #2296F3 - #000000 + #000000 + @color/blue40 diff --git a/app/src/debug/res/values-night/colors.xml b/app/src/debug/res/values-night/colors.xml index 62ae86f86..17eda47b6 100644 --- a/app/src/debug/res/values-night/colors.xml +++ b/app/src/debug/res/values-night/colors.xml @@ -15,6 +15,8 @@ limitations under the License. --> + #FFA23F16 + #FFFFFF - #2296F3 + @color/orange40 diff --git a/app/src/debug/res/values/colors.xml b/app/src/debug/res/values/colors.xml index b07b3c4ce..8fde71bfc 100644 --- a/app/src/debug/res/values/colors.xml +++ b/app/src/debug/res/values/colors.xml @@ -17,7 +17,8 @@ #4D000000 + #FFA23F16 #000000 - #2296F3 + @color/orange40 diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 7cdd25527..c75ef8a98 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -33,4 +33,5 @@ @style/Theme.Nia + From 1e0e1140ffb9dcb8593cc3a04eee377bb4dc52b1 Mon Sep 17 00:00:00 2001 From: TM Date: Wed, 18 Oct 2023 18:21:37 -0700 Subject: [PATCH 13/20] Removed Comments --- app/src/benchmark/res/values/colors.xml | 1 - app/src/debug/res/values/colors.xml | 1 - 2 files changed, 2 deletions(-) diff --git a/app/src/benchmark/res/values/colors.xml b/app/src/benchmark/res/values/colors.xml index 184477d97..78797a661 100644 --- a/app/src/benchmark/res/values/colors.xml +++ b/app/src/benchmark/res/values/colors.xml @@ -15,7 +15,6 @@ limitations under the License. --> - #4D000000 #FF006780 diff --git a/app/src/debug/res/values/colors.xml b/app/src/debug/res/values/colors.xml index 8fde71bfc..dc5364335 100644 --- a/app/src/debug/res/values/colors.xml +++ b/app/src/debug/res/values/colors.xml @@ -15,7 +15,6 @@ limitations under the License. --> - #4D000000 #FFA23F16 From ecb7528a2c0a28d7ec2c30ba2d59898fa1cde37b Mon Sep 17 00:00:00 2001 From: TM Date: Thu, 19 Oct 2023 15:47:23 -0700 Subject: [PATCH 14/20] Inline color values for app icons --- app/src/benchmark/res/values-night/colors.xml | 4 +--- app/src/benchmark/res/values/colors.xml | 5 +---- app/src/debug/res/values-night/colors.xml | 4 +--- app/src/debug/res/values/colors.xml | 5 +---- app/src/main/res/values/themes.xml | 1 - 5 files changed, 4 insertions(+), 15 deletions(-) diff --git a/app/src/benchmark/res/values-night/colors.xml b/app/src/benchmark/res/values-night/colors.xml index 8d39d7588..677eb4e03 100644 --- a/app/src/benchmark/res/values-night/colors.xml +++ b/app/src/benchmark/res/values-night/colors.xml @@ -15,8 +15,6 @@ limitations under the License. --> - #FF006780 - #FFFFFF - @color/blue40 + #FF006780 diff --git a/app/src/benchmark/res/values/colors.xml b/app/src/benchmark/res/values/colors.xml index 78797a661..d33b7ba72 100644 --- a/app/src/benchmark/res/values/colors.xml +++ b/app/src/benchmark/res/values/colors.xml @@ -15,9 +15,6 @@ limitations under the License. --> - #4D000000 - #FF006780 - #000000 - @color/blue40 + #FF006780 diff --git a/app/src/debug/res/values-night/colors.xml b/app/src/debug/res/values-night/colors.xml index 17eda47b6..d6a4c98e0 100644 --- a/app/src/debug/res/values-night/colors.xml +++ b/app/src/debug/res/values-night/colors.xml @@ -15,8 +15,6 @@ limitations under the License. --> - #FFA23F16 - #FFFFFF - @color/orange40 + #FFA23F16 diff --git a/app/src/debug/res/values/colors.xml b/app/src/debug/res/values/colors.xml index dc5364335..6365ddb3f 100644 --- a/app/src/debug/res/values/colors.xml +++ b/app/src/debug/res/values/colors.xml @@ -15,9 +15,6 @@ limitations under the License. --> - #4D000000 - #FFA23F16 - #000000 - @color/orange40 + #FFA23F16 diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index c75ef8a98..7cdd25527 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -33,5 +33,4 @@ @style/Theme.Nia - From b985f6515862c1ade3308feee8a29ec1cd30762b Mon Sep 17 00:00:00 2001 From: Don Turner Date: Fri, 20 Oct 2023 12:06:59 +0100 Subject: [PATCH 15/20] Remove underscore check Change-Id: Iec1d07787a6e1cd350b9d8d082e729ef62492013 --- .../nowinandroid/lint/NiaIssueRegistry.kt | 1 - .../lint/TestMethodNameDetector.kt | 34 -------------- .../lint/TestMethodNameDetectorTest.kt | 46 ------------------- 3 files changed, 81 deletions(-) diff --git a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt index 08783dfc3..27ae46ab3 100644 --- a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt +++ b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt @@ -25,7 +25,6 @@ class NiaIssueRegistry : IssueRegistry() { override val issues = listOf( DesignSystemDetector.ISSUE, - TestMethodNameDetector.UNDERSCORE, TestMethodNameDetector.FORMAT, TestMethodNameDetector.PREFIX, ) diff --git a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetector.kt b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetector.kt index 01791b2d1..532994d99 100644 --- a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetector.kt +++ b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetector.kt @@ -37,7 +37,6 @@ import kotlin.io.path.Path /** * A detector that checks for common patterns in naming the test methods: * - [detectPrefix] removes unnecessary "test" prefix in all unit test. - * - [detectUnderscore] removes underscores in JVM unit test (and add backticks if necessary). * - [detectFormat] Checks the `given_when_then` format of Android instrumented tests (backticks are not supported). */ class TestMethodNameDetector : Detector(), SourceCodeScanner { @@ -54,7 +53,6 @@ class TestMethodNameDetector : Detector(), SourceCodeScanner { method.detectPrefix(context, usageInfo) method.detectFormat(context, usageInfo) - method.detectUnderscore(context, usageInfo) } private fun JavaContext.isAndroidTest() = Path("androidTest") in file.toPath() @@ -92,31 +90,6 @@ class TestMethodNameDetector : Detector(), SourceCodeScanner { ) } - private fun PsiMethod.detectUnderscore( - context: JavaContext, - usageInfo: AnnotationUsageInfo, - ) { - if (context.isAndroidTest()) return - if ("_" !in name) return - context.report( - issue = UNDERSCORE, - scope = usageInfo.usage, - location = context.getNameLocation(this), - message = UNDERSCORE.getBriefDescription(RAW), - quickfixData = LintFix.create() - .name("Replace underscores with spaces") - .replace() - .range(context.getNameLocation(this)) - .with( - name.replace("_", " ") - .removeSurrounding("`") - .let { """`$it`""" }, - ) - .autoFix() - .build(), - ) - } - companion object { private fun issue( @@ -136,13 +109,6 @@ class TestMethodNameDetector : Detector(), SourceCodeScanner { ), ) - @JvmField - val UNDERSCORE: Issue = issue( - id = "TestMethodUnderscore", - briefDescription = "Test method contains underscores", - explanation = "Test methods should not contains underscores.", - ) - @JvmField val PREFIX: Issue = issue( id = "TestMethodPrefix", diff --git a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetectorTest.kt b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetectorTest.kt index bc84b2b4a..8da173285 100644 --- a/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetectorTest.kt +++ b/lint/src/test/kotlin/com/google/samples/apps/nowinandroid/lint/TestMethodNameDetectorTest.kt @@ -21,7 +21,6 @@ import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin import com.android.tools.lint.checks.infrastructure.TestLintTask.lint import com.google.samples.apps.nowinandroid.lint.TestMethodNameDetector.Companion.FORMAT import com.google.samples.apps.nowinandroid.lint.TestMethodNameDetector.Companion.PREFIX -import com.google.samples.apps.nowinandroid.lint.TestMethodNameDetector.Companion.UNDERSCORE import org.junit.Test class TestMethodNameDetectorTest { @@ -113,51 +112,6 @@ class TestMethodNameDetectorTest { ) } - @Test - fun `detect underscores`() { - lint().issues(UNDERSCORE) - .files( - JUNIT_TEST_STUB, - kotlin( - """ - import org.junit.Test - class Test { - @Test - fun foo() = Unit - @Test - fun foo_bar_baz() = Unit - @Test - fun `foo_bar_baz`() = Unit - } - """, - ).indented(), - ) - .run() - .expect( - """ - src/Test.kt:6: Warning: Test method contains underscores [TestMethodUnderscore] - fun foo_bar_baz() = Unit - ~~~~~~~~~~~ - src/Test.kt:8: Warning: Test method contains underscores [TestMethodUnderscore] - fun `foo_bar_baz`() = Unit - ~~~~~~~~~~~~~ - 0 errors, 2 warnings - """.trimIndent(), - ) - .expectFixDiffs( - """ - Autofix for src/Test.kt line 6: Replace underscores with spaces: - @@ -6 +6 - - fun foo_bar_baz() = Unit - + fun `foo bar baz`() = Unit - Autofix for src/Test.kt line 8: Replace underscores with spaces: - @@ -8 +8 - - fun `foo_bar_baz`() = Unit - + fun `foo bar baz`() = Unit - """.trimIndent(), - ) - } - private companion object { private val JUNIT_TEST_STUB: TestFile = kotlin( """ From adb627bf9472eea10c78e6f3a8825d5a09255023 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Fri, 20 Oct 2023 15:25:05 +0200 Subject: [PATCH 16/20] Update lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt --- .../google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt index 27ae46ab3..b806312fd 100644 --- a/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt +++ b/lint/src/main/kotlin/com/google/samples/apps/nowinandroid/lint/NiaIssueRegistry.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright 2022 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. From 22be30f4b925f2e694bd68c3ca82c8ad260ffa7f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 15:42:07 +0000 Subject: [PATCH 17/20] Update all dependencies --- gradle/libs.versions.toml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 60d827c0e..693e934ee 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,16 +5,16 @@ androidGradlePlugin = "8.1.2" androidxActivity = "1.8.0" androidxAppCompat = "1.6.1" androidxBrowser = "1.6.0" -androidxComposeBom = "2023.10.00" +androidxComposeBom = "2023.10.01" androidxComposeCompiler = "1.5.3" -androidxComposeRuntimeTracing = "1.0.0-alpha03" +androidxComposeRuntimeTracing = "1.0.0-alpha04" androidxCore = "1.12.0" androidxCoreSplashscreen = "1.0.1" androidxDataStore = "1.0.0" androidxEspresso = "3.5.1" androidxHiltNavigationCompose = "1.0.0" androidxLifecycle = "2.6.2" -androidxMacroBenchmark = "1.2.0-rc02" +androidxMacroBenchmark = "1.2.0" androidxMetrics = "1.0.0-alpha04" androidxNavigation = "2.7.4" androidxProfileinstaller = "1.3.1" @@ -26,9 +26,9 @@ androidxTestRunner = "1.5.2" androidxTracing = "1.1.0" androidxUiAutomator = "2.2.0" androidxWindowManager = "1.1.0" -androidxWork = "2.9.0-beta01" +androidxWork = "2.9.0-rc01" coil = "2.4.0" -firebaseBom = "32.3.1" +firebaseBom = "32.4.0" firebaseCrashlyticsPlugin = "2.9.9" firebasePerfPlugin = "1.4.2" gmsPlugin = "4.4.0" @@ -44,14 +44,14 @@ kotlinxDatetime = "0.4.1" kotlinxSerializationJson = "1.6.0" ksp = "1.9.10-1.0.13" lint = "31.1.2" -okhttp = "4.11.0" +okhttp = "4.12.0" protobuf = "3.24.4" protobufPlugin = "0.9.4" retrofit = "2.9.0" retrofitKotlinxSerializationJson = "1.0.0" robolectric = "4.10.3" -roborazzi = "1.5.0" -room = "2.5.2" +roborazzi = "1.6.0" +room = "2.6.0" secrets = "2.0.1" turbine = "1.0.0" From 96f9d15dde0f0c431ec5aee94feba7fd74f06463 Mon Sep 17 00:00:00 2001 From: Don Turner Date: Tue, 24 Oct 2023 15:44:10 +0100 Subject: [PATCH 18/20] Rollback runtime tracing version to avoid pulling in new Compose runtime Change-Id: Ie194b90d6bff6b738cabac6c9708f037eb18ea08 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 693e934ee..770602996 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ androidxAppCompat = "1.6.1" androidxBrowser = "1.6.0" androidxComposeBom = "2023.10.01" androidxComposeCompiler = "1.5.3" -androidxComposeRuntimeTracing = "1.0.0-alpha04" +androidxComposeRuntimeTracing = "1.0.0-alpha03" androidxCore = "1.12.0" androidxCoreSplashscreen = "1.0.1" androidxDataStore = "1.0.0" From a2aa831d6dc766551a1349a4513771438de4f8b7 Mon Sep 17 00:00:00 2001 From: kimdowoo Date: Wed, 25 Oct 2023 21:06:48 +0900 Subject: [PATCH 19/20] refactor/Convert object to data object in sealed interface --- .../nowinandroid/feature/settings/SettingsViewModel.kt | 2 +- .../apps/nowinandroid/feature/topic/TopicViewModel.kt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt index 33bf58a2c..a49c0d512 100644 --- a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt @@ -87,6 +87,6 @@ data class UserEditableSettings( ) sealed interface SettingsUiState { - object Loading : SettingsUiState + data object Loading : SettingsUiState data class Success(val settings: UserEditableSettings) : SettingsUiState } diff --git a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt index 1c39f29da..6adfe0a67 100644 --- a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt +++ b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt @@ -177,12 +177,12 @@ private fun newsUiState( sealed interface TopicUiState { data class Success(val followableTopic: FollowableTopic) : TopicUiState - object Error : TopicUiState - object Loading : TopicUiState + data object Error : TopicUiState + data object Loading : TopicUiState } sealed interface NewsUiState { data class Success(val news: List) : NewsUiState - object Error : NewsUiState - object Loading : NewsUiState + data object Error : NewsUiState + data object Loading : NewsUiState } From f4d115c0f9bae699b9f066b3b1761f2e3542dcce Mon Sep 17 00:00:00 2001 From: Alex Vanyo Date: Wed, 25 Oct 2023 17:55:03 -0700 Subject: [PATCH 20/20] Add automatic checks for badging Change-Id: Ic8fca86d7aa84675fa91fb1aa99abcc6e19d663e --- .github/workflows/Build.yaml | 3 + app/prodRelease-badging.txt | 121 +++++++++++++++ .../AndroidApplicationConventionPlugin.kt | 4 + .../samples/apps/nowinandroid/Badging.kt | 143 ++++++++++++++++++ 4 files changed, 271 insertions(+) create mode 100644 app/prodRelease-badging.txt create mode 100644 build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index 4a4320590..ccea5dc4f 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -125,6 +125,9 @@ jobs: name: lint-reports path: '**/build/reports/lint-results-*.html' + - name: Check badging + run: ./gradlew :app:checkProdReleaseBadging + androidTest: needs: build runs-on: macOS-latest # enables hardware acceleration in the virtual machine diff --git a/app/prodRelease-badging.txt b/app/prodRelease-badging.txt new file mode 100644 index 000000000..6c3a859c7 --- /dev/null +++ b/app/prodRelease-badging.txt @@ -0,0 +1,121 @@ +package: name='com.google.samples.apps.nowinandroid' versionCode='8' versionName='0.1.2' platformBuildVersionName='14' platformBuildVersionCode='34' compileSdkVersion='34' compileSdkVersionCodename='14' +sdkVersion:'21' +targetSdkVersion:'34' +uses-permission: name='android.permission.INTERNET' +uses-permission: name='android.permission.ACCESS_NETWORK_STATE' +uses-permission: name='android.permission.POST_NOTIFICATIONS' +uses-permission: name='android.permission.WAKE_LOCK' +uses-permission: name='com.google.android.c2dm.permission.RECEIVE' +uses-permission: name='com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE' +uses-permission: name='android.permission.RECEIVE_BOOT_COMPLETED' +uses-permission: name='android.permission.FOREGROUND_SERVICE' +uses-permission: name='com.google.samples.apps.nowinandroid.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION' +application-label:'Now in Android' +application-label-af:'Now in Android' +application-label-am:'Now in Android' +application-label-ar:'Now in Android' +application-label-as:'Now in Android' +application-label-az:'Now in Android' +application-label-be:'Now in Android' +application-label-bg:'Now in Android' +application-label-bn:'Now in Android' +application-label-bs:'Now in Android' +application-label-ca:'Now in Android' +application-label-cs:'Now in Android' +application-label-da:'Now in Android' +application-label-de:'Now in Android' +application-label-el:'Now in Android' +application-label-en-AU:'Now in Android' +application-label-en-CA:'Now in Android' +application-label-en-GB:'Now in Android' +application-label-en-IN:'Now in Android' +application-label-en-XC:'Now in Android' +application-label-es:'Now in Android' +application-label-es-US:'Now in Android' +application-label-et:'Now in Android' +application-label-eu:'Now in Android' +application-label-fa:'Now in Android' +application-label-fi:'Now in Android' +application-label-fr:'Now in Android' +application-label-fr-CA:'Now in Android' +application-label-gl:'Now in Android' +application-label-gu:'Now in Android' +application-label-hi:'Now in Android' +application-label-hr:'Now in Android' +application-label-hu:'Now in Android' +application-label-hy:'Now in Android' +application-label-in:'Now in Android' +application-label-is:'Now in Android' +application-label-it:'Now in Android' +application-label-iw:'Now in Android' +application-label-ja:'Now in Android' +application-label-ka:'Now in Android' +application-label-kk:'Now in Android' +application-label-km:'Now in Android' +application-label-kn:'Now in Android' +application-label-ko:'Now in Android' +application-label-ky:'Now in Android' +application-label-lo:'Now in Android' +application-label-lt:'Now in Android' +application-label-lv:'Now in Android' +application-label-mk:'Now in Android' +application-label-ml:'Now in Android' +application-label-mn:'Now in Android' +application-label-mr:'Now in Android' +application-label-ms:'Now in Android' +application-label-my:'Now in Android' +application-label-nb:'Now in Android' +application-label-ne:'Now in Android' +application-label-nl:'Now in Android' +application-label-or:'Now in Android' +application-label-pa:'Now in Android' +application-label-pl:'Now in Android' +application-label-pt:'Now in Android' +application-label-pt-BR:'Now in Android' +application-label-pt-PT:'Now in Android' +application-label-ro:'Now in Android' +application-label-ru:'Now in Android' +application-label-si:'Now in Android' +application-label-sk:'Now in Android' +application-label-sl:'Now in Android' +application-label-sq:'Now in Android' +application-label-sr:'Now in Android' +application-label-sr-Latn:'Now in Android' +application-label-sv:'Now in Android' +application-label-sw:'Now in Android' +application-label-ta:'Now in Android' +application-label-te:'Now in Android' +application-label-th:'Now in Android' +application-label-tl:'Now in Android' +application-label-tr:'Now in Android' +application-label-uk:'Now in Android' +application-label-ur:'Now in Android' +application-label-uz:'Now in Android' +application-label-vi:'Now in Android' +application-label-zh-CN:'Now in Android' +application-label-zh-HK:'Now in Android' +application-label-zh-TW:'Now in Android' +application-label-zu:'Now in Android' +application-icon-120:'res/mipmap-anydpi-v26/ic_launcher.xml' +application-icon-160:'res/mipmap-anydpi-v26/ic_launcher.xml' +application-icon-240:'res/mipmap-anydpi-v26/ic_launcher.xml' +application-icon-320:'res/mipmap-anydpi-v26/ic_launcher.xml' +application-icon-480:'res/mipmap-anydpi-v26/ic_launcher.xml' +application-icon-640:'res/mipmap-anydpi-v26/ic_launcher.xml' +application-icon-65534:'res/mipmap-anydpi-v26/ic_launcher.xml' +application: label='Now in Android' icon='res/mipmap-anydpi-v26/ic_launcher.xml' +launchable-activity: name='com.google.samples.apps.nowinandroid.MainActivity' label='' icon='' +uses-library-not-required:'androidx.window.extensions' +uses-library-not-required:'androidx.window.sidecar' +uses-library-not-required:'android.ext.adservices' +feature-group: label='' + uses-feature: name='android.hardware.faketouch' + uses-implied-feature: name='android.hardware.faketouch' reason='default feature for all apps' +main +other-activities +other-receivers +other-services +supports-screens: 'small' 'normal' 'large' 'xlarge' +supports-any-density: 'true' +locales: '--_--' 'af' 'am' 'ar' 'as' 'az' 'be' 'bg' 'bn' 'bs' 'ca' 'cs' 'da' 'de' 'el' 'en-AU' 'en-CA' 'en-GB' 'en-IN' 'en-XC' 'es' 'es-US' 'et' 'eu' 'fa' 'fi' 'fr' 'fr-CA' 'gl' 'gu' 'hi' 'hr' 'hu' 'hy' 'in' 'is' 'it' 'iw' 'ja' 'ka' 'kk' 'km' 'kn' 'ko' 'ky' 'lo' 'lt' 'lv' 'mk' 'ml' 'mn' 'mr' 'ms' 'my' 'nb' 'ne' 'nl' 'or' 'pa' 'pl' 'pt' 'pt-BR' 'pt-PT' 'ro' 'ru' 'si' 'sk' 'sl' 'sq' 'sr' 'sr-Latn' 'sv' 'sw' 'ta' 'te' 'th' 'tl' 'tr' 'uk' 'ur' 'uz' 'vi' 'zh-CN' 'zh-HK' 'zh-TW' 'zu' +densities: '120' '160' '240' '320' '480' '640' '65534' diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt index 50baf3dc6..f73ed1478 100644 --- a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt @@ -17,11 +17,14 @@ import com.android.build.api.dsl.ApplicationExtension import com.google.samples.apps.nowinandroid.configureGradleManagedDevices import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.android.build.gradle.BaseExtension +import com.google.samples.apps.nowinandroid.configureBadgingTasks import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configurePrintApksTask import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.getByType class AndroidApplicationConventionPlugin : Plugin { override fun apply(target: Project) { @@ -39,6 +42,7 @@ class AndroidApplicationConventionPlugin : Plugin { } extensions.configure { configurePrintApksTask(this) + configureBadgingTasks(extensions.getByType(), this) } } } diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt new file mode 100644 index 000000000..609961a2f --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/Badging.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2023 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 + * + * https://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.google.samples.apps.nowinandroid + +import com.android.build.api.artifact.SingleArtifact +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.android.build.gradle.BaseExtension +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.Copy +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.register +import org.gradle.language.base.plugins.LifecycleBasePlugin +import org.gradle.process.ExecOperations +import java.io.File +import java.nio.file.Files +import javax.inject.Inject + +abstract class GenerateBadgingTask : DefaultTask() { + + @get:OutputFile + abstract val badging: RegularFileProperty + + @get:InputFile + abstract val apk: RegularFileProperty + + @get:InputFile + abstract val aapt2Executable: RegularFileProperty + + @get:Inject + abstract val execOperations: ExecOperations + + @TaskAction + fun taskAction() { + execOperations.exec { + commandLine( + aapt2Executable.get().asFile.absolutePath, + "dump", + "badging", + apk.get().asFile.absolutePath + ) + standardOutput = badging.asFile.get().outputStream() + } + } +} + +abstract class CheckBadgingTask : DefaultTask() { + + // In order for the task to be up-to-date when the inputs have not changed, + // the task must declare an output, even if it's not used. Tasks with no + // output are always run regardless of whether the inputs changed + @get:OutputDirectory + abstract val output: DirectoryProperty + + @get:InputFile + abstract val goldenBadging: RegularFileProperty + + @get:InputFile + abstract val generatedBadging: RegularFileProperty + + override fun getGroup(): String = LifecycleBasePlugin.VERIFICATION_GROUP + + @TaskAction + fun taskAction() { + if ( + Files.mismatch( + goldenBadging.get().asFile.toPath(), + generatedBadging.get().asFile.toPath() + ) != -1L + ) { + throw GradleException( + "Generated badging is different from golden badging! " + + "If this change is intended, run ./gradlew updateBadging" + ) + } + } +} + +fun Project.configureBadgingTasks( + baseExtension: BaseExtension, + componentsExtension: ApplicationAndroidComponentsExtension +) { + // Registers a callback to be called, when a new variant is configured + componentsExtension.onVariants { variant -> + // Registers a new task to verify the app bundle. + val generateBadging = tasks.register("generate${variant.name}Badging") { + apk.set( + variant.artifacts.get(SingleArtifact.APK_FROM_BUNDLE) + ) + aapt2Executable.set( + File( + baseExtension.sdkDirectory, + "build-tools/${baseExtension.buildToolsVersion}/aapt2" + ) + ) + + badging.set( + project.layout.buildDirectory.file( + "outputs/apk_from_bundle/${variant.name}/${variant.name}-badging.txt" + ) + ) + } + + tasks.register("update${variant.name}Badging") { + from(generateBadging.get().badging) + into(project.layout.projectDirectory) + } + + val checkBadgingTaskName = "check${variant.name}Badging" + tasks.register(checkBadgingTaskName) { + goldenBadging.set( + project.layout.projectDirectory.file("${variant.name}-badging.txt")) + generatedBadging.set( + generateBadging.get().badging + ) + + output.set( + project.layout.buildDirectory.dir("intermediates/$checkBadgingTaskName") + ) + } + } +}