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

package com.facebook.appevents.iap

import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.os.Bundle
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.internal.FeatureManager
import java.util.concurrent.atomic.AtomicBoolean
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.api.support.membermodification.MemberModifier
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(
    FacebookSdk::class,
    FeatureManager::class,
    InAppPurchaseActivityLifecycleTracker::class,
    InAppPurchaseAutoLogger::class,
    InAppPurchaseManager::class)
class InAppPurchaseManagerTest : FacebookPowerMockTestCase() {
  private lateinit var mockContext: Context
  override fun setup() {
    super.setup()
    mockContext = mock()
    PowerMockito.mockStatic(FeatureManager::class.java)
    PowerMockito.mockStatic(InAppPurchaseActivityLifecycleTracker::class.java)
    PowerMockito.mockStatic(InAppPurchaseAutoLogger::class.java)
    PowerMockito.mockStatic(FeatureManager::class.java)
    PowerMockito.mockStatic(FacebookSdk::class.java)
    Whitebox.setInternalState(InAppPurchaseManager::class.java, "enabled", AtomicBoolean(false))
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockContext)
  }

  @Test
  fun `test start iap logging when billing lib 2+ is not available`() {
    MemberModifier.stub<Boolean>(
            PowerMockito.method(InAppPurchaseManager::class.java, "usingBillingLib2Plus"))
        .toReturn(false)
    var isStartIapLoggingCalled = false
    whenever(InAppPurchaseActivityLifecycleTracker.startIapLogging()).thenAnswer {
      isStartIapLoggingCalled = true
      Unit
    }
    InAppPurchaseManager.enableAutoLogging()
    assertThat(isStartIapLoggingCalled).isTrue
  }

  @Test
  fun `test start iap logging when billing lib 2+ is available but feature is off`() {
    MemberModifier.stub<Boolean>(
            PowerMockito.method(InAppPurchaseManager::class.java, "usingBillingLib2Plus"))
        .toReturn(true)
    whenever(FeatureManager.isEnabled(FeatureManager.Feature.IapLoggingLib2)).thenReturn(false)
    var isStartIapLoggingCalled = false
    whenever(InAppPurchaseActivityLifecycleTracker.startIapLogging()).thenAnswer {
      isStartIapLoggingCalled = true
      Unit
    }
    InAppPurchaseManager.enableAutoLogging()
    assertThat(isStartIapLoggingCalled).isTrue
  }

  @Test
  fun `test start iap logging when billing lib 2+ is available and feature is on`() {
    whenever(FeatureManager.isEnabled(FeatureManager.Feature.IapLoggingLib2)).thenReturn(true)
    val mockPackageManager: PackageManager = mock()
    val mockApplicationInfo = ApplicationInfo()
    val metaData = Bundle()
    metaData.putString("com.google.android.play.billingclient.version", "2.0.3")
    whenever(mockContext.packageManager).thenReturn(mockPackageManager)
    whenever(mockContext.packageName).thenReturn("com.facebook.test")
    whenever(mockPackageManager.getApplicationInfo(any(), any())).thenReturn(mockApplicationInfo)
    mockApplicationInfo.metaData = metaData

    var isStartIapLoggingCalled = false
    whenever(InAppPurchaseAutoLogger.startIapLogging(any())).thenAnswer {
      isStartIapLoggingCalled = true
      Unit
    }
    InAppPurchaseManager.enableAutoLogging()
    assertThat(isStartIapLoggingCalled).isTrue
  }
}


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

package com.facebook.appevents.iap

import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.FacebookSdk.getExecutor
import com.facebook.FacebookSdk.isInitialized
import com.facebook.FacebookTestUtility.assertNotNull
import com.facebook.appevents.iap.InAppPurchaseUtils.getClass
import com.facebook.appevents.iap.InAppPurchaseUtils.getMethod
import java.util.concurrent.Executor
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class)
class InAppPurchaseSkuDetailsWrapperTest : FacebookPowerMockTestCase() {
  companion object {
    private const val CLASSNAME_SKU_DETAILS_PARAMS =
        "com.facebook.appevents.iap.InAppPurchaseSkuDetailsWrapperTest\$FakeSkuDetailsParams"
    private const val CLASSNAME_SKU_DETAILS_PARAMS_BUILDER =
        "com.facebook.appevents.iap.InAppPurchaseSkuDetailsWrapperTest\$FakeSkuDetailsParams\$Builder"
    private const val METHOD_NEW_BUILDER = "newBuilder"
    private const val METHOD_SET_TYPE = "setType"
    private const val METHOD_SET_SKU_LIST = "setSkusList"
    private const val METHOD_BUILD = "build"
  }

  private val mockExecutor: Executor = FacebookSerialExecutor()

  @Before
  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(isInitialized()).thenReturn(true)
    whenever(getExecutor()).thenReturn(mockExecutor)
  }

  @Test
  fun testGetSkuDetailsParams() {
    val skuDetailsParamsClazz = assertNotNull(getClass(CLASSNAME_SKU_DETAILS_PARAMS))
    val builderClazz = assertNotNull(getClass(CLASSNAME_SKU_DETAILS_PARAMS_BUILDER))

    val newBuilderMethod = assertNotNull(getMethod(skuDetailsParamsClazz, METHOD_NEW_BUILDER))
    val setTypeMethod = assertNotNull(getMethod(builderClazz, METHOD_SET_TYPE, String::class.java))
    val setSkusListMethod =
        assertNotNull(getMethod(builderClazz, METHOD_SET_SKU_LIST, MutableList::class.java))
    val buildMethod = assertNotNull(getMethod(builderClazz, METHOD_BUILD))

    val inAppPurchaseSkuDetailsWrapper =
        InAppPurchaseSkuDetailsWrapper(
            skuDetailsParamsClazz,
            builderClazz,
            newBuilderMethod,
            setTypeMethod,
            setSkusListMethod,
            buildMethod)
    val skuType = "inapp"
    val skuIDs: MutableList<String?> = mutableListOf("test sku ID")
    val skuDetailsParams = inAppPurchaseSkuDetailsWrapper.getSkuDetailsParams(skuType, skuIDs)
    assertThat(skuDetailsParams).isNotNull
    assertThat((skuDetailsParams as FakeSkuDetailsParams).skuType).isEqualTo(skuType)
    assertThat(skuDetailsParams.skusList).containsExactlyElementsOf(skuIDs)
  }

  class FakeSkuDetailsParams {
    var skuType: String? = null
      private set
    var skusList: List<String>? = null
      private set

    companion object {
      @JvmStatic
      fun newBuilder(): Builder {
        return Builder()
      }
    }

    class Builder internal constructor() {
      private val params: FakeSkuDetailsParams = FakeSkuDetailsParams()

      fun setSkusList(skusList: List<String>): Builder {
        params.skusList = skusList
        return this
      }

      fun setType(type: String): Builder {
        params.skuType = type
        return this
      }

      fun build(): FakeSkuDetailsParams = params
    }
  }
}


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

package com.facebook.appevents.iap

import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.FacebookSdk.getExecutor
import com.facebook.FacebookSdk.isInitialized
import java.util.concurrent.Executor
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class)
class InAppPurchaseUtilsTest : FacebookPowerMockTestCase() {
  private val mockExecutor: Executor = FacebookSerialExecutor()
  @Before
  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(isInitialized()).thenReturn(true)
    whenever(getExecutor()).thenReturn(mockExecutor)
  }

  @Test
  fun testGetClass() {
    val myTestClass = InAppPurchaseTestClass(Any())
    assertThat(myTestClass).isNotNull
    val clazz =
        InAppPurchaseUtils.getClass(
            "com.facebook.appevents.iap.InAppPurchaseUtilsTest\$InAppPurchaseTestClass")
    assertThat(clazz).isNotNull
  }

  @Test
  fun testGetMethod() {
    val clazz =
        InAppPurchaseUtils.getClass(
            "com.facebook.appevents.iap.InAppPurchaseUtilsTest\$InAppPurchaseTestClass")
    assertThat(clazz).isNotNull
    val testMethod = clazz?.let { InAppPurchaseUtils.getMethod(it, "inAppPurchaseTestMethod") }
    assertThat(testMethod).isNotNull
  }

  @Test
  fun testInvokeMethod() {
    val myTestClass = InAppPurchaseTestClass(Any())
    val clazz =
        InAppPurchaseUtils.getClass(
            "com.facebook.appevents.iap.InAppPurchaseUtilsTest\$InAppPurchaseTestClass")
    assertThat(clazz).isNotNull
    val testMethod = clazz?.let { InAppPurchaseUtils.getMethod(it, "inAppPurchaseTestMethod") }
    assertThat(testMethod).isNotNull
    val result =
        clazz?.let {
          if (testMethod != null) {
            InAppPurchaseUtils.invokeMethod(it, testMethod, myTestClass)
          } else {
            null
          }
        }
    assertThat(result).isNotNull
  }

  class InAppPurchaseTestClass internal constructor(private val obj: Any) {
    fun inAppPurchaseTestMethod(): String {
      return "Test String"
    }
  }
}


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

package com.facebook.appevents.integrity

import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.appevents.ml.ModelManager
import com.facebook.internal.FetchedAppGateKeepersManager
import java.util.concurrent.Executor
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(ModelManager::class, FacebookSdk::class, FetchedAppGateKeepersManager::class)
class IntegrityManagerTest : FacebookPowerMockTestCase() {
  private val mockExecutor: Executor = FacebookSerialExecutor()
  @Before
  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getExecutor()).thenReturn(mockExecutor)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123")

    PowerMockito.mockStatic(FetchedAppGateKeepersManager::class.java)
    whenever(FetchedAppGateKeepersManager.getGateKeeperForKey(any(), any(), any())).thenReturn(true)
    IntegrityManager.enable()

    PowerMockito.mockStatic(ModelManager::class.java)
    whenever(ModelManager.predict(any(), any(), any())).thenAnswer {
      val texts = it.arguments[2] as Array<String>
      val value =
          when (texts[0]) {
            "customer_Address" -> arrayOf(IntegrityManager.INTEGRITY_TYPE_NONE)
            "1 Hacker way" -> arrayOf(IntegrityManager.INTEGRITY_TYPE_ADDRESS)
            "customer_event" -> arrayOf(IntegrityManager.INTEGRITY_TYPE_NONE)
            "event" -> arrayOf(IntegrityManager.INTEGRITY_TYPE_NONE)
            "is_pregnant" -> arrayOf(IntegrityManager.INTEGRITY_TYPE_HEALTH)
            "yes" -> arrayOf(IntegrityManager.INTEGRITY_TYPE_NONE)
            "customer_health" -> arrayOf(IntegrityManager.INTEGRITY_TYPE_NONE)
            "heart attack" -> arrayOf(IntegrityManager.INTEGRITY_TYPE_HEALTH)
            else -> null
          }
      value
    }
  }

  @Test
  fun testAddressDetection() {
    val mockParameters: MutableMap<String, String> =
        hashMapOf("customer_Address" to "1 Hacker way", "customer_event" to "event")

    val jsonObject = JSONObject()
    jsonObject.put("customer_Address", "1 Hacker way")
    val expectedParameters: MutableMap<String, String> =
        hashMapOf("customer_event" to "event", "_onDeviceParams" to jsonObject.toString())

    IntegrityManager.processParameters(mockParameters)

    assertThat(mockParameters.size).isEqualTo(2)
    assertThat(mockParameters).isEqualTo(expectedParameters)
  }

  @Test
  fun testHealthDataFiltering() {
    val mockParameters: MutableMap<String, String> =
        hashMapOf(
            "is_pregnant" to "yes",
            "customer_health" to "heart attack",
            "customer_event" to "event")

    val jsonObject = JSONObject()
    jsonObject.put("is_pregnant", "yes")
    jsonObject.put("customer_health", "heart attack")
    val expectedParameters: MutableMap<String, String> =
        hashMapOf("customer_event" to "event", "_onDeviceParams" to jsonObject.toString())
    IntegrityManager.processParameters(mockParameters)

    assertThat(mockParameters.size).isEqualTo(2)
    assertThat(mockParameters).isEqualTo(expectedParameters)
  }
}


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

