Merge "Start using GoldenImagePathManager Test: ScreenshotTestRuleTest Bug: 232141755 Merged-In: I8680217cc62bd198e610e1c5e39b7835a48392f3 Change-Id: I2e8c13d12c975c920ca32d17b58aa46c8b1e1d74"
diff --git a/libraries/screenshot/src/androidTest/java/platform/test/screenshot/ScreenshotTestRuleTest.kt b/libraries/screenshot/src/androidTest/java/platform/test/screenshot/ScreenshotTestRuleTest.kt
index 1581048..6e2ef63 100644
--- a/libraries/screenshot/src/androidTest/java/platform/test/screenshot/ScreenshotTestRuleTest.kt
+++ b/libraries/screenshot/src/androidTest/java/platform/test/screenshot/ScreenshotTestRuleTest.kt
@@ -16,12 +16,14 @@
 
 package platform.test.screenshot
 
+import android.content.Context
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import androidx.test.platform.app.InstrumentationRegistry
 import com.google.common.truth.Truth.assertThat
+import java.io.File
 import java.lang.AssertionError
 import org.junit.After
-import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -34,19 +36,21 @@
 import platform.test.screenshot.proto.ScreenshotResultProto
 import platform.test.screenshot.utils.loadBitmap
 
+class CustomGoldenImagePathManager(appcontext: Context) : GoldenImagePathManager(appcontext) {
+    public override fun goldenIdentifierResolver(
+        testName: String,
+        relativePathOnly: Boolean,
+        localPath: Boolean
+    ): String = "$testName.png"
+}
+
 @RunWith(AndroidJUnit4::class)
 @MediumTest
 class ScreenshotTestRuleTest {
 
     @get:Rule
-    val rule = ScreenshotTestRule()
-
-    @Before
-    fun setup() {
-        rule.setCustomGoldenIdResolver { goldenId ->
-            "$goldenId.png"
-        }
-    }
+    val rule = ScreenshotTestRule(
+        CustomGoldenImagePathManager(InstrumentationRegistry.getInstrumentation().getContext()))
 
     @Test
     fun performDiff_sameBitmaps() {
@@ -160,9 +164,8 @@
 
     @After
     fun after() {
-        rule.clearCustomGoldenIdResolver()
         // Clear all files we generated so we don't have dependencies between tests
-        rule.deviceOutputDirectory.deleteRecursively()
+        File(rule.goldenImagePathManager.locationConfig.deviceLocalPath).deleteRecursively()
     }
 
     private fun expectErrorMessage(expectedErrorMessage: String, block: () -> Unit) {
diff --git a/libraries/screenshot/src/main/java/platform/test/screenshot/GoldenImagePathManager.kt b/libraries/screenshot/src/main/java/platform/test/screenshot/GoldenImagePathManager.kt
index 1f4e775..0d16d5c 100644
--- a/libraries/screenshot/src/main/java/platform/test/screenshot/GoldenImagePathManager.kt
+++ b/libraries/screenshot/src/main/java/platform/test/screenshot/GoldenImagePathManager.kt
@@ -63,14 +63,14 @@
     val pathConfig: PathConfig = getDefaultPathConfig()
 ) {
 
-    private val deviceLocalPath = locationConfig.deviceLocalPath
-    private val repoRemotePath = locationConfig.repoRemotePath
-    private val imageExtension = "png"
+    public val deviceLocalPath = locationConfig.deviceLocalPath
+    public val repoRemotePath = locationConfig.repoRemotePath
+    public val imageExtension = "png"
 
     /*
      * Uses [pathConfig] and [testName] to construct the full path to the golden image.
      */
-    public fun goldenIdentifierResolver(
+    public open fun goldenIdentifierResolver(
         testName: String,
         relativePathOnly: Boolean = true,
         localPath: Boolean = true
diff --git a/libraries/screenshot/src/main/java/platform/test/screenshot/PlatformScreenshotTestRule.kt b/libraries/screenshot/src/main/java/platform/test/screenshot/PlatformScreenshotTestRule.kt
index 891b98d..bb66c52 100644
--- a/libraries/screenshot/src/main/java/platform/test/screenshot/PlatformScreenshotTestRule.kt
+++ b/libraries/screenshot/src/main/java/platform/test/screenshot/PlatformScreenshotTestRule.kt
@@ -16,6 +16,8 @@
 
 package platform.test.screenshot
 
+import android.content.Context
+
 /**
  * Rule to be used in platform project tests. Set's up the proper repository name and golden
  * directory.
@@ -28,12 +30,9 @@
  * @hide
  */
 class PlatformScreenshotTestRule(
+    context: Context,
     moduleDirectory: String,
     outputRootDir: String? = null
 ) : ScreenshotTestRule(
-    ScreenshotTestRuleConfig(
-        "platform/frameworks/support-golden",
-        moduleDirectory.trim('/')
-    ),
-    outputRootDir
+        GoldenImagePathManager(context)
 )
diff --git a/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt b/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt
index 1dba78c..02571f8 100644
--- a/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt
+++ b/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt
@@ -19,7 +19,6 @@
 import android.annotation.SuppressLint
 import android.graphics.Bitmap
 import android.graphics.BitmapFactory
-import android.os.Build
 import android.os.Bundle
 import androidx.test.platform.app.InstrumentationRegistry
 import org.junit.rules.TestRule
@@ -35,36 +34,6 @@
 import java.io.FileOutputStream
 import java.io.IOException
 
-// TODO(b/223901506): Replace this with the more advanced config class after the CL ag/17587688
-// is submitted.
-/**
- * Config for [ScreenshotTestRule].
- *
- * To be used to set up paths to golden images. These paths are not used to retrieve the goldens
- * during the test. They are just directly stored into the result proto file. The proto file can
- * then be used by CI to determined where to put the new approved goldens. Your tests assets
- * directory should be pointing to exactly the same path.
- *
- * @param repoRootPathForGoldens Path to the repo's root that contains the goldens. To be used by
- * CI.
- * @param pathToGoldensInRepo Relative path to goldens inside your [repoRootPathForGoldens].
- */
-class ScreenshotTestRuleConfig(
-    val repoRootPathForGoldens: String = "",
-    val pathToGoldensInRepo: String = ""
-)
-
-/**
- * Type of file that can be produced by the [ScreenshotTestRule].
- */
-internal enum class OutputFileType {
-    IMAGE_ACTUAL,
-    IMAGE_EXPECTED,
-    IMAGE_DIFF,
-    RESULT_PROTO,
-    RESULT_BIN_PROTO
-}
-
 /**
  * Rule to be added to a test to facilitate screenshot testing.
  *
@@ -79,29 +48,9 @@
  */
 @SuppressLint("SyntheticAccessor")
 open class ScreenshotTestRule(
-    val config: ScreenshotTestRuleConfig = ScreenshotTestRuleConfig(),
-    val outputRootDir: String? = null
+    val goldenImagePathManager: GoldenImagePathManager
 ) : TestRule {
 
-    val deviceOutputRootDirectory: File? =
-        if (outputRootDir != null) {
-            File(outputRootDir)
-        } else {
-            InstrumentationRegistry.getInstrumentation().getContext().externalCacheDir
-        }
-
-    /**
-     * Directory on the device that is used to store the output files.
-     */
-    val deviceOutputDirectory
-        get() = File(
-            deviceOutputRootDirectory,
-            "platform_screenshots"
-        )
-
-    private val repoRootPathForGoldens = config.repoRootPathForGoldens.trim('/')
-    private val pathToGoldensInRepo = config.pathToGoldensInRepo.trim('/')
-    private val imageExtension = ".png"
     private val resultBinaryProtoFileSuffix = ".pb"
     // This is used in CI to identify the files.
     private val resultProtoFileSuffix = "goldResult.textproto"
@@ -113,12 +62,9 @@
     private lateinit var testIdentifier: String
     private lateinit var deviceId: String
 
-    private var goldenIdentifierResolver: ((String) -> String) = ::resolveGoldenName
-
     private val testWatcher = object : TestWatcher() {
         override fun starting(description: Description?) {
-            deviceId = getDeviceModel()
-            testIdentifier = "${description!!.className}_${description.methodName}_$deviceId"
+            testIdentifier = "${description!!.className}_${description.methodName}"
         }
     }
 
@@ -129,27 +75,10 @@
 
     class ScreenshotTestStatement(private val base: Statement) : Statement() {
         override fun evaluate() {
-            // NOTE(ihcinihsdk@): My hunch is that we should not add these assumptions at all.
-            // The reason is that this framework should be served for the general platform.
-            // If a test is supposed to run on a specific type of device, it should be the
-            // test authors' duty. What we need to do is to provide guidance on how to add
-            // assumptions on type device and SDK version.
             base.evaluate()
         }
     }
 
-    internal fun setCustomGoldenIdResolver(resolver: ((String) -> String)) {
-        goldenIdentifierResolver = resolver
-    }
-
-    internal fun clearCustomGoldenIdResolver() {
-        goldenIdentifierResolver = ::resolveGoldenName
-    }
-
-    private fun resolveGoldenName(goldenIdentifier: String): String {
-        return "${goldenIdentifier}_$deviceId$imageExtension"
-    }
-
     private fun fetchExpectedImage(goldenIdentifier: String): Bitmap? {
         val instrument = InstrumentationRegistry.getInstrumentation()
         return listOf(
@@ -157,7 +86,9 @@
                 instrument.context
         ).map {
             try {
-                it.assets.open(goldenIdentifierResolver(goldenIdentifier)).use {
+                it.assets.open(
+                    goldenImagePathManager.goldenIdentifierResolver(goldenIdentifier)
+                ).use {
                     return@use BitmapFactory.decodeStream(it)
                 }
             } catch (e: FileNotFoundException) {
@@ -205,7 +136,7 @@
             )
             throw AssertionError(
                 "Missing golden image " +
-                    "'${goldenIdentifierResolver(goldenIdentifier)}'. " +
+                    "'${goldenImagePathManager.goldenIdentifierResolver(goldenIdentifier)}'. " +
                     "Did you mean to check in a new image?"
             )
         }
@@ -267,17 +198,13 @@
             .addMetadata(
                 ScreenshotResultProto.Metadata.newBuilder()
                     .setKey("repoRootPath")
-                    .setValue(repoRootPathForGoldens))
+                    .setValue(goldenImagePathManager.repoRemotePath))
 
         if (comparisonStatistics != null) {
             resultProto.comparisonStatistics = comparisonStatistics
         }
         resultProto.imageLocationGolden =
-            if (pathToGoldensInRepo.isEmpty()) {
-                goldenIdentifierResolver(goldenIdentifier)
-            } else {
-                "$pathToGoldensInRepo/${goldenIdentifierResolver(goldenIdentifier)}"
-            }
+            goldenImagePathManager.goldenIdentifierResolver(goldenIdentifier)
 
         val report = Bundle()
 
@@ -318,13 +245,16 @@
 
     internal fun getPathOnDeviceFor(fileType: OutputFileType): File {
         val fileName = when (fileType) {
-            OutputFileType.IMAGE_ACTUAL -> "${testIdentifier}_actual$imageExtension"
-            OutputFileType.IMAGE_EXPECTED -> "${testIdentifier}_expected$imageExtension"
-            OutputFileType.IMAGE_DIFF -> "${testIdentifier}_diff$imageExtension"
+            OutputFileType.IMAGE_ACTUAL ->
+                "${testIdentifier}_actual$goldenImagePathManager.imageExtension"
+            OutputFileType.IMAGE_EXPECTED ->
+                "${testIdentifier}_expected$goldenImagePathManager.imageExtension"
+            OutputFileType.IMAGE_DIFF ->
+                "${testIdentifier}_diff$goldenImagePathManager.imageExtension"
             OutputFileType.RESULT_PROTO -> "${testIdentifier}_$resultProtoFileSuffix"
             OutputFileType.RESULT_BIN_PROTO -> "${testIdentifier}_$resultBinaryProtoFileSuffix"
         }
-        return File(deviceOutputDirectory, fileName)
+        return File(goldenImagePathManager.locationConfig.deviceLocalPath, fileName)
     }
 
     private fun Bitmap.writeToDevice(fileType: OutputFileType): File {
@@ -337,7 +267,8 @@
         fileType: OutputFileType,
         writeAction: (FileOutputStream) -> Unit
     ): File {
-        if (!deviceOutputDirectory.exists() && !deviceOutputDirectory.mkdir()) {
+        val fileGolden = File(goldenImagePathManager.locationConfig.deviceLocalPath)
+        if (!fileGolden.exists() && !fileGolden.mkdir()) {
             throw IOException("Could not create folder.")
         }
 
@@ -354,14 +285,6 @@
         }
         return file
     }
-
-    private fun getDeviceModel(): String {
-        var model = android.os.Build.MODEL.lowercase()
-        arrayOf("phone", "x86", "x64", "gms").forEach {
-            model = model.replace(it, "")
-        }
-        return model.trim().replace(" ", "_")
-    }
 }
 
 internal fun Bitmap.toIntArray(): IntArray {
@@ -392,3 +315,14 @@
 ) {
     rule.assertBitmapAgainstGolden(this, goldenIdentifier, matcher = matcher)
 }
+
+/**
+ * Type of file that can be produced by the [ScreenshotTestRule].
+ */
+internal enum class OutputFileType {
+    IMAGE_ACTUAL,
+    IMAGE_EXPECTED,
+    IMAGE_DIFF,
+    RESULT_PROTO,
+    RESULT_BIN_PROTO
+}