/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.utils

import com.facebook.react.tests.createProject
import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class BackwardCompatUtilsTest {

  @get:Rule val tempFolder = TemporaryFolder()

  @Test
  fun configureBackwardCompatibilityReactMap_addsEmptyReactMap() {
    val project = createProject()

    configureBackwardCompatibilityReactMap(project)

    assertTrue(project.extensions.extraProperties.has("react"))
    @Suppress("UNCHECKED_CAST")
    assertTrue((project.extensions.extraProperties.get("react") as Map<String, Any?>).isEmpty())
  }

  @Test
  fun configureBackwardCompatibilityReactMap_withExistingMapSetByUser_wipesTheMap() {
    val project = createProject()
    project.extensions.extraProperties.set("react", mapOf("enableHermes" to true))

    configureBackwardCompatibilityReactMap(project)

    assertTrue(project.extensions.extraProperties.has("react"))
    @Suppress("UNCHECKED_CAST")
    assertTrue((project.extensions.extraProperties.get("react") as Map<String, Any?>).isEmpty())
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.utils

import com.facebook.react.tests.createProject
import com.facebook.react.utils.DependencyUtils.configureDependencies
import com.facebook.react.utils.DependencyUtils.configureRepositories
import com.facebook.react.utils.DependencyUtils.getDependencySubstitutions
import com.facebook.react.utils.DependencyUtils.mavenRepoFromURI
import com.facebook.react.utils.DependencyUtils.mavenRepoFromUrl
import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
import java.net.URI
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
import org.gradle.testfixtures.ProjectBuilder
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class DependencyUtilsTest {

  @get:Rule val tempFolder = TemporaryFolder()

  @Test
  fun configureRepositories_withProjectPropertySet_configuresMavenLocalCorrectly() {
    val localMaven = tempFolder.newFolder("m2")
    val localMavenURI = localMaven.toURI()
    val project = createProject()
    project.extensions.extraProperties.set("REACT_NATIVE_MAVEN_LOCAL_REPO", localMaven.absolutePath)

    configureRepositories(project, tempFolder.root)

    assertNotNull(
        project.repositories.firstOrNull {
          it is MavenArtifactRepository && it.url == localMavenURI
        })
  }

  @Test
  fun configureRepositories_containsSnapshotRepo() {
    val repositoryURI = URI.create("https://oss.sonatype.org/content/repositories/snapshots/")
    val project = createProject()

    configureRepositories(project, tempFolder.root)

    assertNotNull(
        project.repositories.firstOrNull {
          it is MavenArtifactRepository && it.url == repositoryURI
        })
  }

  @Test
  fun configureRepositories_containsJscLocalMavenRepo() {
    val projectFolder = tempFolder.newFolder()
    val reactNativeDir = tempFolder.newFolder("react-native")
    val jscAndroidDir = tempFolder.newFolder("jsc-android")
    val repositoryURI = URI.create("file://${jscAndroidDir}/dist")
    val project = createProject(projectFolder)

    configureRepositories(project, reactNativeDir)

    assertNotNull(
        project.repositories.firstOrNull {
          it is MavenArtifactRepository && it.url == repositoryURI
        })
  }

  @Test
  fun configureRepositories_containsMavenCentral() {
    val repositoryURI = URI.create("https://repo.maven.apache.org/maven2/")
    val project = createProject()

    configureRepositories(project, tempFolder.root)

    assertNotNull(
        project.repositories.firstOrNull {
          it is MavenArtifactRepository && it.url == repositoryURI
        })
  }

  @Test
  fun configureRepositories_containsGoogleRepo() {
    val repositoryURI = URI.create("https://dl.google.com/dl/android/maven2/")
    val project = createProject()

    configureRepositories(project, tempFolder.root)

    assertNotNull(
        project.repositories.firstOrNull {
          it is MavenArtifactRepository && it.url == repositoryURI
        })
  }

  @Test
  fun configureRepositories_containsJitPack() {
    val repositoryURI = URI.create("https://www.jitpack.io")
    val project = createProject()

    configureRepositories(project, tempFolder.root)

    assertNotNull(
        project.repositories.firstOrNull {
          it is MavenArtifactRepository && it.url == repositoryURI
        })
  }

  @Test
  fun configureRepositories_withProjectPropertySet_hasHigherPriorityThanMavenCentral() {
    val localMaven = tempFolder.newFolder("m2")
    val localMavenURI = localMaven.toURI()
    val mavenCentralURI = URI.create("https://repo.maven.apache.org/maven2/")
    val project = createProject()
    project.extensions.extraProperties.set("REACT_NATIVE_MAVEN_LOCAL_REPO", localMaven.absolutePath)

    configureRepositories(project, tempFolder.root)

    val indexOfLocalRepo =
        project.repositories.indexOfFirst {
          it is MavenArtifactRepository && it.url == localMavenURI
        }
    val indexOfMavenCentral =
        project.repositories.indexOfFirst {
          it is MavenArtifactRepository && it.url == mavenCentralURI
        }
    assertTrue(indexOfLocalRepo < indexOfMavenCentral)
  }

  @Test
  fun configureRepositories_snapshotRepoHasHigherPriorityThanMavenCentral() {
    val repositoryURI = URI.create("https://oss.sonatype.org/content/repositories/snapshots/")
    val mavenCentralURI = URI.create("https://repo.maven.apache.org/maven2/")
    val project = createProject()

    configureRepositories(project, tempFolder.root)

    val indexOfSnapshotRepo =
        project.repositories.indexOfFirst {
          it is MavenArtifactRepository && it.url == repositoryURI
        }
    val indexOfMavenCentral =
        project.repositories.indexOfFirst {
          it is MavenArtifactRepository && it.url == mavenCentralURI
        }
    assertTrue(indexOfSnapshotRepo < indexOfMavenCentral)
  }

  @Test
  fun configureRepositories_appliesToAllProjects() {
    val repositoryURI = URI.create("https://repo.maven.apache.org/maven2/")
    val rootProject = ProjectBuilder.builder().build()
    val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
    val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()

    configureRepositories(appProject, tempFolder.root)

    assertNotNull(
        appProject.repositories.firstOrNull {
          it is MavenArtifactRepository && it.url == repositoryURI
        })
    assertNotNull(
        libProject.repositories.firstOrNull {
          it is MavenArtifactRepository && it.url == repositoryURI
        })
  }

  @Test
  fun configureRepositories_withPreviousExclusionRulesOnMavenCentral_appliesCorrectly() {
    val repositoryURI = URI.create("https://repo.maven.apache.org/maven2/")
    val rootProject = ProjectBuilder.builder().build()
    val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
    val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()

    // Let's emulate a library which set an `excludeGroup` on `com.facebook.react` for Central.
    libProject.repositories.mavenCentral { repo ->
      repo.content { content -> content.excludeGroup("com.facebook.react") }
    }

    configureRepositories(appProject, tempFolder.root)

    // We need to make sure we have Maven Central defined twice, one by the library,
    // and another is the override by RNGP.
    assertEquals(
        2,
        libProject.repositories.count { it is MavenArtifactRepository && it.url == repositoryURI })
  }

  @Test
  fun configureDependencies_withEmptyVersion_doesNothing() {
    val project = createProject()

    configureDependencies(project, "")

    assertTrue(project.configurations.first().resolutionStrategy.forcedModules.isEmpty())
  }

  @Test
  fun configureDependencies_withVersionString_appliesResolutionStrategy() {
    val project = createProject()

    configureDependencies(project, "1.2.3")

    val forcedModules = project.configurations.first().resolutionStrategy.forcedModules
    assertTrue(forcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
    assertTrue(forcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
  }

  @Test
  fun configureDependencies_withVersionString_appliesOnAllProjects() {
    val rootProject = ProjectBuilder.builder().build()
    val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
    val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
    appProject.plugins.apply("com.android.application")
    libProject.plugins.apply("com.android.library")

    configureDependencies(appProject, "1.2.3")

    val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
    val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
    assertTrue(appForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
    assertTrue(appForcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
    assertTrue(libForcedModules.any { it.toString() == "com.facebook.react:react-android:1.2.3" })
    assertTrue(libForcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
  }

  @Test
  fun configureDependencies_withVersionStringAndGroupString_appliesOnAllProjects() {
    val rootProject = ProjectBuilder.builder().build()
    val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
    val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
    appProject.plugins.apply("com.android.application")
    libProject.plugins.apply("com.android.library")

    configureDependencies(appProject, "1.2.3", "io.github.test")

    val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
    val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
    assertTrue(appForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
    assertTrue(appForcedModules.any { it.toString() == "io.github.test:hermes-android:1.2.3" })
    assertTrue(libForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
    assertTrue(libForcedModules.any { it.toString() == "io.github.test:hermes-android:1.2.3" })
  }

  @Test
  fun getDependencySubstitutions_withDefaultGroup_substitutesCorrectly() {
    val dependencySubstitutions = getDependencySubstitutions("0.42.0")

    assertEquals(dependencySubstitutions[0].first, "com.facebook.react:react-native")
    assertEquals(dependencySubstitutions[0].second, "com.facebook.react:react-android:0.42.0")
    assertEquals(
        dependencySubstitutions[0].third,
        "The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210.")
    assertEquals(dependencySubstitutions[1].first, "com.facebook.react:hermes-engine")
    assertEquals(dependencySubstitutions[1].second, "com.facebook.react:hermes-android:0.42.0")
    assertEquals(
        dependencySubstitutions[1].third,
        "The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210.")
  }

  @Test
  fun getDependencySubstitutions_withCustomGroup_substitutesCorrectly() {
    val dependencySubstitutions = getDependencySubstitutions("0.42.0", "io.github.test")

    assertEquals(dependencySubstitutions[0].first, "com.facebook.react:react-native")
    assertEquals(dependencySubstitutions[0].second, "io.github.test:react-android:0.42.0")
    assertEquals(
        dependencySubstitutions[0].third,
        "The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210.")
    assertEquals(dependencySubstitutions[1].first, "com.facebook.react:hermes-engine")
    assertEquals(dependencySubstitutions[1].second, "io.github.test:hermes-android:0.42.0")
    assertEquals(
        dependencySubstitutions[1].third,
        "The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210.")
    assertEquals(dependencySubstitutions[2].first, "com.facebook.react:react-android")
    assertEquals(dependencySubstitutions[2].second, "io.github.test:react-android:0.42.0")
    assertEquals(
        dependencySubstitutions[2].third,
        "The react-android dependency was modified to use the correct Maven group.")
    assertEquals(dependencySubstitutions[3].first, "com.facebook.react:hermes-android")
    assertEquals(dependencySubstitutions[3].second, "io.github.test:hermes-android:0.42.0")
    assertEquals(
        dependencySubstitutions[3].third,
        "The hermes-android dependency was modified to use the correct Maven group.")
  }

  @Test
  fun readVersionString_withCorrectVersionString_returnsIt() {
    val propertiesFile =
        tempFolder.newFile("gradle.properties").apply {
          writeText(
              """
        VERSION_NAME=1000.0.0
        ANOTHER_PROPERTY=true
      """
                  .trimIndent())
        }

    val versionString = readVersionAndGroupStrings(propertiesFile).first

    assertEquals("1000.0.0", versionString)
  }

  @Test
  fun readVersionString_withNightlyVersionString_returnsSnapshotVersion() {
    val propertiesFile =
        tempFolder.newFile("gradle.properties").apply {
          writeText(
              """
        VERSION_NAME=0.0.0-20221101-2019-cfe811ab1
        ANOTHER_PROPERTY=true
      """
                  .trimIndent())
        }

    val versionString = readVersionAndGroupStrings(propertiesFile).first

    assertEquals("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT", versionString)
  }

  @Test
  fun readVersionString_withMissingVersionString_returnsEmpty() {
    val propertiesFile =
        tempFolder.newFile("gradle.properties").apply {
          writeText(
              """
        ANOTHER_PROPERTY=true
      """
                  .trimIndent())
        }

    val versionString = readVersionAndGroupStrings(propertiesFile).first
    assertEquals("", versionString)
  }

  @Test
  fun readVersionString_withEmptyVersionString_returnsEmpty() {
    val propertiesFile =
        tempFolder.newFile("gradle.properties").apply {
          writeText(
              """
        VERSION_NAME=
        ANOTHER_PROPERTY=true
      """
                  .trimIndent())
        }

    val versionString = readVersionAndGroupStrings(propertiesFile).first
    assertEquals("", versionString)
  }

  @Test
  fun readGroupString_withCorrectGroupString_returnsIt() {
    val propertiesFile =
        tempFolder.newFile("gradle.properties").apply {
          writeText(
              """
        GROUP=io.github.test
        ANOTHER_PROPERTY=true
      """
                  .trimIndent())
        }

    val groupString = readVersionAndGroupStrings(propertiesFile).second

    assertEquals("io.github.test", groupString)
  }

  @Test
  fun readGroupString_withEmptyGroupString_returnsDefault() {
    val propertiesFile =
        tempFolder.newFile("gradle.properties").apply {
          writeText(
              """
        ANOTHER_PROPERTY=true
      """
                  .trimIndent())
        }

    val groupString = readVersionAndGroupStrings(propertiesFile).second

    assertEquals("com.facebook.react", groupString)
  }

  @Test
  fun mavenRepoFromUrl_worksCorrectly() {
    val process = createProject()
    val mavenRepo = process.mavenRepoFromUrl("https://hello.world")

    assertEquals(URI.create("https://hello.world"), mavenRepo.url)
  }

  @Test
  fun mavenRepoFromURI_worksCorrectly() {
    val process = createProject()
    val repoFolder = tempFolder.newFolder("maven-repo")
    val mavenRepo = process.mavenRepoFromURI(repoFolder.toURI())

    assertEquals(repoFolder.toURI(), mavenRepo.url)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.utils

import java.io.File
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class FileUtilsTest {

  @get:Rule val tempFolder = TemporaryFolder()

  @Test
  fun moveTo_movesCorrectly() {
    val fileToMove = tempFolder.newFile().apply { writeText("42") }
    val destFolder = tempFolder.newFolder("destFolder")
    val destFile = File(destFolder, "destFile")

    fileToMove.moveTo(destFile)

    assertEquals("42", destFile.readText())
    assertFalse(fileToMove.exists())
  }

  @Test
  fun recreateDir_worksCorrectly() {
    val subFolder = tempFolder.newFolder()
    File(subFolder, "1").apply { writeText("1") }
    File(subFolder, "2").apply { writeText("2") }
    File(subFolder, "subDir").apply { mkdirs() }
    File(subFolder, "subDir/3").apply { writeText("3") }

    subFolder.recreateDir()

    assertTrue(subFolder.exists())
    assertEquals(0, subFolder.listFiles()?.size)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.utils

import org.intellij.lang.annotations.Language
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class JsonUtilsTest {

  @get:Rule val tempFolder = TemporaryFolder()

  @Test
  fun withInvalidJson_returnsNull() {
    val invalidJson = createJsonFile("""Â¯\_(ãƒ„)_/Â¯""")

    assertNull(JsonUtils.fromCodegenJson(invalidJson))
  }

  @Test
  fun withEmptyJson_returnsEmptyObject() {
    val invalidJson = createJsonFile("""{}""")

    val parsed = JsonUtils.fromCodegenJson(invalidJson)

    assertNotNull(parsed)
    assertNull(parsed?.codegenConfig)
  }

  @Test
  fun withOldJsonConfig_returnsAnEmptyLibrary() {
    val oldJsonConfig =
        createJsonFile(
            """
      {
        "name": "yet another npm package",
        "codegenConfig": {
          "libraries": [
            {
              "name": "an awesome library",
              "jsSrcsDir": "../js/",
              "android": {}
            }
          ]
        }
      }
      """
                .trimIndent())

    val parsed = JsonUtils.fromCodegenJson(oldJsonConfig)!!

    assertNull(parsed.codegenConfig?.name)
    assertNull(parsed.codegenConfig?.jsSrcsDir)
    assertNull(parsed.codegenConfig?.android)
  }

  @Test
  fun withValidJson_parsesCorrectly() {
    val validJson =
        createJsonFile(
            """
      {
        "name": "yet another npm package",
        "codegenConfig": {
          "name": "an awesome library",
          "jsSrcsDir": "../js/",
          "android": {
            "javaPackageName": "com.awesome.library"
          },
          "ios": {
            "other ios only keys": "which are ignored during parsing"
          }
        }
      }
      """
                .trimIndent())

    val parsed = JsonUtils.fromCodegenJson(validJson)!!

    assertEquals("an awesome library", parsed.codegenConfig!!.name)
    assertEquals("../js/", parsed.codegenConfig!!.jsSrcsDir)
    assertEquals("com.awesome.library", parsed.codegenConfig!!.android!!.javaPackageName)
  }

  private fun createJsonFile(@Language("JSON") input: String) =
      tempFolder.newFile().apply { writeText(input) }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.utils

import com.facebook.react.utils.KotlinStdlibCompatUtils.capitalizeCompat
import com.facebook.react.utils.KotlinStdlibCompatUtils.lowercaseCompat
import com.facebook.react.utils.KotlinStdlibCompatUtils.toBooleanStrictOrNullCompat
import org.junit.Assert.assertEquals
import org.junit.Test

class KotlinStdlibCompatUtilsTest {

  @Test
  fun lowercaseCompat_withEmptyString() {
    assertEquals("", "".lowercaseCompat())
  }

  @Test
  fun lowercaseCompat_withLowercaseString() {
    assertEquals("frodo", "frodo".lowercaseCompat())
  }

  @Test
  fun lowercaseCompat_withTitlecaseString() {
    assertEquals("frodo", "Frodo".lowercaseCompat())
  }

  @Test
  fun lowercaseCompat_withUppercaseString() {
    assertEquals("frodo", "FRODO".lowercaseCompat())
  }

  @Test
  fun capitalizeCompat_withEmptyString() {
    assertEquals("", "".capitalizeCompat())
  }

  @Test
  fun capitalizeCompat_withLowercaseString() {
    assertEquals("Bilbo", "bilbo".capitalizeCompat())
  }

  @Test
  fun capitalizeCompat_withTitlecaseString() {
    assertEquals("Bilbo", "Bilbo".capitalizeCompat())
  }

  @Test
  fun capitalizeCompat_withUppercaseString() {
    assertEquals("BILBO", "BILBO".capitalizeCompat())
  }

  @Test
  fun toBooleanStrictOrNullCompat_withEmptyString() {
    assertEquals(null, "".toBooleanStrictOrNullCompat())
  }

  @Test
  fun toBooleanStrictOrNullCompat_withfalse() {
    assertEquals(false, "false".toBooleanStrictOrNullCompat())
  }

  @Test
  fun toBooleanStrictOrNullCompat_withCapitalTrue_returnsNull() {
    assertEquals(null, "True".toBooleanStrictOrNullCompat())
  }

  @Test
  fun toBooleanStrictOrNullCompat_withCapitalFalse_returnsNull() {
    assertEquals(null, "False".toBooleanStrictOrNullCompat())
  }

  @Test
  fun toBooleanStrictOrNullCompat_withRandomInput_returnsNull() {
    assertEquals(null, "maybe".toBooleanStrictOrNullCompat())
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.utils

import com.facebook.react.utils.NdkConfiguratorUtils.getPackagingOptionsForVariant
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class NdkConfiguratorUtilsTest {

  @Test
  fun getPackagingOptionsForVariant_withHermesEnabled() {
    val (excludes, includes) = getPackagingOptionsForVariant(hermesEnabled = true)

    assertTrue("**/libjsc.so" in excludes)
    assertTrue("**/libjscexecutor.so" in excludes)
    assertFalse("**/libjsc.so" in includes)
    assertFalse("**/libjscexecutor.so" in includes)

    assertTrue("**/libhermes.so" in includes)
    assertTrue("**/libhermes_executor.so" in includes)
    assertFalse("**/libhermes.so" in excludes)
    assertFalse("**/libhermes_executor.so" in excludes)
  }

  @Test
  fun getPackagingOptionsForVariant_withHermesDisabled() {
    val (excludes, includes) = getPackagingOptionsForVariant(hermesEnabled = false)

    assertTrue("**/libhermes.so" in excludes)
    assertTrue("**/libhermes_executor.so" in excludes)
    assertFalse("**/libhermes.so" in includes)
    assertFalse("**/libhermes_executor.so" in includes)

    assertTrue("**/libjsc.so" in includes)
    assertTrue("**/libjscexecutor.so" in includes)
    assertFalse("**/libjsc.so" in excludes)
    assertFalse("**/libjscexecutor.so" in excludes)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.utils

import com.facebook.react.tests.OS
import com.facebook.react.tests.OsRule
import com.facebook.react.tests.WithOs
import com.facebook.react.utils.Os.cliPath
import com.facebook.react.utils.Os.unixifyPath
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class OsTest {

  @get:Rule val osRule = OsRule()
  @get:Rule val tempFolder = TemporaryFolder()

  @Test
  @WithOs(OS.LINUX, "amd64")
  fun onLinuxAmd64_checksOsCorrectly() {
    assertFalse(Os.isWindows())
    assertFalse(Os.isMac())
    assertTrue(Os.isLinuxAmd64())
  }

  @Test
  @WithOs(OS.MAC)
  fun onMac_checksOsCorrectly() {
    assertFalse(Os.isWindows())
    assertTrue(Os.isMac())
    assertFalse(Os.isLinuxAmd64())
  }

  @Test
  @WithOs(OS.WIN)
  fun isWindows_onWindows_returnsTrue() {
    assertTrue(Os.isWindows())
    assertFalse(Os.isMac())
    assertFalse(Os.isLinuxAmd64())
  }

  @Test
  fun unixifyPath_withAUnixPath_doesNothing() {
    val aUnixPath = "/just/a/unix/path.sh"

    assertEquals(aUnixPath, aUnixPath.unixifyPath())
  }

  @Test
  fun unixifyPath_withAWindowsPath_convertsItCorrectly() {
    val aWindowsPath = "D:\\just\\a\\windows\\path\\"

    assertEquals("/D/just/a/windows/path/", aWindowsPath.unixifyPath())
  }

  @Test
  @WithOs(OS.WIN)
  fun cliPath_onWindows_returnsRelativePath() {
    val tempFile = tempFolder.newFile("test.txt").apply { createNewFile() }

    assertEquals(tempFile.relativeTo(tempFolder.root).path, tempFile.cliPath(tempFolder.root))
  }

  @Test
  @WithOs(OS.LINUX)
  fun cliPath_onLinux_returnsAbsolutePath() {
    val tempFile = tempFolder.newFile("test.txt").apply { createNewFile() }

    assertEquals(tempFile.absolutePath, tempFile.cliPath(tempFolder.root))
  }

  @Test
  @WithOs(OS.MAC)
  fun cliPath_onMac_returnsAbsolutePath() {
    val tempFile = tempFolder.newFile("test.txt").apply { createNewFile() }

    assertEquals(tempFile.absolutePath, tempFile.cliPath(tempFolder.root))
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.utils

import com.facebook.react.ReactExtension
import com.facebook.react.TestReactExtension
import com.facebook.react.tests.OS
import com.facebook.react.tests.OsRule
import com.facebook.react.tests.WithOs
import java.io.File
import org.gradle.testfixtures.ProjectBuilder
import org.junit.Assert.*
import org.junit.Assume.assumeTrue
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class PathUtilsTest {

  @get:Rule val tempFolder = TemporaryFolder()
  @get:Rule val osRule = OsRule()

  @Test
  fun detectedEntryFile_withProvidedVariable() {
    val extension = TestReactExtension(ProjectBuilder.builder().build())
    val expected = tempFolder.newFile("fake.index.js")
    extension.entryFile.set(expected)

    val actual = detectedEntryFile(extension)

    assertEquals(expected, actual)
  }

  @Test
  fun detectedEntryFile_withAndroidEntryPoint() {
    val extension = TestReactExtension(ProjectBuilder.builder().build())
    extension.root.set(tempFolder.root)
    tempFolder.newFile("index.android.js")

    val actual = detectedEntryFile(extension)

    assertEquals(File(tempFolder.root, "index.android.js"), actual)
  }

  @Test
  fun detectedEntryFile_withDefaultEntryPoint() {
    val extension = TestReactExtension(ProjectBuilder.builder().build())
    extension.root.set(tempFolder.root)

    val actual = detectedEntryFile(extension)

    assertEquals(File(tempFolder.root, "index.js"), actual)
  }

  @Test
  fun detectedEntryFile_withEnvironmentVariable() {
    val extension = TestReactExtension(ProjectBuilder.builder().build())
    val expected = tempFolder.newFile("./fromenv.index.js")
    // As we can't override env variable for tests, we're going to emulate them here.
    val envVariable = "./fromenv.index.js"

    extension.root.set(tempFolder.root)

    val actual = detectedEntryFile(extension, envVariable)

    assertEquals(expected, actual)
  }

  @Test
  fun detectedCliPath_withCliPathFromExtensionAndFileExists_returnsIt() {
    val project = ProjectBuilder.builder().build()
    val cliFile = tempFolder.newFile("cli.js").apply { createNewFile() }
    val extension = TestReactExtension(project)
    extension.cliFile.set(cliFile)

    val actual = detectedCliFile(extension)

    assertEquals(cliFile, actual)
  }

  @Test
  fun detectedCliPath_withCliFromNodeModules() {
    val project = ProjectBuilder.builder().build()
    val extension = TestReactExtension(project)
    File(tempFolder.root, "node_modules/react-native/cli.js").apply {
      parentFile.mkdirs()
      writeText("<!-- nothing to see here -->")
    }
    val locationToResolveFrom = File(tempFolder.root, "a-subdirectory").apply { mkdirs() }
    extension.root.set(locationToResolveFrom)

    val actual = detectedCliFile(extension)

    assertEquals("<!-- nothing to see here -->", actual.readText())
  }

  @Test(expected = IllegalStateException::class)
  fun detectedCliPath_failsIfNotFound() {
    val project = ProjectBuilder.builder().build()
    val extension = TestReactExtension(project)

    // Because react-native is now a package, it is always
    // accessible from <root>/node_modules/react-native
    // We need to provide location where cli.js file won't be resolved
    extension.root.set(tempFolder.root)

    detectedCliFile(extension)
  }

  @Test
  fun projectPathToLibraryName_withSimplePath() {
    assertEquals("SampleSpec", projectPathToLibraryName(":sample"))
  }

  @Test
  fun projectPathToLibraryName_withComplexPath() {
    assertEquals("SampleAndroidAppSpec", projectPathToLibraryName(":sample:android:app"))
  }

  @Test
  fun projectPathToLibraryName_withKebabCase() {
    assertEquals("SampleAndroidAppSpec", projectPathToLibraryName("sample-android-app"))
  }

  @Test
  fun projectPathToLibraryName_withDotsAndUnderscores() {
    assertEquals("SampleAndroidAppSpec", projectPathToLibraryName("sample_android.app"))
  }

  @Test
  fun detectOSAwareHermesCommand_withProvidedCommand() {
    assertEquals(
        "./my-home/hermes", detectOSAwareHermesCommand(tempFolder.root, "./my-home/hermes"))
  }

  @Test
  fun detectOSAwareHermesCommand_withHermescBuiltLocally() {
    // As we can't mock env variables, we skip this test if an override of the Hermes
    // path has been provided.
    assumeTrue(System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR") == null)

    tempFolder.newFolder("node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/")
    val expected =
        tempFolder.newFile(
            "node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/hermesc")

    assertEquals(expected.toString(), detectOSAwareHermesCommand(tempFolder.root, ""))
  }

  @Test
  @WithOs(OS.MAC)
  fun detectOSAwareHermesCommand_withBundledHermescInsideRN() {
    tempFolder.newFolder("node_modules/react-native/sdks/hermesc/osx-bin/")
    val expected = tempFolder.newFile("node_modules/react-native/sdks/hermesc/osx-bin/hermesc")

    assertEquals(expected.toString(), detectOSAwareHermesCommand(tempFolder.root, ""))
  }

  @Test(expected = IllegalStateException::class)
  @WithOs(OS.MAC)
  fun detectOSAwareHermesCommand_failsIfNotFound() {
    detectOSAwareHermesCommand(tempFolder.root, "")
  }

  @Test
  @WithOs(OS.MAC)
  fun detectOSAwareHermesCommand_withProvidedCommand_takesPrecedence() {
    tempFolder.newFolder("node_modules/react-native/sdks/hermes/build/bin/")
    tempFolder.newFile("node_modules/react-native/sdks/hermes/build/bin/hermesc")
    tempFolder.newFolder("node_modules/react-native/sdks/hermesc/osx-bin/")
    tempFolder.newFile("node_modules/react-native/sdks/hermesc/osx-bin/hermesc")

    assertEquals(
        "./my-home/hermes", detectOSAwareHermesCommand(tempFolder.root, "./my-home/hermes"))
  }

  @Test
  @WithOs(OS.MAC)
  fun detectOSAwareHermesCommand_withoutProvidedCommand_builtHermescTakesPrecedence() {
    // As we can't mock env variables, we skip this test if an override of the Hermes
    // path has been provided.
    assumeTrue(System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR") == null)

    tempFolder.newFolder("node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/")
    val expected =
        tempFolder.newFile(
            "node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/hermesc")
    tempFolder.newFolder("node_modules/react-native/sdks/hermesc/osx-bin/")
    tempFolder.newFile("node_modules/react-native/sdks/hermesc/osx-bin/hermesc")

    assertEquals(expected.toString(), detectOSAwareHermesCommand(tempFolder.root, ""))
  }

  @Test
  fun getBuiltHermescFile_withoutOverride() {
    assertEquals(
        File(
            tempFolder.root,
            "node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/hermesc"),
        getBuiltHermescFile(tempFolder.root, ""))
  }

  @Test
  @WithOs(OS.WIN)
  fun getBuiltHermescFile_onWindows_withoutOverride() {
    assertEquals(
        File(
            tempFolder.root,
            "node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/hermesc.exe"),
        getBuiltHermescFile(tempFolder.root, ""))
  }

  @Test
  fun getBuiltHermescFile_withOverride() {
    assertEquals(
        File("/home/circleci/hermes/build/bin/hermesc"),
        getBuiltHermescFile(tempFolder.root, "/home/circleci/hermes"))
  }

  @Test
  @WithOs(OS.WIN)
  fun getHermesCBin_onWindows_returnsHermescExe() {
    assertEquals("hermesc.exe", getHermesCBin())
  }

  @Test
  @WithOs(OS.LINUX)
  fun getHermesCBin_onLinux_returnsHermesc() {
    assertEquals("hermesc", getHermesCBin())
  }

  @Test
  @WithOs(OS.MAC)
  fun getHermesCBin_onMac_returnsHermesc() {
    assertEquals("hermesc", getHermesCBin())
  }

  @Test
  fun findPackageJsonFile_withFileInParentFolder_picksItUp() {
    tempFolder.newFile("package.json")
    val moduleFolder = tempFolder.newFolder("awesome-module")

    val project = ProjectBuilder.builder().withProjectDir(moduleFolder).build()
    project.plugins.apply("com.android.library")
    project.plugins.apply("com.facebook.react")
    val extension = project.extensions.getByType(ReactExtension::class.java)

    assertEquals(project.file("../package.json"), findPackageJsonFile(project, extension.root))
  }

  @Test
  fun findPackageJsonFile_withFileConfiguredInExtension_picksItUp() {
    val moduleFolder = tempFolder.newFolder("awesome-module")
    val localFile = File(moduleFolder, "package.json").apply { writeText("{}") }

    val project = ProjectBuilder.builder().withProjectDir(moduleFolder).build()
    project.plugins.apply("com.android.library")
    project.plugins.apply("com.facebook.react")
    val extension =
        project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) }

    assertEquals(localFile, findPackageJsonFile(project, extension.root))
  }

  @Test
  fun readPackageJsonFile_withMissingFile_returnsNull() {
    val moduleFolder = tempFolder.newFolder("awesome-module")
    val project = ProjectBuilder.builder().withProjectDir(moduleFolder).build()
    project.plugins.apply("com.android.library")
    project.plugins.apply("com.facebook.react")
    val extension =
        project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) }

    val actual = readPackageJsonFile(project, extension.root)

    assertNull(actual)
  }

  @Test
  fun readPackageJsonFile_withFileConfiguredInExtension_andMissingCodegenConfig_returnsNullCodegenConfig() {
    val moduleFolder = tempFolder.newFolder("awesome-module")
    File(moduleFolder, "package.json").apply { writeText("{}") }
    val project = ProjectBuilder.builder().withProjectDir(moduleFolder).build()
    project.plugins.apply("com.android.library")
    project.plugins.apply("com.facebook.react")
    val extension =
        project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) }

    val actual = readPackageJsonFile(project, extension.root)

    assertNotNull(actual)
    assertNull(actual!!.codegenConfig)
  }

  @Test
  fun readPackageJsonFile_withFileConfiguredInExtension_andHavingCodegenConfig_returnsValidCodegenConfig() {
    val moduleFolder = tempFolder.newFolder("awesome-module")
    File(moduleFolder, "package.json").apply {
      writeText(
          // language=json
          """
      {
        "name": "a-library",
        "codegenConfig": {}
      }
      """
              .trimIndent())
    }
    val project = ProjectBuilder.builder().withProjectDir(moduleFolder).build()
    project.plugins.apply("com.android.library")
    project.plugins.apply("com.facebook.react")
    val extension =
        project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) }

    val actual = readPackageJsonFile(project, extension.root)

    assertNotNull(actual)
    assertNotNull(actual!!.codegenConfig)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.utils

import com.facebook.react.TestReactExtension
import com.facebook.react.model.ModelCodegenConfig
import com.facebook.react.model.ModelPackageJson
import com.facebook.react.tests.createProject
import com.facebook.react.utils.ProjectUtils.getReactNativeArchitectures
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
import java.io.File
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class ProjectUtilsTest {

  @get:Rule val tempFolder = TemporaryFolder()

  @Test
  fun isNewArchEnabled_returnsFalseByDefault() {
    assertFalse(createProject().isNewArchEnabled)
  }

  @Test
  fun isNewArchEnabled_withDisabled_returnsFalse() {
    val project = createProject()
    project.extensions.extraProperties.set("newArchEnabled", "false")
    assertFalse(project.isNewArchEnabled)
  }

  @Test
  fun isNewArchEnabled_withEnabled_returnsTrue() {
    val project = createProject()
    project.extensions.extraProperties.set("newArchEnabled", "true")
    assertTrue(project.isNewArchEnabled)
  }

  @Test
  fun isNewArchEnabled_withInvalid_returnsFalse() {
    val project = createProject()
    project.extensions.extraProperties.set("newArchEnabled", "Â¯\\_(ãƒ„)_/Â¯")
    assertFalse(project.isNewArchEnabled)
  }

  @Test
  fun isHermesEnabled_returnsTrueByDefault() {
    assertTrue(createProject().isHermesEnabled)
  }

  @Test
  fun isNewArchEnabled_withDisabledViaProperty_returnsFalse() {
    val project = createProject()
    project.extensions.extraProperties.set("hermesEnabled", "false")
    assertFalse(project.isHermesEnabled)
  }

  @Test
  fun isHermesEnabled_withEnabledViaProperty_returnsTrue() {
    val project = createProject()
    project.extensions.extraProperties.set("hermesEnabled", "true")
    assertTrue(project.isHermesEnabled)
  }

  @Test
  fun isHermesEnabled_withInvalidViaProperty_returnsTrue() {
    val project = createProject()
    project.extensions.extraProperties.set("hermesEnabled", "Â¯\\_(ãƒ„)_/Â¯")
    assertTrue(project.isHermesEnabled)
  }

  @Test
  fun isHermesEnabled_withDisabledViaExt_returnsFalse() {
    val project = createProject()
    val extMap = mapOf("enableHermes" to false)
    project.extensions.extraProperties.set("react", extMap)
    assertFalse(project.isHermesEnabled)
  }

  @Test
  fun isHermesEnabled_withEnabledViaExt_returnsTrue() {
    val project = createProject()
    val extMap = mapOf("enableHermes" to true)
    project.extensions.extraProperties.set("react", extMap)
    assertTrue(project.isHermesEnabled)
  }

  @Test
  fun isHermesEnabled_withDisabledViaExtAsString_returnsFalse() {
    val project = createProject()
    val extMap = mapOf("enableHermes" to "false")
    project.extensions.extraProperties.set("react", extMap)
    assertFalse(project.isHermesEnabled)
  }

  @Test
  fun isHermesEnabled_withInvalidViaExt_returnsTrue() {
    val project = createProject()
    val extMap = mapOf("enableHermes" to "Â¯\\_(ãƒ„)_/Â¯")
    project.extensions.extraProperties.set("react", extMap)
    assertTrue(project.isHermesEnabled)
  }

  @Test
  fun needsCodegenFromPackageJson_withCodegenConfigInPackageJson_returnsTrue() {
    val project = createProject()
    val extension = TestReactExtension(project)
    File(tempFolder.root, "package.json").apply {
      writeText(
          // language=json
          """
      {
        "name": "a-library",
        "codegenConfig": {}
      }
      """
              .trimIndent())
    }
    extension.root.set(tempFolder.root)
    assertTrue(project.needsCodegenFromPackageJson(extension.root))
  }

  @Test
  fun needsCodegenFromPackageJson_withMissingCodegenConfigInPackageJson_returnsFalse() {
    val project = createProject()
    val extension = TestReactExtension(project)
    File(tempFolder.root, "package.json").apply {
      writeText(
          // language=json
          """
      {
        "name": "a-library"
      }
      """
              .trimIndent())
    }
    extension.root.set(tempFolder.root)
    assertFalse(project.needsCodegenFromPackageJson(extension.root))
  }

  @Test
  fun needsCodegenFromPackageJson_withCodegenConfigInModel_returnsTrue() {
    val project = createProject()
    val model = ModelPackageJson(ModelCodegenConfig(null, null, null, null))

    assertTrue(project.needsCodegenFromPackageJson(model))
  }

  @Test
  fun needsCodegenFromPackageJson_withMissingCodegenConfigInModel_returnsFalse() {
    val project = createProject()
    val model = ModelPackageJson(null)

    assertFalse(project.needsCodegenFromPackageJson(model))
  }

  @Test
  fun needsCodegenFromPackageJson_withMissingPackageJson_returnsFalse() {
    val project = createProject()
    val extension = TestReactExtension(project)

    assertFalse(project.needsCodegenFromPackageJson(extension.root))
  }

  @Test
  fun getReactNativeArchitectures_withMissingProperty_returnsEmptyList() {
    val project = createProject()
    assertTrue(project.getReactNativeArchitectures().isEmpty())
  }

  @Test
  fun getReactNativeArchitectures_withEmptyProperty_returnsEmptyList() {
    val project = createProject()
    project.extensions.extraProperties.set("reactNativeArchitectures", "")
    assertTrue(project.getReactNativeArchitectures().isEmpty())
  }

  @Test
  fun getReactNativeArchitectures_withSingleArch_returnsSingleton() {
    val project = createProject()
    project.extensions.extraProperties.set("reactNativeArchitectures", "x86")

    val archs = project.getReactNativeArchitectures()
    assertEquals(1, archs.size)
    assertEquals("x86", archs[0])
  }

  @Test
  fun getReactNativeArchitectures_withMultipleArch_returnsList() {
    val project = createProject()
    project.extensions.extraProperties.set(
        "reactNativeArchitectures", "armeabi-v7a,arm64-v8a,x86,x86_64")

    val archs = project.getReactNativeArchitectures()
    assertEquals(4, archs.size)
    assertEquals("armeabi-v7a", archs[0])
    assertEquals("arm64-v8a", archs[1])
    assertEquals("x86", archs[2])
    assertEquals("x86_64", archs[3])
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.utils

import com.facebook.react.tests.OS
import com.facebook.react.tests.OsRule
import com.facebook.react.tests.WithOs
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test

class TaskUtilsTest {

  @get:Rule val osRule = OsRule()

  @Test
  fun windowsAwareCommandLine_withEmptyInput_isEmpty() {
    assertTrue(windowsAwareCommandLine().isEmpty())
  }

  @Test
  fun windowsAwareCommandLine_withList_isEqualAsVararg() {
    assertEquals(
        windowsAwareCommandLine(listOf("a", "b", "c")), windowsAwareCommandLine("a", "b", "c"))
  }

  @Test
  @WithOs(OS.MAC)
  fun windowsAwareCommandLine_onMac_returnsTheList() {
    assertEquals(listOf("a", "b", "c"), windowsAwareCommandLine("a", "b", "c"))
  }

  @Test
  @WithOs(OS.LINUX)
  fun windowsAwareCommandLine_onLinux_returnsTheList() {
    assertEquals(listOf("a", "b", "c"), windowsAwareCommandLine("a", "b", "c"))
  }

  @Test
  @WithOs(OS.WIN)
  fun windowsAwareCommandLine_onWindows_prependsCmd() {
    assertEquals(listOf("cmd", "/c", "a", "b", "c"), windowsAwareCommandLine("a", "b", "c"))
  }

  @Test
  @WithOs(OS.MAC)
  fun windowsAwareBashCommandLine_onMac_returnsTheList() {
    assertEquals(
        listOf("a", "b", "c"), windowsAwareBashCommandLine("a", "b", "c", bashWindowsHome = "abc"))
  }

  @Test
  @WithOs(OS.LINUX)
  fun windowsAwareBashCommandLine_onLinux_returnsTheList() {
    assertEquals(listOf("a", "b", "c"), windowsAwareBashCommandLine("a", "b", "c"))
  }

  @Test
  @WithOs(OS.WIN)
  fun windowsAwareBashCommandLine_onWindows_prependsBash() {
    assertEquals(listOf("bash", "-c", "a", "b", "c"), windowsAwareBashCommandLine("a", "b", "c"))
  }

  @Test
  @WithOs(OS.WIN)
  fun windowsAwareBashCommandLine_onWindows_prependsCustomBashPath() {
    assertEquals(
        listOf("/custom/bash", "-c", "a", "b", "c"),
        windowsAwareBashCommandLine("a", "b", "c", bashWindowsHome = "/custom/bash"))
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.redextest

import kotlin.sequences.sequence
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope

internal abstract class Base() {
  open abstract fun fibonacciSeq(x: Int): Sequence<Int>
}

internal class A : Base() {
  override fun fibonacciSeq(x: Int): Sequence<Int> = sequence {
    var b = 1
    var a = x

    yield(5)

    while (true) {
      yield(a + b)

      val tmp = a + b
      a = b
      b = tmp
    }
  }
}

class ClassMergingKotlinCoroutinesTest {
  fun main(args: Array<String>) {
    val a = A()

    a.fibonacciSeq(0).take(5).toList()

    CoroutineScope(CoroutineName("Parent"))
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package redex

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

// The following rules are about the helper class synthesized by OptimizeEnumsPass.
// POSTCHECK-LABEL: class: redex.$EnumUtils
// POSTCHECK-NEXT: Access flags: (PUBLIC, FINAL)
// POSTCHECK-NEXT: Superclass: java.lang.Object
// POSTCHECK: (PRIVATE, STATIC, FINAL) $VALUES:java.lang.Integer[]
// POSTCHECK: (PUBLIC, STATIC, FINAL) f0:java.lang.Integer
// POSTCHECK: (PUBLIC, STATIC, FINAL) f1:java.lang.Integer
// POSTCHECK: (PUBLIC, STATIC, FINAL) f2:java.lang.Integer
// POSTCHECK: (PUBLIC, STATIC, FINAL) f3:java.lang.Integer
// POSTCHECK-NOT: (PUBLIC, STATIC, FINAL) f4:java.lang.Integer

// Simple case is optimized.
// CHECK-LABEL: class: redex.A
// CHECK-NEXT: Access flags:
// PRECHECK-NEXT: Superclass: java.lang.Enum
// POSTCHECK-NEXT: Superclass: java.lang.Object
enum class A {
  A0,
  A1,
  A2
}

interface I {
  fun getVal(): Int
}

// Not optimized if implements an interface.
// CHECK-LABEL: class: redex.B
// CHECK-NEXT: Access flags:
// PRECHECK-NEXT: Superclass: java.lang.Enum
// POSTCHECK-NEXT: Superclass: java.lang.Enum
enum class B : I {
  B0,
  B1,
  B2;

  override fun getVal(): Int {
    return 42
  }
}

// Enum w/ Int field is optimized.
// CHECK-LABEL: class: redex.C
// CHECK-NEXT: Access flags:
// PRECHECK-NEXT: Superclass: java.lang.Enum
// POSTCHECK-NEXT: Superclass: java.lang.Object
enum class C(val idx: Int) {
  C0(1),
  C1(2),
  C2(3)
}

// Enum w/ String field is optimized.
// CHECK-LABEL: class: redex.D
// CHECK-NEXT: Access flags:
// PRECHECK-NEXT: Superclass: java.lang.Enum
// POSTCHECK-NEXT: Superclass: java.lang.Object
enum class D(val bname: String) {
  D0("Tool"),
  D1("APC"),
  D2("Puscifer")
}

data class User(val name: String)

// Enum w/ field of custom type is not optimized.
// CHECK-LABEL: class: redex.E
// CHECK-NEXT: Access flags:
// PRECHECK-NEXT: Superclass: java.lang.Enum
// POSTCHECK-NEXT: Superclass: java.lang.Enum
enum class E(val u: User) {
  E0(User("Hi")),
  E1(User("Yo")),
  E2(User("There"))
}

// Not optimized if enum constant is declared with anonymous class w/ overriding method.
// CHECK-LABEL: class: redex.ProtocolState
// CHECK-NEXT: Access flags:
// PRECHECK-NEXT: Superclass: java.lang.Enum
// POSTCHECK-NEXT: Superclass: java.lang.Enum
enum class ProtocolState {
  WAITING {
    override fun signal(): ProtocolState = TALKING
  },
  TALKING {
    override fun signal(): ProtocolState = WAITING
  };

  abstract fun signal(): ProtocolState
}

// Enum w/ companion static method is optimized
// CHECK-LABEL: class: redex.F
// CHECK-NEXT: Access flags:
// PRECHECK-NEXT: Superclass: java.lang.Enum
// POSTCHECK-NEXT: Superclass: java.lang.Object
enum class F {
  F0,
  F1,
  F2;

  companion object {
    fun getFByName(name: String): F = valueOf(name.toUpperCase())
  }
}

// Not optimized due to the upcast to Any (Object in Java).
// Note that the is type check in useEnumH is not what rejects the optimization,
// but the upcast preceding that is the reason.
// CHECK-LABEL: class: redex.H
// CHECK-NEXT: Access flags:
// PRECHECK-NEXT: Superclass: java.lang.Enum
// POSTCHECK-NEXT: Superclass: java.lang.Enum
enum class H {
  H0,
  H1,
  H2
}

// Not optimized due to the upcast to java.io.Serializable.
// Note that on the return value, the as type cast could be implicit.
// CHECK-LABEL: class: redex.J
// CHECK-NEXT: Access flags:
// PRECHECK-NEXT: Superclass: java.lang.Enum
// POSTCHECK-NEXT: Superclass: java.lang.Enum
enum class J {
  J0,
  J1,
  J2
}

// Not optimized if referenced by reflection.
// CHECK-LABEL: class: redex.K
// CHECK-NEXT: Access flags:
// PRECHECK-NEXT: Superclass: java.lang.Enum
// POSTCHECK-NEXT: Superclass: java.lang.Enum
enum class K {
  K0,
  K1,
  K2
}

class Helper {
  companion object {
    // This method cannot be in the Test class to avoid Proguard keep rules.
    fun useEnumA(elem: A): Int =
        when (elem) {
          A.A0 -> 40
          A.A2 -> 42
          else -> 41
        }

    fun useEnumAValueOf(s: String): Int {
      val elem: A = A.valueOf(s)
      return when (elem) {
        A.A0 -> 40
        A.A2 -> 42
        else -> 41
      }
    }

    fun useEnumAValues(): Int {
      val vals = A.values()
      return vals.size
    }

    fun useEnumB(elem: B): Int =
        when (elem) {
          B.B1 -> 41
          B.B2 -> 42
          else -> 40
        }

    fun useEnumC(elem: C): Int =
        when (elem) {
          C.C0 -> 40
          C.C1 -> 41
          else -> 42
        }

    fun useEnumD(elem: D): String =
        when (elem) {
          D.D0 -> "Tool"
          D.D1 -> "APC"
          else -> "Puscifer"
        }

    fun useEnumE(elem: E): String =
        when (elem) {
          E.E0 -> "Hi"
          E.E1 -> "Yo"
          else -> "There"
        }

    fun useEnumProtocolState(elem: ProtocolState): String =
        when (elem.signal()) {
          ProtocolState.TALKING -> "waiting"
          else -> "talking"
        }

    fun useEnumF(name: String): String =
        when (F.Companion.getFByName(name)) {
          F.F0 -> "F0"
          F.F1 -> "F1"
          else -> "F2"
        }

    fun useEnumH(elem: Any): String {
      if (elem is H) {
        return elem.name
      } else {
        return "Not H"
      }
    }

    fun useEnumJ(elem: J): java.io.Serializable {
      return elem as java.io.Serializable
    }

    fun useEnumK(elem: K): String = elem::class.java.simpleName
  }
}

class EnumTransformTest {

  @Test
  fun testA() {
    assertThat(Helper.Companion.useEnumA(A.A0)).isEqualTo(40)
    assertThat(Helper.Companion.useEnumA(A.A1)).isEqualTo(41)
    assertThat(Helper.Companion.useEnumA(A.A2)).isEqualTo(42)

    assertThat(Helper.Companion.useEnumAValueOf("A0")).isEqualTo(40)
    assertThat(Helper.Companion.useEnumAValueOf("A1")).isEqualTo(41)
    assertThat(Helper.Companion.useEnumAValueOf("A2")).isEqualTo(42)

    assertThat(Helper.Companion.useEnumAValues()).isEqualTo(3)
  }

  @Test
  fun testB() {
    assertThat(Helper.Companion.useEnumB(B.B0)).isEqualTo(40)
    assertThat(Helper.Companion.useEnumB(B.B1)).isEqualTo(41)
    assertThat(Helper.Companion.useEnumB(B.B2)).isEqualTo(42)
  }

  @Test
  fun testC() {
    assertThat(Helper.Companion.useEnumC(C.C0)).isEqualTo(40)
    assertThat(Helper.Companion.useEnumC(C.C1)).isEqualTo(41)
    assertThat(Helper.Companion.useEnumC(C.C2)).isEqualTo(42)
  }

  @Test
  fun testD() {
    assertThat(Helper.Companion.useEnumD(D.D0)).isEqualTo("Tool")
    assertThat(Helper.Companion.useEnumD(D.D1)).isEqualTo("APC")
    assertThat(Helper.Companion.useEnumD(D.D2)).isEqualTo("Puscifer")
  }

  @Test
  fun testE() {
    assertThat(Helper.Companion.useEnumE(E.E0)).isEqualTo("Hi")
    assertThat(Helper.Companion.useEnumE(E.E1)).isEqualTo("Yo")
    assertThat(Helper.Companion.useEnumE(E.E2)).isEqualTo("There")
  }

  @Test
  fun testProtocolState() {
    assertThat(Helper.Companion.useEnumProtocolState(ProtocolState.TALKING)).isEqualTo("talking")
  }

  @Test
  fun testF() {
    assertThat(Helper.Companion.useEnumF("f0")).isEqualTo("F0")
    assertThat(Helper.Companion.useEnumF("f1")).isEqualTo("F1")
    assertThat(Helper.Companion.useEnumF("f2")).isEqualTo("F2")
  }

  @Test
  fun testH() {
    assertThat(Helper.Companion.useEnumH(H.H0)).isEqualTo("H0")
    assertThat(Helper.Companion.useEnumH(H.H1)).isEqualTo("H1")
    assertThat(Helper.Companion.useEnumH(H.H2)).isEqualTo("H2")
    assertThat(Helper.Companion.useEnumH(1)).isEqualTo("Not H")
  }

  @Test
  fun testJ() {
    assertThat(Helper.Companion.useEnumJ(J.J0)).isNotNull()
    assertThat(Helper.Companion.useEnumJ(J.J1)).isNotNull()
    assertThat(Helper.Companion.useEnumJ(J.J2)).isNotNull()
  }

  @Test
  fun testK() {
    assertThat(Helper.Companion.useEnumK(K.K0)).isEqualTo("K")
    assertThat(Helper.Companion.useEnumK(K.K1)).isEqualTo("K")
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import androidx.annotation.IntDef

/* Note: companion object in annotation class will be removed by AnnoKill + RMU pass, NOT KotlinObjectInliner pass*/
@IntDef(AnnoClass.START, AnnoClass.END)
annotation class AnnoClass {
  companion object {
    const val START = 0
    const val END = 1
  }
}

/* After KotlinObjectInliner + LocalDce + RMU, class LCompanionClass$Companion; should be removed*/
class CompanionClass(val greeting: String) {
  fun greet(name: String) = "$greeting, $name!"

  companion object {
    val s: String = "Hello"
    var s1 = "Hello2"

    fun hello() = CompanionClass(s)

    fun hello1(): Boolean {
      if (s1 == "Hello2") {
        return true
      } else {
        return false
      }
    }

    fun get(): String {
      return s1
    }
  }

  fun get(): String {
    return s
  }
}

class AnotherCompanionClass {
  companion object Test {
    @JvmStatic var someOtherStr: String = "Bar"

    @JvmStatic
    fun funX(): String {
      return someOtherStr
    }
  }
}

class ThirdCompanionClass {
  companion object Test {
    const val thirdStr: String = "Bar"

    private fun funY(): String {
      return thirdStr
    }
  }

  fun get(): String {
    return funY()
  }
}

class Foo {
  fun main() {

    println(CompanionClass.hello().greet("Olive"))
    println(CompanionClass.s)
    println(CompanionClass.s1)
    println(CompanionClass.hello1())
    println(CompanionClass.get())
    println(AnotherCompanionClass.funX())
    println(AnnoClass.START)

    val obj = ThirdCompanionClass()
    println(obj.get())
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

// Use this example to test kotlin non-capturing lambda inling.
class KtNonCapturingLambda {
  var sink: Long = 0
  var ret: Boolean = false

  fun doCalc(addfn: (a: Long, b: Long) -> Long): Long {
    return addfn(123L, 456L)
  }

  fun doCalc1(orfn: (a: Boolean, b: Boolean) -> Boolean): Boolean {
    return orfn(true, false)
  }

  fun foo() {
    sink = doCalc { a: Long, b: Long -> a + b }
  }

  fun foo1() {
    ret = doCalc1 { a: Boolean, b: Boolean -> a && b }
  }

  fun main() {
    foo()
    foo1()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.redextest.kt

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test

enum class A {
  A0,
  A1,
  A2;

  companion object {
    val MY_FAVORITE: A = A1
  }
}

enum class B {
  B0,
  B1,
  B2
}

enum class Big {
  BIG01,
  BIG02,
  BIG03,
  BIG04,
  BIG05,
  BIG06,
  BIG07,
  BIG08,
  BIG09,
  BIG10,
  BIG11,
  BIG12,
  BIG13,
  BIG14,
  BIG15,
  BIG16,
  BIG17,
  BIG18,
  BIG19,
  BIG20
}

public fun useA(elem: A): Int =
    when (elem) {
      A.A0 -> 40
      A.A2 -> 42
      else -> 41
    }

public fun useB(elem: B): Int =
    when (elem) {
      B.B0 -> 43
      B.B2 -> 45
      else -> 44
    }

public fun useAAgain(elem: A): Int =
    when (elem) {
      A.A0 -> 46
      A.A1 -> 47
      else -> 48
    }

public fun useBAgain(elem: B?): Int =
    when (elem) {
      B.B0 -> 49
      B.B2 -> 51
      null -> 52
      else -> 50
    }

public fun withOtherCode(elem: B, b: Boolean, o: Any?): Int =
    try {
      if (b) {
        when (elem) {
          B.B0 -> 49
          B.B1 -> 50
          B.B2 -> 51
          else -> 500
        }
      } else {
        o!!.hashCode()
      }
    } catch (_: NullPointerException) {
      404
    }

public fun useBig(elem: Big): Int =
    when (elem) {
      Big.BIG01 -> 1
      Big.BIG02 -> 2
      Big.BIG03 -> 3
      Big.BIG04 -> 4
      Big.BIG05 -> 5
      Big.BIG06 -> 6
      Big.BIG07 -> 7
      Big.BIG08 -> 8
      Big.BIG09 -> 9
      Big.BIG10 -> 10
      Big.BIG11 -> 11
      Big.BIG12 -> 12
      Big.BIG13 -> 13
      Big.BIG14 -> 14
      Big.BIG15 -> 15
      Big.BIG16 -> 16
      Big.BIG17 -> 17
      Big.BIG18 -> 18
      Big.BIG19 -> 19
      Big.BIG20 -> 20
      else -> throw IllegalArgumentException()
    }

public fun useBigAgain(elem: Big?): Int =
    when (elem) {
      Big.BIG01 -> 21
      Big.BIG02 -> 22
      Big.BIG03 -> 23
      Big.BIG04 -> 24
      Big.BIG05 -> 25
      Big.BIG06 -> 26
      Big.BIG07 -> 27
      Big.BIG08 -> 28
      Big.BIG09 -> 29
      Big.BIG10 -> 30
      Big.BIG11 -> 31
      Big.BIG12 -> 32
      Big.BIG13 -> 33
      Big.BIG14 -> 34
      Big.BIG15 -> 35
      Big.BIG16 -> 36
      Big.BIG17 -> 37
      Big.BIG18 -> 38
      Big.BIG19 -> 39
      Big.BIG20 -> 40
      null -> 41
      else -> throw IllegalArgumentException()
    }

class OptimizeEnumSwitchMapTest {
  @Test
  fun testA() {
    assertThat(useA(A.A0)).isEqualTo(40)
    assertThat(useA(A.A1)).isEqualTo(41)
    assertThat(useA(A.A2)).isEqualTo(42)

    assertThat(useAAgain(A.A0)).isEqualTo(46)
    assertThat(useAAgain(A.A1)).isEqualTo(47)
    assertThat(useAAgain(A.A2)).isEqualTo(48)
  }

  @Test
  fun testB() {
    assertThat(useB(B.B0)).isEqualTo(43)
    assertThat(useB(B.B1)).isEqualTo(44)
    assertThat(useB(B.B2)).isEqualTo(45)

    assertThat(useBAgain(B.B0)).isEqualTo(49)
    assertThat(useBAgain(B.B1)).isEqualTo(50)
    assertThat(useBAgain(B.B2)).isEqualTo(51)
    assertThat(useBAgain(null)).isEqualTo(52)

    val o = Object()
    assertThat(withOtherCode(B.B0, true, null)).isEqualTo(49)
    assertThat(withOtherCode(B.B1, true, null)).isEqualTo(50)
    assertThat(withOtherCode(B.B2, true, o)).isEqualTo(51)
    assertThat(withOtherCode(B.B0, false, null)).isEqualTo(404)
    assertThat(withOtherCode(B.B1, false, o)).isEqualTo(o.hashCode())
    assertThat(withOtherCode(B.B2, false, null)).isEqualTo(404)
  }

  @Test
  fun testBig() {
    assertThat(useBig(Big.BIG01)).isEqualTo(1)
    assertThat(useBig(Big.BIG02)).isEqualTo(2)
    assertThat(useBig(Big.BIG03)).isEqualTo(3)
    assertThat(useBig(Big.BIG04)).isEqualTo(4)
    assertThat(useBig(Big.BIG05)).isEqualTo(5)
    assertThat(useBig(Big.BIG06)).isEqualTo(6)
    assertThat(useBig(Big.BIG07)).isEqualTo(7)
    assertThat(useBig(Big.BIG08)).isEqualTo(8)
    assertThat(useBig(Big.BIG09)).isEqualTo(9)
    assertThat(useBig(Big.BIG10)).isEqualTo(10)
    assertThat(useBig(Big.BIG11)).isEqualTo(11)
    assertThat(useBig(Big.BIG12)).isEqualTo(12)
    assertThat(useBig(Big.BIG13)).isEqualTo(13)
    assertThat(useBig(Big.BIG14)).isEqualTo(14)
    assertThat(useBig(Big.BIG15)).isEqualTo(15)
    assertThat(useBig(Big.BIG16)).isEqualTo(16)
    assertThat(useBig(Big.BIG17)).isEqualTo(17)
    assertThat(useBig(Big.BIG18)).isEqualTo(18)
    assertThat(useBig(Big.BIG19)).isEqualTo(19)
    assertThat(useBig(Big.BIG20)).isEqualTo(20)

    assertThat(useBigAgain(Big.BIG01)).isEqualTo(21)
    assertThat(useBigAgain(Big.BIG02)).isEqualTo(22)
    assertThat(useBigAgain(Big.BIG03)).isEqualTo(23)
    assertThat(useBigAgain(Big.BIG04)).isEqualTo(24)
    assertThat(useBigAgain(Big.BIG05)).isEqualTo(25)
    assertThat(useBigAgain(Big.BIG06)).isEqualTo(26)
    assertThat(useBigAgain(Big.BIG07)).isEqualTo(27)
    assertThat(useBigAgain(Big.BIG08)).isEqualTo(28)
    assertThat(useBigAgain(Big.BIG09)).isEqualTo(29)
    assertThat(useBigAgain(Big.BIG10)).isEqualTo(30)
    assertThat(useBigAgain(Big.BIG11)).isEqualTo(31)
    assertThat(useBigAgain(Big.BIG12)).isEqualTo(32)
    assertThat(useBigAgain(Big.BIG13)).isEqualTo(33)
    assertThat(useBigAgain(Big.BIG14)).isEqualTo(34)
    assertThat(useBigAgain(Big.BIG15)).isEqualTo(35)
    assertThat(useBigAgain(Big.BIG16)).isEqualTo(36)
    assertThat(useBigAgain(Big.BIG17)).isEqualTo(37)
    assertThat(useBigAgain(Big.BIG18)).isEqualTo(38)
    assertThat(useBigAgain(Big.BIG19)).isEqualTo(39)
    assertThat(useBigAgain(Big.BIG20)).isEqualTo(40)
    assertThat(useBigAgain(null)).isEqualTo(41)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.redextest.objtest
class CompanionClass {
  companion object {
    var someStr: String = "Foo"
  }

  fun get(): String {
    return someStr
  }
}

class AnotherCompanionClass {
  companion object Test {
    @JvmStatic var someOtherStr: String = "Bar"

    @JvmStatic
    fun funX(): String {
      return someOtherStr
    }
  }
}

class Foo {
  fun main() {
    val obj = CompanionClass()
    print(obj.get())
    print(CompanionClass.someStr)
    print(AnotherCompanionClass.someOtherStr)
    print(AnotherCompanionClass.funX())
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import kotlin.reflect.KProperty

class Delegate1 {
  operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
    return "$thisRef, '${property.name}'"
  }

  operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
    println("$value '${property.name}' $thisRef")
  }
}

class Example {
  var p: String by Delegate1()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

class KotlinLambdaInline {
  var sink: Long = 0

  fun doCalc(addfn: (a: Long, b: Long) -> Long): Long {
    return addfn(123L, 456L)
  }

  fun foo() {
    sink = doCalc { a: Long, b: Long -> a + b }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

fun <T> lazyFn(initializer: () -> T): Lazy<T> = Foo(initializer)

internal class Foo<out T>(initializer: () -> T) : Lazy<T> {
  private val unsafeLazy = lazy(LazyThreadSafetyMode.NONE, initializer)
  override val value: T
    get() {
      return unsafeLazy.value
    }

  override fun isInitialized(): Boolean = unsafeLazy.isInitialized()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

class FooDelagates {
  val lazyValue: String by lazy { expensiveFn() }

  fun expensiveFn(): String {
    return "Help"
  }
}

class DelegateTest {
  fun main() {
    val foo = FooDelagates()
    println(foo.lazyValue)
    println(foo.lazyValue)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

class TypeAnalysisRemoveRedundantCmp {
  private val data: Array<Any> = arrayOf(1, "1")
  val x
    get() = data[0] as Int

  val y
    get() = data[1] as String

  private val dataMayBeNull: Array<Any?> = arrayOf(1, null)
  val xx
    get() = dataMayBeNull[0] as Int

  val yy
    get() = dataMayBeNull[1] as String

  fun main() {
    println(TypeAnalysisRemoveRedundantCmp().x)
    println(TypeAnalysisRemoveRedundantCmp().yy)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

class TypeAnalysisRemoveRedundantNullCheck {
  fun foo(str: String) {
    print(str)
  }

  fun main() {
    foo("test")
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.build

import com.android.build.gradle.api.TestVariant
import org.gradle.api.tasks.TaskAction

open class CleanScreenshotsTask : ScreenshotTask() {
  companion object {
    fun taskName(variant: TestVariant) = "clean${variant.name.capitalize()}Screenshots"
  }

  init {
    description = "Clean last generated screenshot report"
    group = ScreenshotsPlugin.GROUP
  }

  @TaskAction
  fun cleanScreenshots() {
    val outputDir = PullScreenshotsTask.getReportDir(project, variant)
    project.delete(outputDir)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.build

import com.android.build.gradle.api.ApkVariantOutput
import com.android.build.gradle.api.TestVariant
import java.io.File
import org.gradle.api.Project
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction

open class PullScreenshotsTask : ScreenshotTask() {
  companion object {
    fun taskName(variant: TestVariant) = "pull${variant.name.capitalize()}Screenshots"

    fun getReportDir(project: Project, variant: TestVariant): File =
        File(project.buildDir, "screenshots" + variant.name.capitalize())
  }

  private lateinit var apkPath: File

  @Input protected var verify = false

  @Input protected var record = false

  @Input protected var bundleResults = false

  @Input protected lateinit var testRunId: String

  init {
    description = "Pull screenshots from your device"
    group = ScreenshotsPlugin.GROUP
  }

  override fun init(variant: TestVariant, extension: ScreenshotsPluginExtension) {
    super.init(variant, extension)
    val output =
        variant.outputs.find { it is ApkVariantOutput } as? ApkVariantOutput
            ?: throw IllegalArgumentException("Can't find APK output")
    val packageTask =
        variant.packageApplicationProvider.orNull
            ?: throw IllegalArgumentException("Can't find package application provider")

    apkPath = File(packageTask.outputDirectory.asFile.get(), output.outputFileName)
    bundleResults = extension.bundleResults
    testRunId = extension.testRunId
  }

  @TaskAction
  fun pullScreenshots() {
    val codeSource = ScreenshotsPlugin::class.java.protectionDomain.codeSource
    val jarFile = File(codeSource.location.toURI().path)
    val isVerifyOnly = verify && extension.referenceDir != null

    val outputDir =
        if (isVerifyOnly) {
          File(extension.referenceDir)
        } else {
          getReportDir(project, variant)
        }

    assert(if (isVerifyOnly) outputDir.exists() else !outputDir.exists())

    project.exec {
      it.executable = extension.pythonExecutable
      it.environment("PYTHONPATH", jarFile)

      it.args =
          mutableListOf(
                  "-m",
                  "android_screenshot_tests.pull_screenshots",
                  "--apk",
                  apkPath.absolutePath,
                  "--test-run-id",
                  testRunId,
                  "--temp-dir",
                  outputDir.absolutePath)
              .apply {
                if (verify) {
                  add("--verify")
                } else if (record) {
                  add("--record")
                }

                if (verify || record) {
                  add(extension.recordDir)
                }

                if (verify && extension.failureDir != null) {
                  add("--failure-dir")
                  add("${extension.failureDir}")
                }

                if (extension.multipleDevices) {
                  add("--multiple-devices")
                  add("${extension.multipleDevices}")
                }

                if (isVerifyOnly) {
                  add("--no-pull")
                }

                if (bundleResults) {
                  add("--bundle-results")
                }
              }

      println(it.args)
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.build

import com.android.build.gradle.api.TestVariant

open class RecordScreenshotTestTask : RunScreenshotTestTask() {
  companion object {
    fun taskName(variant: TestVariant) = "record${variant.name.capitalize()}ScreenshotTest"
  }

  init {
    description =
        "Installs and runs screenshot tests, then records their output for later verification"
    group = ScreenshotsPlugin.GROUP
  }

  override fun init(variant: TestVariant, extension: ScreenshotsPluginExtension) {
    super.init(variant, extension)
    record = true
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.build

import com.android.build.gradle.api.TestVariant

open class RunScreenshotTestTask : PullScreenshotsTask() {
  companion object {
    fun taskName(variant: TestVariant) = "run${variant.name.capitalize()}ScreenshotTest"
  }

  init {
    description = "Installs and runs screenshot tests, then generates a report"
    group = ScreenshotsPlugin.GROUP
  }

  override fun init(variant: TestVariant, extension: ScreenshotsPluginExtension) {
    super.init(variant, extension)

    if (verify && extension.referenceDir != null) {
      return
    }

    dependsOn(variant.connectedInstrumentTestProvider)
    mustRunAfter(variant.connectedInstrumentTestProvider)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.build

import com.android.build.gradle.AppExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.TestedExtension
import com.android.build.gradle.api.ApkVariantOutput
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.internal.tasks.factory.dependsOn
import com.facebook.testing.screenshot.generated.ScreenshotTestBuildConfig
import java.util.UUID
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.TaskProvider

open class ScreenshotsPluginExtension {
  /** The directory to store recorded screenshots in */
  var recordDir = "screenshots"
  /** Whether to have the plugin dependency automatically add the core dependency */
  var addDeps = true
  /** Whether to store screenshots in device specific folders */
  var multipleDevices = false
  /** The python executable to use */
  var pythonExecutable = "python"
  /** The directory to compare screenshots from in verify only mode */
  var referenceDir: String? = null
  /** The directory to save failed screenshots */
  var failureDir: String? = null
  /** Whether to tar the screenshots in an archive file to transfer */
  var bundleResults = false

  var testRunId: String = UUID.randomUUID().toString()
}

class ScreenshotsPlugin : Plugin<Project> {
  companion object {
    const val GROUP = "Screenshot Test"
    const val DEPENDENCY_GROUP = "com.facebook.testing.screenshot"
    const val DEPENDENCY_CORE = "core"
  }

  private lateinit var screenshotExtensions: ScreenshotsPluginExtension

  override fun apply(project: Project) {
    val extensions = project.extensions
    screenshotExtensions = extensions.create("screenshots", ScreenshotsPluginExtension::class.java)

    project.afterEvaluate {
      if (screenshotExtensions.addDeps) {
        it.dependencies.add(
            "androidTestImplementation",
            "$DEPENDENCY_GROUP:$DEPENDENCY_CORE:${ScreenshotTestBuildConfig.VERSION}")
      }
    }
    val androidExtension = getProjectExtension(project)
    androidExtension.testVariants.all { generateTasksFor(project, it) }
    androidExtension.defaultConfig.testInstrumentationRunnerArguments["SCREENSHOT_TESTS_RUN_ID"] =
        screenshotExtensions.testRunId
  }

  private fun getProjectExtension(project: Project): TestedExtension {
    val extensions = project.extensions
    val plugins = project.plugins
    return when {
      plugins.hasPlugin("com.android.application") ->
          extensions.findByType(AppExtension::class.java)!!
      plugins.hasPlugin("com.android.library") ->
          extensions.findByType(LibraryExtension::class.java)!!
      else -> throw IllegalArgumentException("Screenshot Test plugin requires Android's plugin")
    }
  }

  private fun <T : ScreenshotTask> registerTask(
      project: Project,
      name: String,
      variant: TestVariant,
      clazz: Class<T>
  ): TaskProvider<T> {
    return project.tasks.register(name, clazz).apply {
      configure { it.init(variant, screenshotExtensions) }
    }
  }

  private fun generateTasksFor(project: Project, variant: TestVariant) {
    variant.outputs.all {
      if (it is ApkVariantOutput) {
        val cleanScreenshots =
            registerTask(
                project,
                CleanScreenshotsTask.taskName(variant),
                variant,
                CleanScreenshotsTask::class.java)
        registerTask(
                project,
                PullScreenshotsTask.taskName(variant),
                variant,
                PullScreenshotsTask::class.java)
            .dependsOn(cleanScreenshots)

        registerTask(
            project,
            RunScreenshotTestTask.taskName(variant),
            variant,
            RunScreenshotTestTask::class.java)

        registerTask(
            project,
            RecordScreenshotTestTask.taskName(variant),
            variant,
            RecordScreenshotTestTask::class.java)

        registerTask(
            project,
            VerifyScreenshotTestTask.taskName(variant),
            variant,
            VerifyScreenshotTestTask::class.java)
      }
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.build

import com.android.build.gradle.api.TestVariant
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.Input

open class ScreenshotTask : DefaultTask() {
  @Input protected lateinit var extension: ScreenshotsPluginExtension

  @Input protected lateinit var variant: TestVariant

  open fun init(variant: TestVariant, extension: ScreenshotsPluginExtension) {
    this.extension = extension
    this.variant = variant
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.build

import com.android.build.gradle.api.TestVariant

open class VerifyScreenshotTestTask : RunScreenshotTestTask() {
  companion object {
    fun taskName(variant: TestVariant) = "verify${variant.name.capitalize()}ScreenshotTest"
  }

  init {
    description =
        "Installs and runs screenshot tests, then verifies their output against previously recorded screenshots"
    group = ScreenshotsPlugin.GROUP
    verify = true
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.sample

import android.view.View
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import com.facebook.testing.screenshot.Screenshot
import org.hamcrest.core.AllOf.allOf
import org.junit.Before
import org.junit.Rule
import org.junit.Test

class MainActivityTest {
  @get:Rule
  var activityTestRule = ActivityTestRule<MainActivity>(MainActivity::class.java, false, false)

  @Before
  fun before() {
    InstrumentationRegistry.getInstrumentation().getUiAutomation()
  }

  @Test
  fun testScreenshotEntireActivity() {
    val activity = activityTestRule.launchActivity(null)
    Screenshot.snapActivity(activity).record()
  }

  @Test
  fun mainActivityTestSettingsOpen() {
    val activity = activityTestRule.launchActivity(null)
    val floatingActionButton = onView(allOf<View>(withId(R.id.fab), isDisplayed()))
    floatingActionButton.perform(click())

    openActionBarOverflowOrOptionsMenu(activity)
    Screenshot.snapActivity(activity).record()
  }

  @Test
  fun mainActivityTestFabWithEspresso() {
    activityTestRule.launchActivity(null)
    onView(withId(R.id.fab)).perform(screenshot("fab"))
  }

  @Test
  fun errorTextShouldBeRed() {
    val intent = MainActivity.intent(MainActivity.Status.ERROR)
    val activity = activityTestRule.launchActivity(intent)

    Screenshot.snapActivity(activity).record()
  }

  @Test
  fun warningTextShouldBeYellow() {
    val intent = MainActivity.intent(MainActivity.Status.WARNING)
    val activity = activityTestRule.launchActivity(intent)

    Screenshot.snapActivity(activity).record()
  }

  @Test
  fun okTextShouldBeGreen() {
    val intent = MainActivity.intent(MainActivity.Status.OK)
    val activity = activityTestRule.launchActivity(intent)

    Screenshot.snapActivity(activity).record()
  }

  @Test
  fun testScreenshotEntireActivityWithoutAccessibilityMetadata() {
    val activity = activityTestRule.launchActivity(null)
    Screenshot.snapActivity(activity).setIncludeAccessibilityInfo(false).record()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.sample

import android.os.Bundle
import androidx.test.runner.AndroidJUnitRunner
import com.facebook.litho.config.ComponentsConfiguration
import com.facebook.testing.screenshot.ScreenshotRunner
import com.facebook.testing.screenshot.layouthierarchy.LayoutHierarchyDumper
import com.facebook.testing.screenshot.layouthierarchy.litho.LithoAttributePlugin
import com.facebook.testing.screenshot.layouthierarchy.litho.LithoHierarchyPlugin

class ScreenshotTestRunner : AndroidJUnitRunner() {
  companion object {
    init {
      ComponentsConfiguration.isDebugModeEnabled = true
      LayoutHierarchyDumper.addGlobalHierarchyPlugin(LithoHierarchyPlugin.getInstance())
      LayoutHierarchyDumper.addGlobalAttributePlugin(LithoAttributePlugin.getInstance())
    }
  }

  override fun onCreate(arguments: Bundle) {
    ScreenshotRunner.onCreate(this, arguments)
    super.onCreate(arguments)
  }

  override fun finish(resultCode: Int, results: Bundle) {
    ScreenshotRunner.onDestroy()
    super.finish(resultCode, results)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.sample

import android.view.View
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import com.facebook.testing.screenshot.Screenshot
import org.hamcrest.Matcher

fun screenshot(name: String): ViewAction {
  return ScreenshotViewAction(name)
}

class ScreenshotViewAction internal constructor(private val name: String) : ViewAction {
  override fun getConstraints(): Matcher<View> {
    return isDisplayed()
  }

  override fun getDescription(): String {
    return name
  }

  override fun perform(uiController: UiController, view: View) {
    Screenshot.snap(view).setName(name).record()
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.sample

import android.app.Application
import com.facebook.soloader.SoLoader

class App : Application() {
  override fun onCreate() {
    super.onCreate()
    SoLoader.init(this, false)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.sample

import com.facebook.litho.Border
import com.facebook.litho.Column
import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.widget.Text
import com.facebook.yoga.YogaEdge

@LayoutSpec
object ExampleSpec {
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext): Component =
      Column.create(c)
          .child(ImageRow.create(c))
          .child(Text.create(c).textRes(R.string.large_text))
          .paddingDip(YogaEdge.ALL, 16f)
          .border(
              Border.create(c)
                  .colorRes(YogaEdge.ALL, R.color.colorPrimary)
                  .widthDip(YogaEdge.ALL, 8f)
                  .build())
          .build()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.sample

import com.facebook.litho.Component
import com.facebook.litho.ComponentContext
import com.facebook.litho.Row
import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.widget.Image
import com.facebook.yoga.YogaEdge

@LayoutSpec
object ImageRowSpec {
  @OnCreateLayout
  fun onCreateLayout(c: ComponentContext): Component =
      Row.create(c)
          .child(
              Image.create(c)
                  .drawableRes(R.drawable.ic_launcher_background)
                  .widthDip(64f)
                  .heightDip(64f)
                  .paddingDip(YogaEdge.ALL, 4f))
          .child(
              Image.create(c)
                  .drawableRes(R.drawable.ic_launcher_background)
                  .widthDip(128f)
                  .heightDip(128f)
                  .paddingDip(YogaEdge.ALL, 4f))
          .child(
              Image.create(c)
                  .drawableRes(R.drawable.ic_launcher_background)
                  .widthDip(32f)
                  .heightDip(32f)
                  .paddingDip(YogaEdge.ALL, 4f))
          .build()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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.facebook.testing.screenshot.sample

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.widget.TextView
import androidx.annotation.ColorRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar

class MainActivity : AppCompatActivity() {
  companion object {
    private const val STATUS = "status"

    fun intent(status: Status) = Intent().apply { putExtra(STATUS, status.name) }
  }

  enum class Status {
    OK,
    WARNING,
    ERROR
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(findViewById(R.id.toolbar))

    val textView = findViewById<TextView>(R.id.text_view)
    val status = Status.valueOf(intent.string(STATUS, Status.OK.name))
    when (status) {
      Status.OK ->
          textView.run {
            setTextColor(context.color(R.color.ok))
            text = "Status is OK"
          }
      Status.WARNING ->
          textView.run {
            setTextColor(context.color(R.color.warning))
            text = "Status is WARNING"
          }
      Status.ERROR ->
          textView.run {
            setTextColor(context.color(R.color.error))
            text = "Status is ERROR"
          }
    }

    findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { view ->
      Snackbar.make(view, "This is a snackbar", Snackbar.LENGTH_LONG)
          .setAction("Action", null)
          .show()
    }
  }

  override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.menu_main, menu)
    return true
  }

  private fun Intent.string(name: String, defValue: String): String {
    if (hasExtra(name)) {
      return getStringExtra(name)
    }
    return defValue
  }

  private fun Context.color(@ColorRes color: Int): Int {
    return ContextCompat.getColor(this, color)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.shimmer.sample

import android.animation.ValueAnimator
import android.app.Activity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.Toast
import com.facebook.shimmer.Shimmer
import com.facebook.shimmer.ShimmerFrameLayout
import kotlinx.android.synthetic.main.main.*

class MainActivity : Activity(), View.OnClickListener {
  private lateinit var shimmerViewContainer: ShimmerFrameLayout
  private lateinit var presetButtons: Array<Button>
  private var currentPreset = -1
  private var toast: Toast? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main)
    shimmerViewContainer = shimmer_view_container
    presetButtons =
        arrayOf(
            preset_button0,
            preset_button1,
            preset_button2,
            preset_button3,
            preset_button4,
            preset_button5,
            preset_button6)
    presetButtons.forEach { it.setOnClickListener(this@MainActivity) }
    selectPreset(0, false)
  }

  override fun onClick(v: View) {
    selectPreset(presetButtons.indexOf(v as Button), true)
  }

  public override fun onResume() {
    super.onResume()
    shimmerViewContainer.startShimmer()
  }

  public override fun onPause() {
    shimmerViewContainer.stopShimmer()
    super.onPause()
  }

  private fun selectPreset(preset: Int, showToast: Boolean) {
    if (currentPreset == preset) {
      return
    }

    if (currentPreset >= 0) {
      presetButtons[currentPreset].setBackgroundResource(R.color.preset_button_background)
    }
    currentPreset = preset
    presetButtons[currentPreset].setBackgroundResource(R.color.preset_button_background_selected)

    // If a toast is already showing, hide it
    toast?.cancel()

    val shimmerBuilder = Shimmer.AlphaHighlightBuilder()
    shimmerViewContainer.setShimmer(
        when (preset) {
          1 -> {
            // Slow and reverse
            toast = Toast.makeText(this, "Slow and reverse", Toast.LENGTH_SHORT)
            shimmerBuilder.setDuration(5000L).setRepeatMode(ValueAnimator.REVERSE)
          }
          2 -> {
            // Thin, straight and transparent
            toast = Toast.makeText(this, "Thin, straight and transparent", Toast.LENGTH_SHORT)
            shimmerBuilder.setBaseAlpha(0.1f).setDropoff(0.1f).setTilt(0f)
          }
          3 -> {
            // Sweep angle 90
            toast = Toast.makeText(this, "Sweep angle 90", Toast.LENGTH_SHORT)
            shimmerBuilder.setDirection(Shimmer.Direction.TOP_TO_BOTTOM).setTilt(0f)
          }
          4 -> {
            // Spotlight
            toast = Toast.makeText(this, "Spotlight", Toast.LENGTH_SHORT)
            shimmerBuilder
                .setBaseAlpha(0f)
                .setDuration(2000L)
                .setDropoff(0.1f)
                .setIntensity(0.35f)
                .setShape(Shimmer.Shape.RADIAL)
          }
          5 -> {
            // Spotlight angle 45
            toast = Toast.makeText(this, "Spotlight angle 45", Toast.LENGTH_SHORT)
            shimmerBuilder
                .setBaseAlpha(0f)
                .setDuration(2000L)
                .setDropoff(0.1f)
                .setIntensity(0.35f)
                .setTilt(45f)
                .setShape(Shimmer.Shape.RADIAL)
          }
          6 -> {
            // Off
            toast = Toast.makeText(this, "Off", Toast.LENGTH_SHORT)
            null
          }
          else -> {
            toast = Toast.makeText(this, "Default", Toast.LENGTH_SHORT)
            shimmerBuilder
          }
        }?.build())

    // Show toast describing the chosen preset, if necessary
    if (showToast) {
      toast?.show()
    }
  }
}