package com.facebook.appevents.integrity

import androidx.core.os.bundleOf
import com.facebook.FacebookTestCase
import org.json.JSONObject
import org.junit.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class MACARuleMatchingManagerTest: FacebookTestCase() {

  @Test
  fun `test string comparison with null`() {
    val json = JSONObject()
    json.put("eq", "platinum")
    assertFalse(
      MACARuleMatchingManager.stringComparison(
        variable = "card_type",
        values = json,
        data = null
      )
    )
  }

  @Test
  fun `test string comparison for contains`() {
    val json = JSONObject()
    json.put("contains", "xxxxx")
    assertTrue(
      MACARuleMatchingManager.stringComparison(
        variable = "URL",
        values = json,
        data = bundleOf(
          "event" to "CompleteRegistration",
          "url" to "www.xxxxx.com"
        )
      )
    )
  }

  @Test
  fun `test string comparison for i_contains`() {
    val json = JSONObject()
    json.put("i_contains", "xxxxx")
    assertTrue(
      MACARuleMatchingManager.stringComparison(
        variable = "URL",
        values = json,
        data = bundleOf(
          "event" to "CompleteRegistration",
          "url" to "www.xxXxx.com"
        )
      )
    )
  }

  @Test
  fun `test string comparison for regex_match`() {
    val json = JSONObject()
    json.put("regex_match", "eylea.us/support/?\$|eylea.us/support/?")
    assertTrue(
      MACARuleMatchingManager.stringComparison(
        variable = "URL",
        values = json,
        data = bundleOf(
          "event" to "CompleteRegistration",
          "url" to "eylea.us/support"
        )
      )
    )
  }

  @Test
  fun `test string comparison for eq`() {
    val json = JSONObject()
    json.put("eq", "CompleteRegistration")
    assertTrue(
      MACARuleMatchingManager.stringComparison(
        variable = "event",
        values = json,
        data = bundleOf(
          "event" to "CompleteRegistration",
          "url" to "eylea.us/support"
        )
      )
    )
  }

  @Test
  fun `test string comparison for neq`() {
    val json = JSONObject()
    json.put("neq", "0")
    assertTrue(
      MACARuleMatchingManager.stringComparison(
        variable = "value",
        values = json,
        data = bundleOf(
          "value" to "1"
        )
      )
    )
  }

  @Test
  fun `test string comparison for lt`() {
    val json = JSONObject()
    json.put("lt", "10")
    assertTrue(
      MACARuleMatchingManager.stringComparison(
        variable = "value",
        values = json,
        data = bundleOf(
          "value" to "1"
        )
      )
    )
  }

  @Test
  fun `test string comparison for lte`() {
    val json = JSONObject()
    json.put("lte", "30")
    assertTrue(
      MACARuleMatchingManager.stringComparison(
        variable = "value",
        values = json,
        data = bundleOf(
          "value" to "30"
        )
      )
    )
  }

  @Test
  fun `test string comparison for gt`() {
    val json = JSONObject()
    json.put("gt", "0")
    assertTrue(
      MACARuleMatchingManager.stringComparison(
        variable = "value",
        values = json,
        data = bundleOf(
          "value" to "1"
        )
      )
    )
  }

  @Test
  fun `test string comparison for gte`() {
    val json = JSONObject()
    json.put("gte", "100")
    assertTrue(
      MACARuleMatchingManager.stringComparison(
        variable = "value",
        values = json,
        data = bundleOf(
          "value" to "100"
        )
      )
    )
  }

  @Test
  fun `test string comparison for invalid op`() {
    val json = JSONObject()
    json.put("none", "0")
    assertFalse(
      MACARuleMatchingManager.stringComparison(
        variable = "value",
        values = json,
        data = bundleOf(
          "value" to "1"
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for not existed data value`() {
    assertFalse(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """{"and":[{"event":{"eq":"Lead"}},{"or":[{"URL":{"contains":"xxxxx"}}]}]}""",
        data = null
      )
    )
  }

  @Test
  fun `test isMatchCCRule for contains`() {
    assertTrue(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """{"and":[{"event":{"eq":"Lead"}},{"or":[{"URL":{"contains":"xxxxx"}}]}]}""",
        data = bundleOf(
          "event" to "Lead",
          "url" to "www.xxxxx.com"
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for not contains`() {
    assertFalse(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """{"and":[{"event":{"eq":"Lead"}},{"or":[{"URL":{"contains":"xxxxx"}}]}]}""",
        data = bundleOf(
          "event" to "Lead",
          "url" to "www.xxXxx.com"
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for i_contains match`() {
    assertTrue(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """
          {"and":[{"event":{"eq":"Lead"}},{"or":[{"URL":{"i_contains":"xxxxx"}}]}]}
          """.trimIndent(),
        data = bundleOf(
          "event" to "Lead",
          "url" to "www.xxXxx.com"
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for i_not_contains match`() {
    assertTrue(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """
          {"and":[{"event":{"eq":"Lead"}},{"or":[{"URL":{"i_not_contains":"xxxxx"}}]}]}
        """.trimIndent(),
        data = bundleOf(
          "event" to "Lead",
          "url" to "www.xx.com"
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for i_not_contains not match`() {
    assertFalse(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """
          {"and":[{"event":{"eq":"Lead"}},{"or":[{"URL":{"i_not_contains":"xxxxx"}}]}]}
        """.trimIndent(),
        data = bundleOf(
          "event" to "Lead",
          "url" to "www.xxXxxww.com"
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for regex match`() {
    assertTrue(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """
          {"and":[{"or":[{"URL":{"regex_match":"eylea.us/support/?${'$'}|eylea.us/support/?"}}]}]}
        """.trimIndent(),
        data = bundleOf(
          "event" to "Lead",
          "url" to "eylea.us/support"
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for regex not match`() {
    assertFalse(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """
          {"and":[{"or":[{"URL":{"regex_match":"eylea.us/support/?${'$'}|eylea.us/support/?"}}]}]}
        """.trimIndent(),
        data = bundleOf(
          "event" to "Lead",
          "url" to "eylea.us.support"
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for eq`() {
    assertTrue(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """{"and":[{"event":{"eq":"PageLoad"}}]}""",
        data = bundleOf(
          "event" to "PageLoad"
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for neq`() {
    assertFalse(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """{"and":[{"event":{"neq":"PageLoad"}}]}""",
        data = bundleOf(
          "event" to "PageLoad"
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for lt`() {
    assertTrue(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """{"and":[{"value":{"lt":"30"}}]}""",
        data = bundleOf(
          "value" to 1
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for lte`() {
    assertTrue(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """{"and":[{"value":{"lte":"30"}}]}""",
        data = bundleOf(
          "value" to "30"
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for gt`() {
    assertTrue(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """{"and":[{"value":{"gt":"30"}}]}""",
        data = bundleOf(
          "value" to 31
        )
      )
    )
  }

  @Test
  fun `test isMatchCCRule for gte`() {
    assertTrue(
      MACARuleMatchingManager.isMatchCCRule(
        ruleString = """{"and":[{"value":{"gte":"30"}}]}""",
        data = bundleOf(
          "value" to "30"
        )
      )
    )
  }
}


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

package com.facebook.appevents.integrity

import android.os.Bundle
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.appevents.ml.ModelManager
import com.facebook.internal.FetchedAppGateKeepersManager
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(ModelManager::class, FacebookSdk::class, FetchedAppGateKeepersManager::class)
class ProtectedModeManagerTest : FacebookPowerMockTestCase() {
    @Before
    override fun setup() {
        super.setup()
        PowerMockito.mockStatic(FacebookSdk::class.java)
        whenever(FacebookSdk.getApplicationId()).thenReturn("123")
    }

    @After
    fun tearDown() {
        ProtectedModeManager.disable()
    }

    @Test
    fun `test null as parameters when enable`() {
        ProtectedModeManager.enable()
        val mockParameters = null
        val expectedParameters = null

        ProtectedModeManager.processParametersForProtectedMode(mockParameters)
        assertEqual(mockParameters, expectedParameters)
    }

    @Test
    fun `test empty parameters when enable`() {
        ProtectedModeManager.enable()
        val mockParameters = Bundle()
        val expectedParameters = Bundle()

        ProtectedModeManager.processParametersForProtectedMode(mockParameters)

        assertEqual(mockParameters, expectedParameters)
    }

    @Test
    fun `test all standard parameters when enable`() {
        ProtectedModeManager.enable()
        val mockParameters = Bundle().apply {
            putString("fb_currency", "USD")
            putString("fb_price", "0.99")
        }
        val expectedParameters = Bundle().apply {
            putString("fb_currency", "USD")
            putString("fb_price", "0.99")
        }

        ProtectedModeManager.processParametersForProtectedMode(mockParameters)

        assertEqual(mockParameters, expectedParameters)
    }

    @Test
    fun `test filter out non-standard parameters when enable`() {
        ProtectedModeManager.enable()
        val mockParameters = Bundle().apply {
            putString("fb_currency", "USD")
            putString("fb_price", "0.99")
            putString("fb_product_price_amount", "0.990")
            putString("quantity", "1")
        }
        val expectedParameters = Bundle().apply {
            putString("fb_currency", "USD")
            putString("fb_price", "0.99")
        }

        ProtectedModeManager.processParametersForProtectedMode(mockParameters)

        assertEqual(mockParameters, expectedParameters)
    }

    @Test
    fun `test not filter out non-standard parameters when disable`() {
        val mockParameters = Bundle().apply {
            putString("fb_currency", "USD")
            putString("fb_price", "0.99")
            putString("fb_product_price_amount", "0.990")
        }
        val expectedParameters = Bundle().apply {
            putString("fb_currency", "USD")
            putString("fb_price", "0.99")
            putString("fb_product_price_amount", "0.990")
        }

        ProtectedModeManager.processParametersForProtectedMode(mockParameters)

        assertEqual(mockParameters, expectedParameters)
    }

    private fun assertEqual(mockBundle: Bundle?, expectedBundle: Bundle?): Boolean {
        val s1 = mockBundle?.keySet() ?: return false
        val s2 = expectedBundle?.keySet() ?: return false

        if (!s1.equals(s2)) {
            return false
        }

        for (s in s1) {
            val v1 = mockBundle.get(s) ?: return false
            val v2 = expectedBundle.get(s) ?: return false
            if (v1 != v2) {
                return false
            }
        }
        return true
    }
}


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

package com.facebook.appevents.internal

import android.app.Activity
import android.app.Application
import com.facebook.FacebookPowerMockTestCase
import com.facebook.appevents.aam.MetadataIndexer
import com.facebook.appevents.codeless.CodelessManager
import com.facebook.appevents.suggestedevents.SuggestedEventsManager
import com.facebook.internal.FeatureManager
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(
    FeatureManager::class,
    CodelessManager::class,
    MetadataIndexer::class,
    SuggestedEventsManager::class)
class ActivityLifecycleTrackerTest : FacebookPowerMockTestCase() {

  private lateinit var mockApplication: Application
  private lateinit var mockActivity: Activity
  private lateinit var mockScheduledExecutor: FacebookSerialThreadPoolMockExecutor

  private val appID = "123"

  @Before
  fun init() {
    mockApplication = PowerMockito.mock(Application::class.java)
    mockActivity = PowerMockito.mock(Activity::class.java)
    PowerMockito.mockStatic(FeatureManager::class.java)
    PowerMockito.mockStatic(CodelessManager::class.java)
    PowerMockito.mockStatic(MetadataIndexer::class.java)
    PowerMockito.mockStatic(SuggestedEventsManager::class.java)

    mockScheduledExecutor = spy(FacebookSerialThreadPoolMockExecutor(1))
    Whitebox.setInternalState(
        ActivityLifecycleTracker::class.java, "singleThreadExecutor", mockScheduledExecutor)
  }

  @Test
  fun `test start tracking`() {
    ActivityLifecycleTracker.startTracking(mockApplication, appID)
    verify(mockApplication, times(1)).registerActivityLifecycleCallbacks(any())
  }

  @Test
  fun `test create activity`() {
    ActivityLifecycleTracker.onActivityCreated(mockActivity)
    verify(mockScheduledExecutor).execute(any<Runnable>())
  }

  @Test
  fun `test resume activity`() {
    var codelessManagerCounter = 0
    var metadataIndexerCounter = 0
    var suggestedEventsManagerCounter = 0

    whenever(CodelessManager.onActivityResumed(eq(mockActivity))).thenAnswer {
      codelessManagerCounter++
    }
    whenever(MetadataIndexer.onActivityResumed(eq(mockActivity))).thenAnswer {
      metadataIndexerCounter++
    }
    whenever(SuggestedEventsManager.trackActivity(eq(mockActivity))).thenAnswer {
      suggestedEventsManagerCounter++
    }
    ActivityLifecycleTracker.onActivityResumed(mockActivity)
    assertEquals(1, codelessManagerCounter)
    assertEquals(1, metadataIndexerCounter)
    assertEquals(1, suggestedEventsManagerCounter)

    verify(mockScheduledExecutor).execute(any<Runnable>())

    assertEquals(mockActivity, ActivityLifecycleTracker.getCurrentActivity())
  }
}


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

package com.facebook.appevents.internal

import android.content.Context
import com.facebook.FacebookPowerMockTestCase
import com.facebook.appevents.AppEventsLogger
import com.facebook.internal.AttributionIdentifiers
import com.facebook.internal.Utility
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mock
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.internal.WhiteboxImpl

@PrepareForTest(Utility::class)
class AppEventsLoggerUtilityTest : FacebookPowerMockTestCase() {

  private lateinit var mockContext: Context
  private lateinit var mockCompanion: AppEventsLogger.Companion
  @Before
  fun init() {
    mockStatic(Utility::class.java)
    mockContext = mock(Context::class.java)
    whenever(mockContext.packageName).thenReturn("packagename")

    mockCompanion = mock(AppEventsLogger.Companion::class.java)
    WhiteboxImpl.setInternalState(AppEventsLogger::class.java, "Companion", mockCompanion)
  }

  @Test
  fun `test with userid`() {
    whenever(mockCompanion.getUserID()).thenReturn("13379000")
    val result =
        AppEventsLoggerUtility.getJSONObjectForGraphAPICall(
            AppEventsLoggerUtility.GraphAPIActivityType.MOBILE_INSTALL_EVENT,
            AttributionIdentifiers(),
            "anonGUID",
            false,
            mockContext)
    assertNotNull(result["event"])
    assertNotNull(result["app_user_id"])
    assertNotNull(result["application_package_name"])
  }

  @Test
  fun `test without userid`() {
    whenever(mockCompanion.getUserID()).thenReturn(null)
    val result =
        AppEventsLoggerUtility.getJSONObjectForGraphAPICall(
            AppEventsLoggerUtility.GraphAPIActivityType.MOBILE_INSTALL_EVENT,
            AttributionIdentifiers(),
            "anonGUID",
            false,
            mockContext)
    assertNotNull(result["event"])
    assertThat(result.has("app_user_id")).isFalse
    assertNotNull(result["application_package_name"])
  }
}


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

package com.facebook.appevents.internal

import java.util.Locale
import org.junit.Assert
import org.junit.Test

class AppEventUtilityTest {
  @Test
  fun testNormalizePrice() {
    Locale.setDefault(Locale.US)
    var price = "$1,234.567"
    Assert.assertEquals(1_234.567, AppEventUtility.normalizePrice(price), 0.001)
    price = "price: $1,234.567. Good deal!"
    Assert.assertEquals(1_234.567, AppEventUtility.normalizePrice(price), 0.001)
    price = "price: $1,234.567. Good deal! Add 2 more? "
    Assert.assertEquals(1_234.567, AppEventUtility.normalizePrice(price), 0.001)
    price = "price: $123 for 2 items"
    Assert.assertEquals(123.0, AppEventUtility.normalizePrice(price), 0.001)
    price = "Click to check price!"
    Assert.assertEquals(0.0, AppEventUtility.normalizePrice(price), 0.001)
    price = "$-1.23"
    Assert.assertEquals(-1.23, AppEventUtility.normalizePrice(price), 0.001)
    Locale.setDefault(Locale.FRANCE)
    price = "$1,234"
    Assert.assertEquals(1.234, AppEventUtility.normalizePrice(price), 0.001)
    Locale.setDefault(Locale.ITALY)
    price = "$1.234,567"
    Assert.assertEquals(1_234.567, AppEventUtility.normalizePrice(price), 0.001)
  }
}


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

package com.facebook.appevents.internal

import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.util.Log
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.appevents.AppEventsConstants
import com.facebook.appevents.AppEventsLogger
import com.facebook.appevents.InternalAppEventsLogger
import com.facebook.internal.FetchedAppGateKeepersManager
import com.facebook.internal.FetchedAppSettings
import com.facebook.internal.FetchedAppSettingsManager
import java.math.BigDecimal
import java.util.Currency
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mock
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.api.mockito.PowerMockito.verifyNew
import org.powermock.api.mockito.PowerMockito.whenNew
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox
import org.powermock.reflect.internal.WhiteboxImpl
import org.robolectric.Robolectric
import org.robolectric.RuntimeEnvironment

@PrepareForTest(
    Log::class,
    FacebookSdk::class,
    InternalAppEventsLogger::class,
    FetchedAppSettings::class,
    FetchedAppSettingsManager::class,
    AutomaticAnalyticsLogger::class,
    FetchedAppGateKeepersManager::class)
class AutomaticAnalyticsLoggerTest : FacebookPowerMockTestCase() {

  private val appID = "123"
  private val activityName = "activity name"
  private val timeSpent = 5L
  private val purchase =
      "{\"productId\":\"id123\", \"purchaseTime\":\"12345\", \"purchaseToken\": \"token123\"}"
  private val skuDetails = "{\"price_currency_code\":\"USD\",\"price_amount_micros\":5000}"

  private var logWarningCallCount = 0
  private var appEventLoggerCallCount = 0

  private lateinit var context: Context
  private lateinit var mockInternalAppEventsLogger: InternalAppEventsLogger
  private lateinit var mockBundle: Bundle
  private lateinit var mockFetchedAppSettings: FetchedAppSettings

  @Before
  fun init() {
    mockStatic(FacebookSdk::class.java)
    mockStatic(Log::class.java)
    mockStatic(FetchedAppSettingsManager::class.java)
    mockStatic(FetchedAppGateKeepersManager::class.java)

    context = Robolectric.buildActivity(Activity::class.java).get()
    whenever(FacebookSdk.getApplicationContext()).thenReturn(context)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn(appID)

    mockInternalAppEventsLogger = mock(InternalAppEventsLogger::class.java)
    whenNew(InternalAppEventsLogger::class.java)
        .withAnyArguments()
        .thenReturn(mockInternalAppEventsLogger)

    Whitebox.setInternalState(
        AutomaticAnalyticsLogger::class.java,
        "internalAppEventsLogger",
        mockInternalAppEventsLogger)

    mockBundle = mock(Bundle::class.java)
    whenNew(Bundle::class.java).withAnyArguments().thenReturn(mockBundle)

    mockFetchedAppSettings = mock(FetchedAppSettings::class.java)
    whenever(FetchedAppSettingsManager.queryAppSettings(appID, false))
        .thenReturn(mockFetchedAppSettings)
    whenever(FetchedAppSettingsManager.getAppSettingsWithoutQuery(appID))
        .thenReturn(mockFetchedAppSettings)

    whenever(Log.w(any(), any<String>())).then { logWarningCallCount++ }
    whenever(mockFetchedAppSettings.iAPAutomaticLoggingEnabled).thenReturn(true)

    val mockManager = mock(FetchedAppGateKeepersManager::class.java)
    Whitebox.setInternalState(FetchedAppGateKeepersManager::class.java, "INSTANCE", mockManager)

    whenever(mockManager.getGateKeeperForKey(any<String>(), any<String>(), any<Boolean>()))
        .thenReturn(true)
  }

  @Test
  fun `test log activate app event when autoLogAppEvent disable`() {
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(false)

    AutomaticAnalyticsLogger.logActivateAppEvent()

    assertEquals(0, logWarningCallCount)
    assertEquals(0, appEventLoggerCallCount)
  }

  @Test
  fun `test log activate app event when autoLogAppEvent enable`() {
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(true)

    AutomaticAnalyticsLogger.logActivateAppEvent()

    assertEquals(1, logWarningCallCount)
  }

  @Test
  fun `test log activate app event when autoLogAppEvent enable & context is application`() {
    val appContext = RuntimeEnvironment.application
    whenever(FacebookSdk.getApplicationContext()).thenReturn(appContext)
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(true)
    val mockCompanion = mock(AppEventsLogger.Companion::class.java)
    WhiteboxImpl.setInternalState(AppEventsLogger::class.java, "Companion", mockCompanion)
    whenever(mockCompanion.activateApp(appContext, appID)).then { appEventLoggerCallCount++ }

    AutomaticAnalyticsLogger.logActivateAppEvent()

    assertEquals(1, appEventLoggerCallCount)
  }

  @Test
  fun `test log activity time spent event when automatic logging disable`() {
    whenever(mockFetchedAppSettings.automaticLoggingEnabled).thenReturn(false)

    AutomaticAnalyticsLogger.logActivityTimeSpentEvent(activityName, timeSpent)

    verify(mockFetchedAppSettings).automaticLoggingEnabled
    verifyNew(Bundle::class.java, never()).withArguments(any())
    verify(mockInternalAppEventsLogger, never()).logEvent(any(), any())
  }

  @Test
  fun `test log activity time spent event when automatic logging enable`() {
    whenever(mockFetchedAppSettings.automaticLoggingEnabled).thenReturn(true)

    AutomaticAnalyticsLogger.logActivityTimeSpentEvent(activityName, timeSpent)

    verify(mockFetchedAppSettings).automaticLoggingEnabled
    verifyNew(Bundle::class.java).withArguments(eq(1))
    verify(mockInternalAppEventsLogger)
        .logEvent(eq(Constants.AA_TIME_SPENT_EVENT_NAME), eq(5.0), eq(mockBundle))
  }

  @Test
  fun `test log purchase when implicit purchase logging disable`() {
    var appGateKeepersManagerCallCount = 0
    val mockManager = mock(FetchedAppGateKeepersManager::class.java)
    Whitebox.setInternalState(FetchedAppGateKeepersManager::class.java, "INSTANCE", mockManager)
    whenever(mockManager.getGateKeeperForKey(any<String>(), any<String>(), any<Boolean>())).then {
      appGateKeepersManagerCallCount++
    }
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(false)

    AutomaticAnalyticsLogger.logPurchase(purchase, skuDetails, true)

    assertEquals(0, appGateKeepersManagerCallCount)
  }

  @Test
  fun `test log purchase when implicit purchase logging enable & subscribed`() {
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(true)
    AutomaticAnalyticsLogger.logPurchase(purchase, skuDetails, true)
    verify(mockInternalAppEventsLogger)
        .logEventImplicitly(
            eq(AppEventsConstants.EVENT_NAME_SUBSCRIBE),
            any<BigDecimal>(),
            any<Currency>(),
            any<Bundle>())
  }

  @Test
  fun `test log purchase when implicit purchase logging enable & not subscribed`() {
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(true)
    AutomaticAnalyticsLogger.logPurchase(purchase, skuDetails, false)
    verify(mockInternalAppEventsLogger)
        .logPurchaseImplicitly(any<BigDecimal>(), any<Currency>(), any<Bundle>())
  }

  @Test
  fun `test is implicit purchase logging enabled when autoLogAppEvent Disable`() {
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(false)
    val result = AutomaticAnalyticsLogger.isImplicitPurchaseLoggingEnabled()
    assertEquals(false, result)
  }

  @Test
  fun `test is implicit purchase logging enabled when autoLogAppEvent enable`() {
    whenever(FacebookSdk.getAutoLogAppEventsEnabled()).thenReturn(true)
    val result2 = AutomaticAnalyticsLogger.isImplicitPurchaseLoggingEnabled()
    assertEquals(true, result2)
  }
}


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

package com.facebook.appevents.internal

import android.annotation.TargetApi
import com.facebook.FacebookPowerMockTestCase
import java.io.ByteArrayInputStream
import java.io.DataInputStream
import java.io.File
import java.net.URL
import java.net.URLConnection
import java.nio.file.Files
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.internal.WhiteboxImpl

@PrepareForTest(FileDownloadTask::class)
class FileDownloadTaskTest : FacebookPowerMockTestCase() {
  private val uriStr = "www.facebook.com"
  private val content = "this is a test"
  private val fileName = "tempFile.txt"

  private lateinit var tempFile: File
  private lateinit var mockUrl: URL
  private lateinit var mockConnection: URLConnection
  private lateinit var mockDataInputStream: DataInputStream

  @Before
  fun `set up`() {
    tempFile = File.createTempFile(fileName, null)
    mockUrl = mock()
    mockConnection = mock()
    mockDataInputStream = DataInputStream(ByteArrayInputStream(content.toByteArray()))

    whenever(mockConnection.contentLength).thenReturn(content.length)
    whenever(mockUrl.openConnection()).thenReturn(mockConnection)
    PowerMockito.whenNew(URL::class.java).withArguments(uriStr).thenReturn(mockUrl)
    PowerMockito.whenNew(DataInputStream::class.java)
        .withAnyArguments()
        .thenReturn(mockDataInputStream)
  }

  @After
  fun `tear down`() {
    tempFile.deleteOnExit()
  }

  @Test
  @TargetApi(26)
  fun `test file download`() {
    val task = mock<FileDownloadTask>()
    whenever(task.doInBackground()).thenCallRealMethod()
    WhiteboxImpl.setInternalState(task, "uriStr", uriStr)
    WhiteboxImpl.setInternalState(task, "destFile", tempFile)
    assertThat(task.doInBackground()).isTrue

    val lines = Files.readAllLines(tempFile.toPath())
    assertEquals(1, lines.size)
    assertEquals(content, lines[0])
  }
}


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

package com.facebook.appevents.internal

import com.facebook.FacebookPowerMockTestCase
import com.facebook.util.common.assertThrows
import java.io.File
import java.io.FileNotFoundException
import org.junit.Assert.assertEquals
import org.junit.Test

class HashUtilsTest : FacebookPowerMockTestCase() {

  @Test
  fun `test computeChecksum with non-empty file`() {
    val tempFile = File.createTempFile("tempFile.txt", null)
    tempFile.writeText("Hello World!")
    tempFile.deleteOnExit()

    val expectedChecksum = "ed076287532e86365e841e92bfc50d8c"
    val actualChecksum = HashUtils.computeChecksum(tempFile.path)
    assertEquals(expectedChecksum, actualChecksum)
  }

  @Test
  fun `test computeChecksum with empty file`() {
    val tempFile = File.createTempFile("tempFile.txt", null)
    tempFile.deleteOnExit()

    val emptyFileChecksum = "d41d8cd98f00b204e9800998ecf8427e"
    val actualChecksum = HashUtils.computeChecksum(tempFile.path)
    assertEquals(emptyFileChecksum, actualChecksum)
  }

  @Test
  fun `test computeChecksum with missing file throws exception`() {
    assertThrows<FileNotFoundException> { HashUtils.computeChecksum("/does/not/exist.txt") }
  }
}


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

package com.facebook.appevents.internal

import android.content.Context
import android.preference.PreferenceManager
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.MockSharedPreference
import java.util.UUID
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.isA
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mock
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox
import org.powermock.reflect.internal.WhiteboxImpl

@PrepareForTest(FacebookSdk::class, PreferenceManager::class, SourceApplicationInfo::class)
class SessionInfoTest : FacebookPowerMockTestCase() {

  private lateinit var mockContext: Context
  private lateinit var mockSharedPreferences: MockSharedPreference
  private lateinit var mockSessionInfo: SessionInfo

  private val sessionStartTime = 1L
  private val sessionLastEventTime = 2L
  private val interruptionCount = 0
  private val sessionId = UUID(2, 1)
  private var clearSavedSourceApplicationInfoFromDiskHasBeenCalledTime = 0

  @Before
  fun init() {
    clearSavedSourceApplicationInfoFromDiskHasBeenCalledTime = 0

    mockContext = mock(Context::class.java)
    mockSharedPreferences = MockSharedPreference()
    mockSessionInfo = mock(SessionInfo::class.java)

    Whitebox.setInternalState(mockSessionInfo, "sessionStartTime", sessionStartTime)
    Whitebox.setInternalState(mockSessionInfo, "sessionLastEventTime", sessionLastEventTime)
    Whitebox.setInternalState(mockSessionInfo, "interruptionCount", interruptionCount)
    Whitebox.setInternalState(mockSessionInfo, "sessionId", sessionId)
    whenever(mockSessionInfo.writeSessionToDisk()).thenCallRealMethod()

    mockStatic(FacebookSdk::class.java)
    mockStatic(PreferenceManager::class.java)

    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockContext)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(PreferenceManager.getDefaultSharedPreferences(isA(Context::class.java)))
        .thenReturn(mockSharedPreferences)

    val mockSourceAppInfoCompanion = mock(SourceApplicationInfo.Companion::class.java)
    WhiteboxImpl.setInternalState(
        SourceApplicationInfo::class.java, "Companion", mockSourceAppInfoCompanion)
    whenever(mockSourceAppInfoCompanion.clearSavedSourceApplicationInfoFromDisk()).then {
      clearSavedSourceApplicationInfoFromDiskHasBeenCalledTime++
    }
  }

  @Test
  fun `write session to disk and get the session info when source application info is null`() {
    mockSessionInfo.writeSessionToDisk()
    val sessionInfo = SessionInfo.getStoredSessionInfo()
    assertEquals(sessionStartTime, sessionInfo?.sessionStartTime)
    assertEquals(sessionLastEventTime, sessionInfo?.sessionLastEventTime)
    assertEquals(interruptionCount, sessionInfo?.interruptionCount)
    assertEquals(sessionId, sessionInfo?.sessionId)
  }

  @Test
  fun `write session to disk and get the session info when source application info is not null`() {
    val mockSourceApplicationInfo = mock(SourceApplicationInfo::class.java)
    Whitebox.setInternalState(mockSessionInfo, "sourceApplicationInfo", mockSourceApplicationInfo)
    mockSessionInfo.writeSessionToDisk()
    val sessionInfo = SessionInfo.getStoredSessionInfo()
    assertEquals(sessionStartTime, sessionInfo?.sessionStartTime)
    assertEquals(sessionLastEventTime, sessionInfo?.sessionLastEventTime)
    assertEquals(interruptionCount, sessionInfo?.interruptionCount)
    assertEquals(sessionId, sessionInfo?.sessionId)
    verify(mockSourceApplicationInfo).writeSourceApplicationInfoToDisk()
  }

  @Test
  fun `get stored session info when there is no stored session info`() {
    val sessionInfo = SessionInfo.getStoredSessionInfo()
    assertNull(sessionInfo)
  }

  @Test
  fun `clear saved session from disk`() {
    mockSessionInfo.writeSessionToDisk()
    SessionInfo.clearSavedSessionFromDisk()
    val sessionInfo = SessionInfo.getStoredSessionInfo()
    assertNull(sessionInfo)
    assertEquals(1, clearSavedSourceApplicationInfoFromDiskHasBeenCalledTime)
  }

  @Test
  fun `clear saved session from disk when there is no saved session`() {
    SessionInfo.clearSavedSessionFromDisk()
    val sessionInfo = SessionInfo.getStoredSessionInfo()
    assertNull(sessionInfo)
    assertEquals(1, clearSavedSourceApplicationInfoFromDiskHasBeenCalledTime)
  }
}


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

package com.facebook.appevents.internal

import android.content.Context
import android.content.SharedPreferences
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Bundle
import android.text.format.DateUtils
import com.facebook.FacebookPowerMockTestCase
import com.facebook.MockSharedPreference
import com.facebook.appevents.AppEventsConstants
import com.facebook.appevents.InternalAppEventsLogger
import com.facebook.internal.security.CertificateUtil
import java.util.Locale
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.data.Offset
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.KArgumentCaptor
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(HashUtils::class, CertificateUtil::class)
class SessionLoggerTest : FacebookPowerMockTestCase() {
  private lateinit var mockSessionInfo: SessionInfo
  private lateinit var mockInternalAppEventsLogger: InternalAppEventsLogger
  private lateinit var mockInternalAppEventsLoggerCompanion: InternalAppEventsLogger.Companion
  private lateinit var mockContext: Context
  private lateinit var mockPreferences: SharedPreferences
  private lateinit var mockPackageManager: PackageManager
  private lateinit var doubleArgumentCaptor: KArgumentCaptor<Double>
  private lateinit var bundleArgumentCaptor: KArgumentCaptor<Bundle>

  private val activityName = "swagactivity"
  private val appId = "yoloapplication"
  private val diskRestoreTime = 10L
  private val sessionLastEventTime = 1L
  private val zeroDelta = 0.0
  private val mockChecksum = "1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

  @Before
  fun init() {
    doubleArgumentCaptor = argumentCaptor()
    bundleArgumentCaptor = argumentCaptor()
    mockSessionInfo = mock()
    whenever(mockSessionInfo.sessionLength).thenReturn(10L)
    Whitebox.setInternalState(mockSessionInfo, "sessionLastEventTime", sessionLastEventTime)
    Whitebox.setInternalState(mockSessionInfo, "diskRestoreTime", diskRestoreTime)

    mockInternalAppEventsLogger = mock()
    mockInternalAppEventsLoggerCompanion = mock()
    whenever(mockInternalAppEventsLoggerCompanion.createInstance(activityName, appId, null))
        .thenReturn(mockInternalAppEventsLogger)
    Whitebox.setInternalState(
        InternalAppEventsLogger::class.java, "Companion", mockInternalAppEventsLoggerCompanion)

    mockContext = mock()
    mockPackageManager = mock()
    whenever(mockContext.packageName).thenReturn(appId)
    whenever(mockContext.packageManager).thenReturn(mockPackageManager)
    val mockPackageInfo = mock<PackageInfo>()
    mockPackageInfo.versionName = "v0.0"
    whenever(mockPackageManager.getPackageInfo(any<String>(), any())).thenReturn(mockPackageInfo)
    mockPreferences = MockSharedPreference()
    whenever(mockContext.getSharedPreferences(any<String>(), any())).thenReturn(mockPreferences)
    PowerMockito.mockStatic(HashUtils::class.java)
    whenever(HashUtils.computeChecksumWithPackageManager(eq(mockContext), anyOrNull()))
        .thenReturn(mockChecksum)
    PowerMockito.mockStatic(CertificateUtil::class.java)
    whenever(CertificateUtil.getCertificateHash(mockContext)).thenReturn("")
  }

  @Test
  fun `logDeactivateApp when sessionInfo is null`() {
    SessionLogger.logDeactivateApp(activityName, null, appId)
    verify(mockInternalAppEventsLogger, never())
        .logEvent(eq(AppEventsConstants.EVENT_NAME_DEACTIVATED_APP), any(), any())
  }

  @Test
  fun `logDeactivateApp when sessionInfo is not null and sessionLength is negative`() {
    val expectedValueToSum = 0.0
    val sessionLengthNegative = -1L
    whenever(mockSessionInfo.sessionLength).thenReturn(sessionLengthNegative)

    SessionLogger.logDeactivateApp(activityName, mockSessionInfo, appId)

    verify(mockInternalAppEventsLogger)
        .logEvent(
            eq(AppEventsConstants.EVENT_NAME_DEACTIVATED_APP),
            doubleArgumentCaptor.capture(),
            bundleArgumentCaptor.capture())
    assertThat(doubleArgumentCaptor.firstValue)
        .isEqualTo(expectedValueToSum, Offset.offset(zeroDelta))
  }

  @Test
  fun `logDeactivateApp when sessionInfo is not null and interruptionDurationMillis is negative`() {
    val sessionLastEventTime2 = 100L
    val diskRestoreTime2 = 1L
    Whitebox.setInternalState(mockSessionInfo, "sessionLastEventTime", sessionLastEventTime2)
    Whitebox.setInternalState(mockSessionInfo, "diskRestoreTime", diskRestoreTime2)

    val interruptionDurationMillis = 0L
    val fbMobileTimeBetweenSessions =
        String.format(
            Locale.ROOT,
            "session_quanta_%d",
            SessionLogger.getQuantaIndex(interruptionDurationMillis))
    val expectedValueToSum = mockSessionInfo.sessionLength.toDouble() / DateUtils.SECOND_IN_MILLIS

    SessionLogger.logDeactivateApp(activityName, mockSessionInfo, appId)

    verify(mockInternalAppEventsLogger)
        .logEvent(
            eq(AppEventsConstants.EVENT_NAME_DEACTIVATED_APP),
            doubleArgumentCaptor.capture(),
            bundleArgumentCaptor.capture())
    assertThat(doubleArgumentCaptor.firstValue)
        .isEqualTo(expectedValueToSum, Offset.offset(zeroDelta))
    assertThat(
            bundleArgumentCaptor.firstValue.getString(
                AppEventsConstants.EVENT_NAME_TIME_BETWEEN_SESSIONS))
        .isEqualTo(fbMobileTimeBetweenSessions)
  }

  @Test
  fun `logDeactivateApp when sessionInfo is not null, sessionLength is positive, and interruptionDurationMillis is positive`() {
    val interruptionDurationMillis = diskRestoreTime - sessionLastEventTime
    val expectedValueToSum = mockSessionInfo.sessionLength.toDouble() / DateUtils.SECOND_IN_MILLIS
    val fbMobileTimeBetweenSessions =
        String.format(
            Locale.ROOT,
            "session_quanta_%d",
            SessionLogger.getQuantaIndex(interruptionDurationMillis))

    SessionLogger.logDeactivateApp(activityName, mockSessionInfo, appId)

    verify(mockInternalAppEventsLogger)
        .logEvent(
            eq(AppEventsConstants.EVENT_NAME_DEACTIVATED_APP),
            doubleArgumentCaptor.capture(),
            bundleArgumentCaptor.capture())
    assertThat(doubleArgumentCaptor.firstValue)
        .isEqualTo(expectedValueToSum, Offset.offset(zeroDelta))
    assertThat(
            bundleArgumentCaptor.firstValue.getString(
                AppEventsConstants.EVENT_NAME_TIME_BETWEEN_SESSIONS))
        .isEqualTo(fbMobileTimeBetweenSessions)
  }

  @Test
  fun `test logActivateApp will log EVENT_NAME_ACTIVATED_APP event and package checksum`() {
    SessionLogger.logActivateApp(activityName, null, appId, mockContext)

    verify(mockInternalAppEventsLogger)
        .logEvent(eq(AppEventsConstants.EVENT_NAME_ACTIVATED_APP), bundleArgumentCaptor.capture())
    val capturedBundle = bundleArgumentCaptor.firstValue
    assertThat(capturedBundle.getString(AppEventsConstants.EVENT_PARAM_PACKAGE_FP))
        .isEqualTo(mockChecksum)
  }
}


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

package com.facebook.appevents.internal

import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.preference.PreferenceManager
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.MockSharedPreference
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mock
import org.powermock.api.mockito.PowerMockito.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class, PreferenceManager::class)
class SourceApplicationInfoTest : FacebookPowerMockTestCase() {
  private lateinit var mockSharedPreferences: MockSharedPreference
  private lateinit var mockContext: Context

  private val callingApplicationPackage = "com.facebook.testapp"
  private val newCallingApplicationPackage = "com.facebook.newApp"
  private val openByAppLink = true
  @Before
  fun init() {
    mockStatic(FacebookSdk::class.java)
    mockStatic(PreferenceManager::class.java)
    mockContext = mock(Context::class.java)
    mockSharedPreferences = MockSharedPreference()
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockContext)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(PreferenceManager.getDefaultSharedPreferences(mockContext))
        .thenReturn(mockSharedPreferences)
    mockSharedPreferences
        .edit()
        .putString(
            "com.facebook.appevents.SourceApplicationInfo.callingApplicationPackage",
            callingApplicationPackage)
        .putBoolean("com.facebook.appevents.SourceApplicationInfo.openedByApplink", openByAppLink)
        .apply()
  }

  @Test
  fun `test get a new stored source application info`() {
    val sourceAppInfo = SourceApplicationInfo.getStoredSourceApplicatioInfo()
    assertEquals(callingApplicationPackage, sourceAppInfo?.callingApplicationPackage)
    assertEquals(openByAppLink, sourceAppInfo?.isOpenedByAppLink)
    assertEquals("Applink(com.facebook.testapp)", sourceAppInfo.toString())
  }

  @Test
  fun `test clear stored source application info from disk`() {
    SourceApplicationInfo.clearSavedSourceApplicationInfoFromDisk()
    val sourceAppInfo = SourceApplicationInfo.getStoredSourceApplicatioInfo()
    assertNull(sourceAppInfo)
  }

  @Test
  fun `test build source application info and write to disk`() {
    val mockActivity = mock(Activity::class.java)
    val mockComponentName = mock(ComponentName::class.java)
    val mockIntent = mock(Intent::class.java)

    whenever(mockComponentName.packageName).thenReturn(newCallingApplicationPackage)
    whenever(mockActivity.packageName).thenReturn(callingApplicationPackage)
    whenever(mockActivity.getCallingActivity()).thenReturn(mockComponentName)
    whenever(mockActivity.getIntent()).thenReturn(mockIntent)
    var sourceAppInfo = SourceApplicationInfo.Factory.create(mockActivity)
    assertEquals(false, sourceAppInfo?.isOpenedByAppLink)
    assertEquals(newCallingApplicationPackage, sourceAppInfo?.callingApplicationPackage)

    sourceAppInfo?.writeSourceApplicationInfoToDisk()
    sourceAppInfo = SourceApplicationInfo.getStoredSourceApplicatioInfo()
    assertEquals(newCallingApplicationPackage, sourceAppInfo?.callingApplicationPackage)
    assertEquals(false, sourceAppInfo?.isOpenedByAppLink)
  }
}


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

package com.facebook.appevents.ml

import android.content.Context
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.GraphRequest
import com.facebook.GraphResponse
import com.facebook.MockSharedPreference
import com.facebook.internal.FeatureManager
import java.io.File
import java.util.concurrent.ConcurrentHashMap
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.Test
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(FacebookSdk::class, FeatureManager::class)
class ModelManagerTest : FacebookPowerMockTestCase() {
  companion object {
    private const val MOCK_APP_ID = "123456987"

    // Created based on the parsing code in ModelManager.parseRawJsonObject()
    private const val MOCK_MODEL_JSON_AS_TEXT =
        """
    {
      "data": [{
        "version_id": "mock_version",
        "use_case": "mock_use_case",
        "thresholds": ["mock_threshold_1", "mock_threshold_2"],
        "asset_uri": "mock_asset_uri"
      }]
    }
    """

    // The parsing code transforms the model JSON into this expected format
    //  - Top level key is the use case value (data key is removed)
    //  - All whitespace is removed (not sure if this is side effect is important or not)
    private val EXPECTED_MODEL_JSON_AS_TEXT =
        """
    {"mock_use_case":{"version_id":"mock_version","use_case":"mock_use_case","thresholds":["mock_threshold_1","mock_threshold_2"],"asset_uri":"mock_asset_uri"}}
    """.trim()

    // Mirror these constant keys so we can make sure they don't get accidentally changed
    private const val MODEL_ASSET_STORE_KEY = "com.facebook.internal.MODEL_STORE"
    private const val CACHE_KEY_MODEL_REQUEST_TIMESTAMP = "model_request_timestamp"
    private const val CACHE_KEY_MODELS = "models"
  }
  @Mock private lateinit var mockApplicationContext: Context
  private lateinit var mockGraphRequestCompanion: GraphRequest.Companion
  private lateinit var mockSharedPreferences: MockSharedPreference

  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getExecutor()).thenReturn(FacebookSerialExecutor())
    whenever(FacebookSdk.getApplicationId()).thenReturn(MOCK_APP_ID)
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockApplicationContext)
    mockSharedPreferences = MockSharedPreference()
    whenever(mockApplicationContext.getSharedPreferences(anyString(), any()))
        .thenReturn(mockSharedPreferences)
    mockGraphRequestCompanion = mock()
    Whitebox.setInternalState(GraphRequest::class.java, "Companion", mockGraphRequestCompanion)
    PowerMockito.mockStatic(FeatureManager::class.java)
  }

  @Test
  fun `test enable() checks ModelRequest feature`() {
    var didCheck = false
    whenever(FeatureManager.isEnabled(FeatureManager.Feature.ModelRequest)).thenAnswer {
      didCheck = true
      return@thenAnswer true
    }
    whenever(mockGraphRequestCompanion.newGraphPathRequest(any(), any(), any())).thenReturn(mock())
    ModelManager.enable()
    assertThat(didCheck).isTrue
  }

  @Test
  fun `test enable() fetches model assets with graph request`() {
    whenever(FeatureManager.isEnabled(FeatureManager.Feature.ModelRequest)).thenReturn(false)
    var capturedPath: String? = null
    val mockGraphRequest: GraphRequest = mock()
    whenever(mockGraphRequestCompanion.newGraphPathRequest(eq(null), any(), eq(null))).thenAnswer {
      capturedPath = it.arguments[1].toString()
      mockGraphRequest
    }
    whenever(mockGraphRequest.executeAndWait()).thenReturn(mock())

    ModelManager.enable()
    checkNotNull(capturedPath)
    assertThat(capturedPath).contains("model_asset")
  }

  @Test
  fun `test enable() checks model store in shared preferences`() {
    whenever(FeatureManager.isEnabled(FeatureManager.Feature.ModelRequest)).thenReturn(false)
    whenever(mockGraphRequestCompanion.newGraphPathRequest(any(), any(), any())).thenReturn(mock())
    whenever(
            mockApplicationContext.getSharedPreferences(
                MODEL_ASSET_STORE_KEY, Context.MODE_PRIVATE))
        .thenReturn(mock())
    ModelManager.enable()
    verify(mockApplicationContext).getSharedPreferences(MODEL_ASSET_STORE_KEY, Context.MODE_PRIVATE)
  }

  @Test
  fun `test getRuleFile() retrieves appropriate taskhandler rule file`() {
    // Rather than mocking this private class field, I'd prefer to have it be populated with
    // some meaningful values, and then test how it behaves.  However, these unit tests do not
    // currently have a way to fill the ModelManager with realistic configuration data.
    val mockTaskHandlers: ConcurrentHashMap<Any, Any> = mock()
    Whitebox.setInternalState(ModelManager::class.java, "taskHandlers", mockTaskHandlers)
    val integrityFile = File("MTML_INTEGRITY_DETECT")
    val mockTaskHandler: ModelManager.TaskHandler = mock()
    whenever(mockTaskHandlers[ModelManager.Task.MTML_INTEGRITY_DETECT.toUseCase()])
        .thenReturn(mockTaskHandler)
    whenever(mockTaskHandler.ruleFile).thenReturn(integrityFile)
    val resultFile = ModelManager.getRuleFile(ModelManager.Task.MTML_INTEGRITY_DETECT)
    assertThat(resultFile).isEqualTo(integrityFile)
  }

  private fun setupMocksForModelFetch(modelRequestTimestamp: Long) {
    mockSharedPreferences
        .edit()
        .putLong(CACHE_KEY_MODEL_REQUEST_TIMESTAMP, modelRequestTimestamp)
        // Return some JSON, as though the model cache contained something
        .putString(CACHE_KEY_MODELS, MOCK_MODEL_JSON_AS_TEXT)
        .apply()

    // Force ModelManager to exercise its cache logic
    whenever(FeatureManager.isEnabled(FeatureManager.Feature.ModelRequest)).thenReturn(true)

    val mockGraphRequest: GraphRequest = mock()
    whenever(mockGraphRequestCompanion.newGraphPathRequest(eq(null), any(), eq(null))).thenAnswer {
      mockGraphRequest
    }

    // Mock the GraphResponse to return JSON so that fetchModels() can complete successfully
    val mockGraphResponse: GraphResponse = mock()
    val mockModelJson = JSONObject(MOCK_MODEL_JSON_AS_TEXT)
    whenever(mockGraphResponse.getJSONObject()).thenReturn(mockModelJson)
    whenever(mockGraphRequest.executeAndWait()).thenReturn(mockGraphResponse)
  }

  private fun assertThatModelWasFetchedAndCached() {
    assertThat(mockSharedPreferences.getString(CACHE_KEY_MODELS, null))
        .isEqualTo(EXPECTED_MODEL_JSON_AS_TEXT)
    assertThat(mockSharedPreferences.getLong(CACHE_KEY_MODEL_REQUEST_TIMESTAMP, 0L))
        .isGreaterThan(0L)
  }

  @Test
  fun `test enable() fetches model assets when cache request timestamp is zero`() {
    // Use a timestamp of zero, as though the cache was empty
    setupMocksForModelFetch(0L)
    ModelManager.enable()
    assertThatModelWasFetchedAndCached()
  }

  @Test
  fun `test enable() fetches model assets when cache is expired`() {
    // Set the cache timestamp to be 1 millisecond older than the expiration interval
    val justExpiredTimestamp: Long = (ModelManager.MODEL_REQUEST_INTERVAL_MILLISECONDS + 1).toLong()
    assertThat(justExpiredTimestamp).isEqualTo(259200001)

    setupMocksForModelFetch(justExpiredTimestamp)
    ModelManager.enable()
    assertThatModelWasFetchedAndCached()
    // Make sure we don't still have the expired model cache timestamp saved
    assertThat(mockSharedPreferences.getLong(CACHE_KEY_MODEL_REQUEST_TIMESTAMP, 0L))
        .isNotEqualTo(justExpiredTimestamp)
  }

  @Test
  fun `test enable() does not fetch model assets when cache is present`() {
    // This recent cache time should result in the cached model getting used
    val recentlyCachedTime = System.currentTimeMillis() - 1000
    setupMocksForModelFetch(recentlyCachedTime)
    ModelManager.enable()
    assertThat(mockSharedPreferences.getString(CACHE_KEY_MODELS, null))
        .isEqualTo(MOCK_MODEL_JSON_AS_TEXT)
    assertThat(mockSharedPreferences.getLong(CACHE_KEY_MODEL_REQUEST_TIMESTAMP, 0L))
        .isEqualTo(recentlyCachedTime)
  }
}


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

package com.facebook.appevents.ml

import com.facebook.FacebookTestCase
import com.facebook.appevents.ml.TensorTestUtils.createModelFile
import com.facebook.appevents.ml.TensorTestUtils.createTestTensor
import java.io.File
import java.util.UUID
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.data.Offset
import org.junit.After
import org.junit.Test
import org.robolectric.annotation.Config

@Config(manifest = Config.NONE)
class ModelTest : FacebookTestCase() {
  private lateinit var testFileDir: File
  private val testWeights =
      hashMapOf(
          "embedding.weight" to createTestTensor(intArrayOf(256, 32)),
          "convs.0.weight" to createTestTensor(intArrayOf(32, 32, 3)),
          "convs.0.bias" to createTestTensor(intArrayOf(32)),
          "convs.1.weight" to createTestTensor(intArrayOf(64, 32, 3)),
          "convs.1.bias" to createTestTensor(intArrayOf(64)),
          "convs.2.weight" to createTestTensor(intArrayOf(64, 64, 3)),
          "convs.2.bias" to createTestTensor(intArrayOf(64)),
          "dense1.weight" to createTestTensor(intArrayOf(128, 190)),
          "dense1.bias" to createTestTensor(intArrayOf(128)),
          "dense2.weight" to createTestTensor(intArrayOf(64, 128)),
          "dense2.bias" to createTestTensor(intArrayOf(64)),
          "integrity_detect.weight" to createTestTensor(intArrayOf(3, 64)),
          "integrity_detect.bias" to createTestTensor(intArrayOf(3)),
          "app_event_pred.weight" to createTestTensor(intArrayOf(5, 64)),
          "app_event_pred.bias" to createTestTensor(intArrayOf(5)))

  override fun setUp() {
    super.setUp()
    testFileDir = File(UUID.randomUUID().toString())
    testFileDir.mkdirs()
  }

  @After
  fun tearDown() {
    testFileDir.deleteRecursively()
  }

  @Test
  fun `test build model with file`() {
    val modelFile = createModelFile(testWeights, testFileDir)
    val model = checkNotNull(Model.build(modelFile))
    val dense = createTestTensor(intArrayOf(1, 30))
    val result = model.predictOnMTML(dense, arrayOf("test"), "app_event_pred")
    checkNotNull(result)
    assertThat(result.data.sum()).isEqualTo(1.0f, Offset.offset(1e-7f))
  }

  @Test
  fun `test run invalid task`() {
    val modelFile = createModelFile(testWeights, testFileDir)
    val model = checkNotNull(Model.build(modelFile))
    val dense = createTestTensor(intArrayOf(1, 30))
    val result = model.predictOnMTML(dense, arrayOf("test"), "invalid task")
    assertThat(result).isNull()
  }

  @Test
  fun `test build model with invalid model file`() {
    val weights = hashMapOf("embedding.weight" to createTestTensor(intArrayOf(256, 32)))
    val modelFile = createModelFile(weights, testFileDir)
    val model = Model.build(modelFile)
    assertThat(model).isNull()
  }
}


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

package com.facebook.appevents.ml

import com.facebook.FacebookPowerMockTestCase
import com.facebook.util.common.assertThrows
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Test
import org.powermock.reflect.Whitebox

class MTensorTest : FacebookPowerMockTestCase() {

  @Test
  fun `test reshape() and getData`() {
    // Capacity: 24 (2 * 3 * 4)
    val tensor = MTensor(intArrayOf(2, 3, 4))
    val data =
        floatArrayOf(
            1f,
            3f,
            5f,
            7f,
            5f,
            7f,
            9f,
            11f,
            9f,
            11f,
            13f,
            15f,
            13f,
            15f,
            17f,
            19f,
            17f,
            19f,
            21f,
            23f,
            21f,
            23f,
            25f,
            27f)
    Whitebox.setInternalState(tensor, "data", data)

    // Reshape to capacity of 16 (2 * 2 * 4)
    tensor.reshape(intArrayOf(2, 2, 4))
    var expectedData =
        floatArrayOf(1f, 3f, 5f, 7f, 5f, 7f, 9f, 11f, 9f, 11f, 13f, 15f, 13f, 15f, 17f, 19f)
    assertArrayEquals(tensor.data, expectedData, 0.0_001f)
    assertEquals(Whitebox.getInternalState<Any>(tensor, "capacity"), 16)

    // Reshape to capacity of 24 (2 * 3 * 4)
    tensor.reshape(intArrayOf(2, 3, 4))
    expectedData =
        floatArrayOf(
            1f,
            3f,
            5f,
            7f,
            5f,
            7f,
            9f,
            11f,
            9f,
            11f,
            13f,
            15f,
            13f,
            15f,
            17f,
            19f,
            0f,
            0f,
            0f,
            0f,
            0f,
            0f,
            0f,
            0f)
    assertArrayEquals(tensor.data, expectedData, 0.0_001f)
    assertEquals(Whitebox.getInternalState<Any>(tensor, "capacity"), 24)
  }

  @Test
  fun `test resizing to large capacity can overflow`() {
    val tensor = MTensor(intArrayOf(1, 2, 3))
    val data = floatArrayOf(1f, 2f, 3f, 4f, 5f, 6f)
    Whitebox.setInternalState(tensor, "data", data)

    // Capacity becomes -6 due to int owerflow (2 * 3 * Int.MAX_VALUE)
    assertThrows<NegativeArraySizeException> { tensor.reshape(intArrayOf(2, 3, Int.MAX_VALUE)) }
  }

  @Test
  fun `test getShape`() {
    val tensor = MTensor(intArrayOf(2, 3, 4))
    assertEquals(2, tensor.getShape(0))
    assertEquals(3, tensor.getShape(1))
    assertEquals(4, tensor.getShape(2))
  }

  @Test
  fun `test getShape() after reshape`() {
    val tensor = MTensor(intArrayOf(2, 3, 4))
    tensor.reshape(intArrayOf(6, 1, 2, 2))
    assertEquals(6, tensor.getShape(0))
    assertEquals(1, tensor.getShape(1))
    assertEquals(2, tensor.getShape(2))
    assertEquals(2, tensor.getShape(3))
    assertEquals(4, tensor.shapeSize)
  }

  @Test
  fun `test getShapeSize`() {
    val tensor = MTensor(intArrayOf(2, 3, 4))
    assertEquals(3, tensor.shapeSize)
  }
}


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

package com.facebook.appevents.ml

import com.facebook.appevents.ml.TensorTestUtils.setTensorData
import org.junit.Assert
import org.junit.Test

class OperatorTest {
  @Test
  fun `test addmv`() {
    val x = MTensor(intArrayOf(2, 3, 4))
    setTensorData(
        x,
        floatArrayOf(
            0f,
            1f,
            2f,
            3f,
            4f,
            5f,
            6f,
            7f,
            8f,
            9f,
            10f,
            11f,
            12f,
            13f,
            14f,
            15f,
            16f,
            17f,
            18f,
            19f,
            20f,
            21f,
            22f,
            23f))
    val bias = MTensor(intArrayOf(4))
    setTensorData(bias, floatArrayOf(1f, 2f, 3f, 4f))
    Operator.addmv(x, bias)
    val expectedData =
        floatArrayOf(
            1f,
            3f,
            5f,
            7f,
            5f,
            7f,
            9f,
            11f,
            9f,
            11f,
            13f,
            15f,
            13f,
            15f,
            17f,
            19f,
            17f,
            19f,
            21f,
            23f,
            21f,
            23f,
            25f,
            27f)
    Assert.assertArrayEquals(expectedData, x.data, 0.0_001f)
  }

  @Test
  fun `test mul`() {
    val x = MTensor(intArrayOf(2, 4))
    setTensorData(x, floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f))
    val weight = MTensor(intArrayOf(4, 2))
    setTensorData(weight, floatArrayOf(1f, 0f, 0f, 2f, -1f, 0f, 0f, 2f))
    val y = Operator.mul(x, weight)
    val expectedData = floatArrayOf(-2f, 8f, -2f, 24.000_001f)
    Assert.assertArrayEquals(expectedData, y.data, 0.0_001f)
  }

  @Test
  fun `test relu`() {
    val x = MTensor(intArrayOf(2, 4))
    setTensorData(x, floatArrayOf(0f, -1f, 2f, -3f, -4f, 5f, -6f, 7f))
    Operator.relu(x)
    val expectedData = floatArrayOf(0f, 0f, 2f, 0f, 0f, 5f, 0f, 7f)
    Assert.assertArrayEquals(expectedData, x.data, 0.0_001f)
  }

  @Test
  fun `test flatten`() {
    val x = MTensor(intArrayOf(2, 3, 4))
    setTensorData(
        x,
        floatArrayOf(
            0f,
            1f,
            2f,
            3f,
            4f,
            5f,
            6f,
            7f,
            8f,
            9f,
            10f,
            11f,
            12f,
            13f,
            14f,
            15f,
            16f,
            17f,
            18f,
            19f,
            20f,
            21f,
            22f,
            23f))
    Operator.flatten(x, 1)
    val expectedShape = intArrayOf(2, 12)
    val expectedData =
        floatArrayOf(
            0f,
            1f,
            2f,
            3f,
            4f,
            5f,
            6f,
            7f,
            8f,
            9f,
            10f,
            11f,
            12f,
            13f,
            14f,
            15f,
            16f,
            17f,
            18f,
            19f,
            20f,
            21f,
            22f,
            23f)
    Assert.assertArrayEquals(expectedData, x.data, 0.0_001f)
    expectedShape.forEachIndexed { index: Int, i: Int -> Assert.assertEquals(i, x.getShape(index)) }
    Assert.assertEquals(expectedShape.size, x.shapeSize)
  }

  @Test
  fun `test concatenate`() {
    val x1 = MTensor(intArrayOf(2, 4))
    setTensorData(x1, floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f))
    val x2 = MTensor(intArrayOf(2, 3))
    setTensorData(x2, floatArrayOf(16f, 17f, 18f, 20f, 21f, 22f))
    val x3 = MTensor(intArrayOf(2, 2))
    setTensorData(x3, floatArrayOf(4f, 5f, 6f, 7f))
    val y = Operator.concatenate(arrayOf(x1, x2, x3))
    val expectedShape = intArrayOf(2, 9)
    val expectedData =
        floatArrayOf(0f, 1f, 2f, 3f, 16f, 17f, 18f, 4f, 5f, 4f, 5f, 6f, 7f, 20f, 21f, 22f, 6f, 7f)
    Assert.assertArrayEquals(expectedData, y.data, 0.0_001f)
    expectedShape.forEachIndexed { index: Int, i: Int -> Assert.assertEquals(i, y.getShape(index)) }
    Assert.assertEquals(expectedShape.size, y.shapeSize)
  }

  @Test
  fun `test softmax`() {
    val x = MTensor(intArrayOf(2, 4))
    setTensorData(x, floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f))
    Operator.softmax(x)
    val expectedData =
        floatArrayOf(
            0.03_205_860f,
            0.08_714_432f,
            0.23_688_284f,
            0.6_439_143f,
            0.03_205_860f,
            0.08_714_432f,
            0.23_688_284f,
            0.6_439_143f)
    Assert.assertArrayEquals(expectedData, x.data, 0.00_000_001f)
  }

  @Test
  fun `test dense`() {
    val x = MTensor(intArrayOf(2, 4))
    setTensorData(x, floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f))
    val weight = MTensor(intArrayOf(4, 2))
    setTensorData(weight, floatArrayOf(1f, 0f, 0f, 2f, -1f, 0f, 0f, 2f))
    val bias = MTensor(intArrayOf(2))
    setTensorData(bias, floatArrayOf(1f, -1f))
    val y = Operator.dense(x, weight, bias)
    val expectedData = floatArrayOf(-1f, 7f, -1f, 23f)
    Assert.assertArrayEquals(expectedData, y.data, 0.0_001f)
  }

  @Test
  fun `test transpose 2D`() {
    val input = MTensor(intArrayOf(3, 4))
    setTensorData(input, floatArrayOf(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 10f, 11f))
    val output = Operator.transpose2D(input)
    val expectedData = floatArrayOf(0f, 4f, 8f, 1f, 5f, 9f, 2f, 6f, 10f, 3f, 7f, 11f)
    Assert.assertArrayEquals(expectedData, output.data, 0.0_001f)
  }

  @Test
  fun `test transpose 3D`() {
    val input = MTensor(intArrayOf(2, 3, 4))
    setTensorData(
        input,
        floatArrayOf(
            0f,
            1f,
            2f,
            3f,
            4f,
            5f,
            6f,
            7f,
            8f,
            9f,
            10f,
            11f,
            12f,
            13f,
            14f,
            15f,
            16f,
            17f,
            18f,
            19f,
            20f,
            21f,
            22f,
            23f))
    val output = Operator.transpose3D(input)
    val expectedData =
        floatArrayOf(
            0f,
            12f,
            4f,
            16f,
            8f,
            20f,
            1f,
            13f,
            5f,
            17f,
            9f,
            21f,
            2f,
            14f,
            6f,
            18f,
            10f,
            22f,
            3f,
            15f,
            7f,
            19f,
            11f,
            23f)
    Assert.assertArrayEquals(expectedData, output.data, 0.0_001f)
  }

  @Test
  fun `test conv 1D`() {
    val input = MTensor(intArrayOf(1, 5, 3))
    setTensorData(input, floatArrayOf(1f, 2f, 3f, 4f, 5f, 6f, 9f, 8f, 7f, 5f, 8f, 1f, 5f, 3f, 0f))
    val weight = MTensor(intArrayOf(3, 3, 4))
    setTensorData(
        weight,
        floatArrayOf(
            -1f,
            3f,
            0f,
            1f,
            5f,
            -7f,
            5f,
            7f,
            -9f,
            9f,
            2f,
            3f,
            2f,
            4f,
            5f,
            6f,
            6f,
            8f,
            9f,
            4f,
            10f,
            -10f,
            5f,
            6f,
            1f,
            0f,
            5f,
            6f,
            2f,
            5f,
            9f,
            4f,
            9f,
            10f,
            5f,
            6f))
    val output = Operator.conv1D(input, weight)
    val expectedData =
        floatArrayOf(168f, 122f, 263f, 232f, 133f, 111f, 291f, 253f, 47f, 123f, 208f, 196f)
    Assert.assertArrayEquals(expectedData, output.data, 0.0_001f)
  }

  @Test
  fun `test max pool 1D`() {
    val input = MTensor(intArrayOf(2, 2, 3))
    setTensorData(input, floatArrayOf(-1f, 2f, 3f, 4f, -5f, 6f, 7f, -8f, 9f, -10f, 11f, 12f))
    val output = Operator.maxPool1D(input, 2)
    val expectedData = floatArrayOf(4f, 2f, 6f, 7f, 11f, 12f)
    Assert.assertArrayEquals(expectedData, output.data, 0.0_001f)
  }
}


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

package com.facebook.appevents.ml

import java.io.File
import java.io.FileOutputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONArray
import org.json.JSONObject

object TensorTestUtils {
  fun setTensorData(x: MTensor, newData: FloatArray) {
    assertThat(x.data.size).isEqualTo(newData.size)
    val dataSize = x.data.size
    System.arraycopy(newData, 0, x.data, 0, dataSize)
  }

  fun createModelFile(model: Map<String, MTensor>, dir: File): File {
    val modelFile = File(dir, "testModelFile")
    val keys = model.keys.toList().sorted()

    modelFile.createNewFile()
    val modelStream = FileOutputStream(modelFile)
    val metadata = JSONObject()
    for (key in keys) {
      val weight = checkNotNull(model[key])
      val shape = JSONArray()
      for (s in 0 until weight.shapeSize) {
        shape.put(weight.getShape(s))
      }
      metadata.put(key, shape)
    }
    val metadataString = metadata.toString()

    val metadataStringLengthBuffer = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
    metadataStringLengthBuffer.putInt(metadataString.length)

    modelStream.write(metadataStringLengthBuffer.array())
    modelStream.write(metadataString.toByteArray())
    for (key in keys) {
      val tensor = model[key] ?: continue
      val dataBuffer = ByteBuffer.allocate(tensor.data.size * 4).order(ByteOrder.LITTLE_ENDIAN)
      dataBuffer.asFloatBuffer().put(tensor.data)
      modelStream.write(dataBuffer.array())
    }
    modelStream.close()
    return modelFile
  }

  fun createTestTensor(shape: IntArray): MTensor {
    val tensor = MTensor(shape)
    for (idx in tensor.data.indices) {
      tensor.data[idx] = idx.toFloat()
    }
    return tensor
  }
}


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

package com.facebook.appevents.ondeviceprocessing

import android.content.Context
import android.os.Bundle
import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.appevents.AppEvent
import com.facebook.appevents.AppEventsConstants
import com.facebook.appevents.ondeviceprocessing.OnDeviceProcessingManager.isOnDeviceProcessingEnabled
import com.facebook.appevents.ondeviceprocessing.OnDeviceProcessingManager.sendCustomEventAsync
import com.facebook.appevents.ondeviceprocessing.OnDeviceProcessingManager.sendInstallEventAsync
import com.facebook.appevents.ondeviceprocessing.RemoteServiceWrapper.ServiceResult
import com.facebook.appevents.ondeviceprocessing.RemoteServiceWrapper.isServiceAvailable
import com.facebook.appevents.ondeviceprocessing.RemoteServiceWrapper.sendCustomEvents
import com.facebook.appevents.ondeviceprocessing.RemoteServiceWrapper.sendInstallEvent
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.KArgumentCaptor
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class, RemoteServiceWrapper::class)
class OnDeviceProcessingManagerTest : FacebookPowerMockTestCase() {
  private val applicationId = "app_id"
  private lateinit var context: Context

  @Before
  fun setUp() {
    context = ApplicationProvider.getApplicationContext()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.getApplicationContext()).thenReturn(context)
    whenever(FacebookSdk.getExecutor()).thenCallRealMethod()
    PowerMockito.mockStatic(RemoteServiceWrapper::class.java)
  }

  @Test
  fun testIsOnDeviceProcessingEnabled() {
    setupPreconditions(true, true)
    assertThat(isOnDeviceProcessingEnabled()).isTrue()
    setupPreconditions(false, true)
    assertThat(isOnDeviceProcessingEnabled()).isFalse()
    setupPreconditions(true, false)
    assertThat(isOnDeviceProcessingEnabled()).isFalse()
    setupPreconditions(false, false)
    assertThat(isOnDeviceProcessingEnabled()).isFalse()
  }

  @Test
  fun testSendCustomEventAsync_AllowedEvents() {
    // Arrange
    val latch = CountDownLatch(4)
    val captor = setupSendCustomEventsArgumentCaptor(latch)

    // Act
    sendCustomEventAsync(applicationId, createEvent("explicit_event", false))
    sendCustomEventAsync(applicationId, createEvent(AppEventsConstants.EVENT_NAME_PURCHASED, true))
    sendCustomEventAsync(applicationId, createEvent(AppEventsConstants.EVENT_NAME_SUBSCRIBE, true))
    sendCustomEventAsync(
        applicationId, createEvent(AppEventsConstants.EVENT_NAME_START_TRIAL, true))
    latch.await(6, TimeUnit.SECONDS)
    // Assert : RemoteServiceWrapper.sendCustomEvents(...) was invoked 4 times
    assertThat(captor.allValues.size).isEqualTo(4)
  }

  @Test
  fun testSendCustomEventAsync_NotAllowedEvents() {
    // Arrange
    val latch = CountDownLatch(1)
    val captor = setupSendCustomEventsArgumentCaptor(latch)

    // Act
    sendCustomEventAsync(applicationId, createEvent("other_implicit_event", true))
    latch.await(1, TimeUnit.SECONDS)

    // Assert : RemoteServiceWrapper.sendCustomEvents(...) never invoked
    assertThat(captor.allValues.size).isEqualTo(0)
  }

  @Test
  fun testSendInstallEventAsync_NonNullArguments() {
    // Arrange
    val latch = CountDownLatch(1)
    val captor = setupSendInstallEventArgumentCaptor(latch)

    // Act
    sendInstallEventAsync(applicationId, "preferences_name")
    latch.await(7, TimeUnit.SECONDS)

    // Assert : RemoteServiceWrapper.sendInstallEvent(...) invoked once
    assertThat(captor.allValues.size).isEqualTo(1)
  }

  @Test
  fun testSendInstallEventAsync_NullArguments() {
    // Arrange
    val latch = CountDownLatch(1)
    val captor = setupSendInstallEventArgumentCaptor(latch)

    // Act
    sendInstallEventAsync(null, null)
    sendInstallEventAsync(null, "preferences_name")
    sendInstallEventAsync(applicationId, null)
    val completed = latch.await(3, TimeUnit.SECONDS)

    // Assert : RemoteServiceWrapper.sendInstallEvent(...) never invoked
    assertThat(captor.allValues.size).isEqualTo(0)
    assertThat(completed).isFalse
  }

  private fun createEvent(eventName: String, isImplicitlyLogged: Boolean): AppEvent {
    return AppEvent("context_name", eventName, 0.0, Bundle(), isImplicitlyLogged, false, null)
  }

  private fun setupPreconditions(
      isApplicationTrackingEnabled: Boolean,
      isServiceAvailable: Boolean
  ) {
    whenever(FacebookSdk.getLimitEventAndDataUsage(context))
        .thenReturn(!isApplicationTrackingEnabled)
    whenever(isServiceAvailable()).thenReturn(isServiceAvailable)
  }

  private fun setupSendCustomEventsArgumentCaptor(
      latch: CountDownLatch
  ): KArgumentCaptor<List<AppEvent>> {
    val captor = argumentCaptor<List<AppEvent>>()
    whenever(sendCustomEvents(any(), captor.capture())).thenAnswer {
      latch.countDown()
      ServiceResult.OPERATION_SUCCESS
    }
    return captor
  }

  private fun setupSendInstallEventArgumentCaptor(latch: CountDownLatch): KArgumentCaptor<String> {
    val captor = argumentCaptor<String>()
    whenever(sendInstallEvent(captor.capture())).thenAnswer {
      latch.countDown()
      ServiceResult.OPERATION_SUCCESS
    }
    return captor
  }
}


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

package com.facebook.appevents.ondeviceprocessing

import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.appevents.AppEvent
import com.facebook.appevents.eventdeactivation.EventDeactivationManager
import com.facebook.appevents.ondeviceprocessing.RemoteServiceParametersHelper.buildEventsBundle
import com.facebook.internal.FetchedAppSettings
import com.facebook.internal.FetchedAppSettingsManager
import com.facebook.internal.FetchedAppSettingsManager.queryAppSettings
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(FacebookSdk::class, FetchedAppSettingsManager::class)
class RemoteServiceParametersHelperTest : FacebookPowerMockTestCase() {
  private val applicationId = "mock_app_id"
  private val implicitEvent: AppEvent =
      AppEvent("context_name", "implicit_event", 0.0, null, true, false, null)
  private val explicitEvent: AppEvent =
      AppEvent("context_name", "explicit_event", 0.0, null, false, false, null)
  private val deprecatedEvent: AppEvent =
      AppEvent("context_name", "deprecated_event", 0.0, null, false, false, null)
  private val invalidChecksumEvent: AppEvent =
      AppEvent("context_name", "invalid_checksum_event", 0.0, null, false, false, null)
  private lateinit var context: Context

  @Before
  fun setUp() {
    Whitebox.setInternalState(invalidChecksumEvent, "checksum", "invalid_checksum")
    context = ApplicationProvider.getApplicationContext()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.getApplicationContext()).thenReturn(context)
    PowerMockito.mockStatic(FetchedAppSettingsManager::class.java)
  }

  @Test
  fun testBuildEventsBundle_FiltersEvents() {
    // Arrange
    Whitebox.setInternalState(EventDeactivationManager::class.java, "enabled", true)
    Whitebox.setInternalState(
        EventDeactivationManager::class.java,
        "deprecatedEvents",
        HashSet(listOf(deprecatedEvent.name)))
    val mockAppSettings: FetchedAppSettings = mock()
    whenever(mockAppSettings.supportsImplicitLogging()).thenReturn(false)
    PowerMockito.mockStatic(FetchedAppSettingsManager::class.java)
    whenever(queryAppSettings(any(), any())).thenReturn(mockAppSettings)
    val appEvents = listOf(implicitEvent, deprecatedEvent, explicitEvent, invalidChecksumEvent)
    val eventType = RemoteServiceWrapper.EventType.CUSTOM_APP_EVENTS

    // Act
    val eventsBundle = buildEventsBundle(eventType, applicationId, appEvents)

    // Assert
    assertThat(eventsBundle?.getString("event")).isEqualTo(eventType.toString())
    assertThat(eventsBundle?.getString("app_id")).isEqualTo(applicationId)
    val expectedEventsJson = String.format("[%s]", explicitEvent.jsonObject.toString())
    assertThat(eventsBundle?.getString("custom_events")).isEqualTo(expectedEventsJson)
  }

  @Test
  fun testBuildEventsBundle_ReturnNull() {
    // Arrange
    val appEvents = listOf(implicitEvent)

    // Act
    val eventsBundle =
        buildEventsBundle(
            RemoteServiceWrapper.EventType.CUSTOM_APP_EVENTS, applicationId, appEvents)

    // Assert
    assertThat(eventsBundle).isNull()
  }
}