/*
 * 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.content.ServiceConnection
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.os.Bundle
import android.os.IBinder
import android.os.RemoteException
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.appevents.AppEvent
import com.facebook.appevents.internal.AppEventUtility
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 com.facebook.internal.FacebookSignatureValidator
import com.facebook.internal.FacebookSignatureValidator.validateSignature
import com.facebook.internal.FetchedAppSettings
import com.facebook.internal.FetchedAppSettingsManager
import com.facebook.internal.FetchedAppSettingsManager.queryAppSettings
import com.facebook.ppml.receiver.IReceiverService
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.kotlin.any
import org.mockito.kotlin.doThrow
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(
    FacebookSdk::class,
    FacebookSignatureValidator::class,
    IReceiverService.Stub::class,
    FetchedAppSettingsManager::class,
    AppEventUtility::class)
class RemoteServiceWrapperTest : FacebookPowerMockTestCase() {
  private val applicationId = "app_id"
  private val appEvents: List<AppEvent> =
      listOf(AppEvent("context_name", "test_event", 0.0, null, false, false, null))
  private lateinit var mockContext: Context

  override fun setup() {
    super.setup()
    mockContext = mock()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockContext)

    // Disable AppEventUtility.isMainThread since executor now runs in main thread
    PowerMockito.spy(AppEventUtility::class.java)
    PowerMockito.doReturn(false).`when`(AppEventUtility::class.java, "isMainThread")

    // Reset internal state
    val value: Boolean? = null
    Whitebox.setInternalState(RemoteServiceWrapper::class.java, "isServiceAvailable", value)
  }

  @Test
  fun testSendCustomEvents_RemoteServiceNotAvailable() {
    // Arrange
    val mockRemoteService = mockRemoteService(null, false, false, true)

    // Act
    val serviceResult = sendCustomEvents(applicationId, appEvents)

    // Assert
    assertThat(serviceResult).isEqualTo(ServiceResult.SERVICE_NOT_AVAILABLE)
    verify(mockRemoteService, never()).sendEvents(any())
  }

  @Test
  fun testSendCustomEvents_RemoteServiceAvailableButSignatureMismatch() {
    // Arrange
    val mockRemoteService = mockRemoteService(mock(), false, false, true)

    // Act
    val serviceResult = sendCustomEvents(applicationId, appEvents)

    // Assert
    assertThat(serviceResult).isEqualTo(ServiceResult.SERVICE_NOT_AVAILABLE)
    verify(mockRemoteService, never()).sendEvents(any())
  }

  @Test
  fun testSendCustomEvents_RemoteServiceAvailableButFailedToBind() {
    // Arrange
    val mockRemoteService = mockRemoteService(mock(), true, false, true)

    // Act
    val serviceResult = sendCustomEvents(applicationId, appEvents)

    // Assert
    assertThat(serviceResult).isEqualTo(ServiceResult.SERVICE_ERROR)
    verify(mockRemoteService, never()).sendEvents(any())
  }

  @Test
  fun testSendCustomEvents_RemoteServiceAvailableButBinderIsNull() {
    // Arrange
    val mockRemoteService = mockRemoteService(mock(), true, true, true)

    // Act
    val serviceResult = sendCustomEvents(applicationId, appEvents)

    // Assert
    assertThat(serviceResult).isEqualTo(ServiceResult.SERVICE_NOT_AVAILABLE)
    verify(mockRemoteService, never()).sendEvents(any())
  }

  @Test
  fun testSendCustomEvents_RemoteServiceAvailableButThrowsException() {
    // Arrange
    val mockRemoteService = mockRemoteService(mock(), true, true, false)
    doThrow(RemoteException::class).`when`(mockRemoteService).sendEvents(any())

    // Act
    val serviceResult = sendCustomEvents(applicationId, appEvents)

    // Assert
    assertThat(serviceResult).isEqualTo(ServiceResult.SERVICE_ERROR)
    verify(mockRemoteService).sendEvents(any())
  }

  @Test
  fun testSendCustomEvents_RemoteServiceAvailable() {
    // Arrange
    val mockRemoteService = mockRemoteService(mock(), true, true, false)

    // Act
    val serviceResult = sendCustomEvents(applicationId, appEvents)

    // Assert
    assertThat(serviceResult).isEqualTo(ServiceResult.OPERATION_SUCCESS)
    val captor = ArgumentCaptor.forClass(Bundle::class.java)
    verify(mockRemoteService).sendEvents(captor.capture())
    assertThat(
            captor.value.getString("event"),
        )
        .isEqualTo(RemoteServiceWrapper.EventType.CUSTOM_APP_EVENTS.toString())
  }

  @Test
  fun testSendInstallEvent_RemoteServiceAvailable() {
    // Arrange
    val mockRemoteService = mockRemoteService(mock(), true, true, false)

    // Act
    val serviceResult = sendInstallEvent(applicationId)

    // Assert
    assertThat(serviceResult).isEqualTo(ServiceResult.OPERATION_SUCCESS)
    val captor = ArgumentCaptor.forClass(Bundle::class.java)
    verify(mockRemoteService).sendEvents(captor.capture())
    assertThat(captor.value.getString("event"))
        .isEqualTo(RemoteServiceWrapper.EventType.MOBILE_APP_INSTALL.toString())
    assertThat(captor.value.getString("custom_events")).isNull()
  }

  @Test
  fun testIsServiceAvailable_RemoteServiceNotAvailable() {
    // Arrange
    mockRemoteService(null, false, false, true)

    // Act
    val serviceAvailable = isServiceAvailable()

    // Assert
    assertThat(serviceAvailable).isFalse()
  }

  @Test
  fun testIsServiceAvailable_RemoteServiceAvailable() {
    // Arrange
    mockRemoteService(mock(), true, false, true)

    // Act
    val serviceAvailable = isServiceAvailable()

    // Assert
    assertThat(serviceAvailable).isTrue()
  }

  private fun mockRemoteService(
      serviceResolveInfo: ResolveInfo?,
      isSignatureValid: Boolean,
      isServiceBindSuccessful: Boolean,
      isBinderNull: Boolean
  ): IReceiverService {
    // Mock PackageManager
    val mockPackageManager: PackageManager = mock()
    whenever(mockPackageManager.resolveService(any(), any())).thenReturn(serviceResolveInfo)
    whenever(mockContext.packageManager).thenReturn(mockPackageManager)

    // Mock FacebookSignatureValidator
    PowerMockito.mockStatic(FacebookSignatureValidator::class.java)
    whenever(validateSignature(any(), any())).thenReturn(isSignatureValid)

    // Mock FetchedAppSettings
    val mockAppSettings: FetchedAppSettings = mock()
    whenever(mockAppSettings.supportsImplicitLogging()).thenReturn(false)
    PowerMockito.mockStatic(FetchedAppSettingsManager::class.java)
    whenever(queryAppSettings(any(), any())).thenReturn(mockAppSettings)

    // Mock remote service binder
    val mockBinder: IBinder? = if (isBinderNull) null else mock()
    // Mock Context.bindService
    whenever(mockContext.bindService(any(), any(), any())).thenAnswer {
      val connection = it.arguments[1] as ServiceConnection
      if (!isBinderNull) {
        connection.onServiceConnected(mock(), mockBinder)
      }
      return@thenAnswer isServiceBindSuccessful
    }

    val mockRemoteService: IReceiverService = mock()
    PowerMockito.mockStatic(IReceiverService.Stub::class.java)
    whenever(IReceiverService.Stub.asInterface(mockBinder)).thenReturn(mockRemoteService)
    return mockRemoteService
  }
}


/*
 * 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.restrictivedatafilter

import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.appevents.restrictivedatafilter.RestrictiveDataManager.RestrictiveParamFilter
import com.facebook.appevents.restrictivedatafilter.RestrictiveDataManager.enable
import com.facebook.appevents.restrictivedatafilter.RestrictiveDataManager.processEvent
import com.facebook.appevents.restrictivedatafilter.RestrictiveDataManager.processParameters
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.json.JSONObject
import org.junit.Assert
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, FetchedAppSettings::class, FetchedAppSettingsManager::class)
class RestrictiveDataManagerTest : FacebookPowerMockTestCase() {

  companion object {
    private const val MOCK_APP_ID = "123"
    private val eventParam =
        mutableMapOf("key1" to "val1", "key2" to "val2", "last_name" to "ln", "first_name" to "fn")
  }

  @Before
  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.getApplicationId()).thenReturn(MOCK_APP_ID)
    Whitebox.setInternalState(RestrictiveDataManager::class.java, "enabled", true)
  }

  @Test
  fun testEnable() {
    val expectedParam = hashMapOf("last_name" to "0", "first_name" to "0")
    val expectedParamDetail =
        hashMapOf("Quantity" to "1", "Product name" to "Coffee", "Price" to "10")
    val jsonObject = JSONObject()

    val jsonObject1 = JSONObject()
    jsonObject1.put("restrictive_param", JSONObject(expectedParam as Map<*, *>))
    val jsonObject2 = JSONObject()
    jsonObject2.put("restrictive_param", JSONObject(expectedParamDetail as Map<*, *>))
    jsonObject2.put("process_event_name", false)
    jsonObject.put("fb_test_event", jsonObject1)
    jsonObject.put("manual_initiated_checkout", jsonObject2)

    val mockResponse = jsonObject.toString()
    val fetchedAppSettings: FetchedAppSettings = mock()
    whenever(fetchedAppSettings.restrictiveDataSetting).thenReturn(mockResponse)
    PowerMockito.mockStatic(FetchedAppSettingsManager::class.java)
    whenever(queryAppSettings(any(), any())).thenReturn(fetchedAppSettings)
    val restrictiveParamFilters =
        Whitebox.getInternalState<MutableList<RestrictiveParamFilter>>(
            RestrictiveDataManager::class.java, "restrictiveParamFilters")
    restrictiveParamFilters.clear()
    val restrictedEvents =
        Whitebox.getInternalState<MutableSet<String>>(
            RestrictiveDataManager::class.java, "restrictedEvents")
    restrictedEvents.clear()
    enable()
    Assert.assertEquals(2, restrictiveParamFilters.size.toLong())
    val rule = restrictiveParamFilters[0]
    Assert.assertEquals("fb_test_event", rule.eventName)
    Assert.assertEquals(expectedParam, rule.restrictiveParams)
    Assert.assertEquals(1, restrictedEvents.size.toLong())
    assertThat(restrictedEvents).contains("manual_initiated_checkout")
  }

  @Test
  fun testProcessParameters() {
    val mockRestrictiveParams = arrayListOf<RestrictiveParamFilter>()
    val mockParam = hashMapOf("last_name" to "0", "first_name" to "1")
    mockRestrictiveParams.add(RestrictiveParamFilter("fb_restrictive_event", mockParam))
    Whitebox.setInternalState(
        RestrictiveDataManager::class.java, "restrictiveParamFilters", mockRestrictiveParams)
    var mockEventParam = eventParam as MutableMap<String, String?>
    processParameters(mockEventParam, "fb_test_event")
    Assert.assertEquals(eventParam, mockEventParam)
    mockEventParam = eventParam
    processParameters(mockEventParam, "fb_restrictive_event")
    assertThat(mockEventParam.containsKey("key1")).isTrue
    assertThat(mockEventParam.containsKey("key2")).isTrue
    assertThat(mockEventParam.containsKey("_restrictedParams")).isTrue
    assertThat(mockEventParam.containsKey("last_name")).isFalse
    assertThat(mockEventParam.containsKey("first_name")).isFalse
  }

  @Test
  fun testProcessEvent() {
    val input = "name_should_be_replaced"
    Whitebox.setInternalState(
        RestrictiveDataManager::class.java, "restrictedEvents", hashSetOf(input))
    val output = processEvent(input)
    Assert.assertEquals(output, "_removed_")
  }
}


/*
 * 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.suggestedevents

import com.facebook.FacebookPowerMockTestCase
import com.facebook.appevents.internal.ViewHierarchyConstants.CHILDREN_VIEW_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.CLASS_NAME_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.CLASS_TYPE_BITMASK_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.HINT_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.IMAGEVIEW_BITMASK
import com.facebook.appevents.internal.ViewHierarchyConstants.IS_INTERACTED_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.SCREEN_NAME_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.TEXT_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.VIEW_KEY
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import java.util.UUID
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONArray
import org.json.JSONObject
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.powermock.reflect.Whitebox

class FeatureExtractorTest : FacebookPowerMockTestCase() {
  private lateinit var dir: File
  private lateinit var file: File

  @Before
  fun init() {
    val dirName = UUID.randomUUID().toString()
    dir = File(dirName)
    dir.mkdir()

    file = File(dir, "jsonFile")
    val output = BufferedWriter(FileWriter(file))
    output.write(buildRules().toString())
    output.close()
  }

  @After
  fun cleanTestDirectory() {
    dir.deleteRecursively()
  }

  @Test
  fun testInitialize() {
    Whitebox.setInternalState(FeatureExtractor::class.java, "initialized", false)
    FeatureExtractor.initialize(file)
    assertThat(FeatureExtractor.isInitialized()).isTrue
    val rules = Whitebox.getInternalState<JSONObject>(FeatureExtractor::class.java, "rules")
    val languageInfo =
        Whitebox.getInternalState<Map<String, String>>(FeatureExtractor::class.java, "languageInfo")
    val eventInfo =
        Whitebox.getInternalState<Map<String, String>>(FeatureExtractor::class.java, "eventInfo")
    val textTypeInfo =
        Whitebox.getInternalState<Map<String, String>>(FeatureExtractor::class.java, "textTypeInfo")
    assertThat(
            rules
                .optJSONObject("rulesForLanguage")
                .optJSONObject("1")
                .optJSONObject("rulesForEvent")
                .optJSONObject("0")
                .optJSONObject("positiveRules")
                .optString("1"))
        .isEqualTo("text")
    assertThat(languageInfo).isNotEmpty
    assertThat(eventInfo).isNotEmpty
    assertThat(textTypeInfo).isNotEmpty
  }

  @Test
  fun testGetDenseFeatures() {
    Whitebox.setInternalState(FeatureExtractor::class.java, "initialized", false)
    FeatureExtractor.initialize(file)

    val viewHierarchy = JSONObject()
    val viewTree = JSONObject()
    viewTree.put(IS_INTERACTED_KEY, true)
    viewTree.put(CLASS_NAME_KEY, "ImageView")
    viewTree.put(CLASS_TYPE_BITMASK_KEY, 1 shl IMAGEVIEW_BITMASK)
    viewTree.put(TEXT_KEY, "text")
    viewTree.put(HINT_KEY, "hint")
    viewTree.put(CHILDREN_VIEW_KEY, JSONArray())

    viewHierarchy.put(VIEW_KEY, viewTree)
    viewHierarchy.put(SCREEN_NAME_KEY, "activity")
    val ret = FeatureExtractor.getDenseFeatures(viewHierarchy, "HackBook")
    val expectedFeatures = FloatArray(30) { 0F }
    expectedFeatures[13] = -1f
    expectedFeatures[14] = -1f
    expectedFeatures[15] = 1f
    expectedFeatures[22] = 1f
    expectedFeatures[28] = 1f
    assertThat(ret).isEqualTo(expectedFeatures)
  }

  private fun buildRules(): JSONObject {
    val languageMap =
        mutableMapOf<String, Map<String, Map<String, Map<String, Map<String, String>>>>>()
    for (language in 1..4) {
      val eventMap = mutableMapOf<String, Map<String, Map<String, String>>>()
      for (event in 0..8) {
        val textMap = mutableMapOf<String, String>()
        for (text in 1..4) {
          textMap[text.toString()] = "text"
        }
        eventMap[event.toString()] = mapOf("positiveRules" to textMap.toMap())
      }

      languageMap[language.toString()] = mapOf("rulesForEvent" to eventMap.toMap())
    }
    return JSONObject(mapOf("rulesForLanguage" to languageMap))
  }
}


/*
 * 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.suggestedevents

import android.content.Context
import android.view.ViewGroup
import android.widget.EditText
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.MockSharedPreference
import com.facebook.appevents.codeless.internal.ViewHierarchy
import com.facebook.appevents.internal.ViewHierarchyConstants.CLASS_NAME_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.TEXT_KEY
import com.facebook.internal.Utility
import java.util.concurrent.atomic.AtomicBoolean
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONArray
import org.json.JSONObject
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, Utility::class, ViewHierarchy::class)
class PredictionHistoryManagerTest : FacebookPowerMockTestCase() {
  companion object {
    private const val PATH_ID = "path123"
    private const val PATH_ID_NOT_ADDED = "path345"
    private const val EVENT_NAME = "some event"
    private const val TEXT = "view text"
  }
  private lateinit var mockContext: Context
  private lateinit var mockSharedPreferences: MockSharedPreference
  private lateinit var mockView: EditText
  private lateinit var mockParentView: ViewGroup
  @Before
  fun init() {
    mockContext = mock()
    mockView = mock()
    mockParentView = mock()
    mockSharedPreferences = MockSharedPreference()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    PowerMockito.mockStatic(Utility::class.java)
    PowerMockito.mockStatic(ViewHierarchy::class.java)
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockContext)
    PowerMockito.`when`(
            mockContext.getSharedPreferences(
                "com.facebook.internal.SUGGESTED_EVENTS_HISTORY", Context.MODE_PRIVATE))
        .thenReturn(mockSharedPreferences)
  }

  @Test
  fun testAddPrediction() {
    Whitebox.setInternalState(
        PredictionHistoryManager::class.java, "initialized", AtomicBoolean(false))
    PredictionHistoryManager.addPrediction(PATH_ID, EVENT_NAME)

    val initialized =
        Whitebox.getInternalState<AtomicBoolean>(
            PredictionHistoryManager::class.java, "initialized")
    assertThat(initialized.get()).isTrue

    assertThat(PredictionHistoryManager.queryEvent(PATH_ID)).isEqualTo(EVENT_NAME)
    assertThat(PredictionHistoryManager.queryEvent(PATH_ID_NOT_ADDED)).isNull()
  }

  @Test
  fun testGetPathID() {
    var pathRouteStr: String? = null
    val expectedPathRoute =
        JSONObject(
            mapOf(
                TEXT_KEY to TEXT,
                CLASS_NAME_KEY to
                    JSONArray(
                        listOf(
                            mockView.javaClass.simpleName, mockParentView.javaClass.simpleName))))
    whenever(Utility.sha256hash(any<String>())).thenAnswer {
      pathRouteStr = it.arguments[0] as String
      ""
    }

    whenever(ViewHierarchy.getParentOfView(mockView)).thenReturn(mockParentView)

    PredictionHistoryManager.getPathID(mockView, TEXT)
    assertThat(pathRouteStr).isEqualTo(expectedPathRoute.toString())
  }
}


/*
 * 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.suggestedevents

import com.facebook.FacebookPowerMockTestCase
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.Before
import org.junit.Test
import org.powermock.reflect.Whitebox

class SuggestedEventsManagerTest : FacebookPowerMockTestCase() {
  private val validJson =
      "{\n" +
          "  \"production_events\": [a,b,c\n" +
          "  ],\n" +
          "  \"eligible_for_prediction_events\": [x,y,z\n" +
          "  ]" +
          "}"

  private val emptyProdEventOnlyJson = "{\n" + "  \"production_events\": [\n" + "  ],\n" + "}"

  private val emptyEligibleEventOnlyJson =
      "{\n" + "  \"eligible_for_prediction_events\": [\n" + "  ]" + "}"

  private val invalidJson =
      "{\n" +
          "  \"production_events\": \"a\"\n" +
          "  ],\n" +
          "  \"eligible_for_prediction_events\":  \"b\"\n" +
          "  ]" +
          "}"
  @Before
  fun init() {
    Whitebox.setInternalState(
        SuggestedEventsManager::class.java, "productionEvents", HashSet<String>())
    Whitebox.setInternalState(
        SuggestedEventsManager::class.java, "eligibleEvents", HashSet<String>())
  }
  @Test
  fun `parse valid json_ok`() {
    val test = JSONObject(validJson) // to verify its actually valid, will throw exception
    SuggestedEventsManager.populateEventsFromRawJsonString(validJson)
    assertThat(SuggestedEventsManager.isProductionEvents("a")).isTrue
    assertThat(SuggestedEventsManager.isProductionEvents("b")).isTrue
    assertThat(SuggestedEventsManager.isProductionEvents("c")).isTrue
    assertThat(SuggestedEventsManager.isProductionEvents("d")).isFalse
    assertThat(SuggestedEventsManager.isEligibleEvents("x")).isTrue
    assertThat(SuggestedEventsManager.isEligibleEvents("y")).isTrue
    assertThat(SuggestedEventsManager.isEligibleEvents("z")).isTrue
    assertThat(SuggestedEventsManager.isEligibleEvents("a")).isFalse
  }

  @Test
  fun `parse zero production events`() {
    SuggestedEventsManager.populateEventsFromRawJsonString(emptyProdEventOnlyJson)
    assertThat(SuggestedEventsManager.isProductionEvents("a")).isFalse
  }

  @Test
  fun `parse zero eligible events`() {
    SuggestedEventsManager.populateEventsFromRawJsonString(emptyEligibleEventOnlyJson)
    assertThat(SuggestedEventsManager.isProductionEvents("a")).isFalse
  }

  @Test
  fun `parse invalid json`() {
    SuggestedEventsManager.populateEventsFromRawJsonString(invalidJson)
    assertThat(SuggestedEventsManager.isProductionEvents("a")).isFalse
    assertThat(SuggestedEventsManager.isEligibleEvents("b")).isFalse
  }
}


/*
 * 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.suggestedevents

import android.app.Activity
import android.view.View
import android.widget.EditText
import android.widget.Switch
import com.facebook.FacebookPowerMockTestCase
import com.facebook.appevents.codeless.internal.ViewHierarchy
import com.facebook.appevents.internal.ViewHierarchyConstants.CHILDREN_VIEW_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.CLASS_NAME_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.CLASS_TYPE_BITMASK_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.HINT_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.INPUT_TYPE_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.IS_INTERACTED_KEY
import com.facebook.appevents.internal.ViewHierarchyConstants.TEXT_KEY
import java.util.Collections.emptyList
import org.json.JSONObject
import org.junit.Assert.assertEquals
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.robolectric.Robolectric

@PrepareForTest(ViewHierarchy::class)
class SuggestedEventViewHierarchyTest : FacebookPowerMockTestCase() {
  @Test
  fun `test get all clickable views`() {
    val context = Robolectric.buildActivity(Activity::class.java).get()

    val blacklistView = Switch(context)
    assertEquals(emptyList<View>(), SuggestedEventViewHierarchy.getAllClickableViews(blacklistView))

    val clickableView = View(context)
    val mockListener: View.OnClickListener = mock<View.OnClickListener>()
    clickableView.setOnClickListener(mockListener)
    assertEquals(
        listOf<View>(clickableView),
        SuggestedEventViewHierarchy.getAllClickableViews(clickableView))

    // Test with inner children views
    val view1 = View(context)
    val view2 = EditText(context)
    val childrenViews = listOf<View>(view1, view2)
    PowerMockito.mockStatic(ViewHierarchy::class.java)
    whenever(ViewHierarchy.getChildrenOfView(clickableView)).thenReturn(childrenViews)
    assertEquals(
        listOf<View>(clickableView),
        SuggestedEventViewHierarchy.getAllClickableViews(clickableView))

    // Test with inner children views, and one child is clickable view
    view1.setOnClickListener(mockListener)
    assertEquals(
        listOf<View>(clickableView, view1),
        SuggestedEventViewHierarchy.getAllClickableViews(clickableView))
  }

  @Test
  fun `test get dictionary Of view`() {
    val context = Robolectric.buildActivity(Activity::class.java).get()

    val view = View(context)
    val clickableView = View(context)
    val mockListener: View.OnClickListener = mock<View.OnClickListener>()
    clickableView.setOnClickListener(mockListener)

    val obj1 = SuggestedEventViewHierarchy.getDictionaryOfView(view, clickableView)
    assertEquals(
        "{\"${CLASS_NAME_KEY}\":\"View\",\"${CLASS_TYPE_BITMASK_KEY}\":0,\"${CHILDREN_VIEW_KEY}\":[]}",
        obj1.toString())

    val obj2 = SuggestedEventViewHierarchy.getDictionaryOfView(clickableView, clickableView)
    assertEquals(
        "{\"${IS_INTERACTED_KEY}\":true,\"${CLASS_NAME_KEY}\":\"View\",\"${CLASS_TYPE_BITMASK_KEY}\":32,\"${CHILDREN_VIEW_KEY}\":[]}",
        obj2.toString())

    // Test with inner children views
    PowerMockito.mockStatic(ViewHierarchy::class.java)
    whenever(ViewHierarchy.getChildrenOfView(view)).thenReturn(listOf<View>(clickableView))
    whenever(ViewHierarchy.getTextOfView(any())).thenReturn("")
    whenever(ViewHierarchy.getHintOfView(any())).thenReturn("")
    val obj3 = SuggestedEventViewHierarchy.getDictionaryOfView(view, clickableView)
    assertEquals(
        "{\"${CLASS_NAME_KEY}\":\"View\",\"${CLASS_TYPE_BITMASK_KEY}\":0,\"${CHILDREN_VIEW_KEY}\":[{\"${IS_INTERACTED_KEY}\":true,\"${CLASS_NAME_KEY}\":\"View\",\"${CLASS_TYPE_BITMASK_KEY}\":0,\"${CHILDREN_VIEW_KEY}\":[]}]}",
        obj3.toString())
  }

  @Test
  fun `test update basic info`() {
    val context = Robolectric.buildActivity(Activity::class.java).get()

    val view = View(context)
    val json = JSONObject()
    SuggestedEventViewHierarchy.updateBasicInfo(view, json)
    assertEquals(
        "{\"${CLASS_NAME_KEY}\":\"View\",\"${CLASS_TYPE_BITMASK_KEY}\":0}", json.toString())

    PowerMockito.mockStatic(ViewHierarchy::class.java)
    whenever(ViewHierarchy.getTextOfView(any())).thenReturn("Some Text")
    whenever(ViewHierarchy.getHintOfView(any())).thenReturn("Some Hint")
    whenever(ViewHierarchy.getClassTypeBitmask(any())).thenReturn(100)

    val viewWithText = View(context)
    val jsonWithText = JSONObject()
    SuggestedEventViewHierarchy.updateBasicInfo(viewWithText, jsonWithText)
    assertEquals(
        "{\"${CLASS_NAME_KEY}\":\"View\",\"${CLASS_TYPE_BITMASK_KEY}\":100,\"${TEXT_KEY}\":\"Some Text\",\"${HINT_KEY}\":\"Some Hint\"}",
        jsonWithText.toString())

    val editText = EditText(context)
    val editTextJson = JSONObject()
    SuggestedEventViewHierarchy.updateBasicInfo(editText, editTextJson)
    assertEquals(
        "{\"${CLASS_NAME_KEY}\":\"EditText\",\"${CLASS_TYPE_BITMASK_KEY}\":100,\"${TEXT_KEY}\":\"Some Text\",\"${HINT_KEY}\":\"Some Hint\",\"${INPUT_TYPE_KEY}\":131073}",
        editTextJson.toString())
  }

  @Test
  fun `test get text Of view recursively`() {
    val context = Robolectric.buildActivity(Activity::class.java).get()
    PowerMockito.mockStatic(ViewHierarchy::class.java)

    val blankView = View(context)
    whenever(ViewHierarchy.getTextOfView(blankView)).thenReturn("")
    assertEquals("", SuggestedEventViewHierarchy.getTextOfViewRecursively(blankView))

    val viewWithText = View(context)
    whenever(ViewHierarchy.getTextOfView(viewWithText)).thenReturn("Some Text")
    assertEquals("Some Text", SuggestedEventViewHierarchy.getTextOfViewRecursively(viewWithText))

    whenever(ViewHierarchy.getChildrenOfView(blankView)).thenReturn(listOf<View>(viewWithText))
    assertEquals("Some Text", SuggestedEventViewHierarchy.getTextOfViewRecursively(blankView))
  }
}


/*
 * 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.suggestedevents

import android.app.Activity
import android.view.View
import android.view.ViewTreeObserver
import com.facebook.FacebookPowerMockTestCase
import com.facebook.appevents.internal.AppEventUtility
import java.util.concurrent.atomic.AtomicBoolean
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.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(AppEventUtility::class)
class ViewObserverTest : FacebookPowerMockTestCase() {
  private lateinit var mockActivity: Activity
  private lateinit var mockView: View
  private lateinit var mockViewTreeObserver: ViewTreeObserver

  @Before
  fun init() {
    mockActivity = mock()
    mockView = mock()
    mockViewTreeObserver = mock()

    PowerMockito.mockStatic(AppEventUtility::class.java)
    whenever(AppEventUtility.getRootView(any())).thenReturn(mockView)
    whenever(mockView.viewTreeObserver).thenReturn(mockViewTreeObserver)
    whenever(mockViewTreeObserver.isAlive).thenReturn(true)
  }

  @Test
  fun testStartTrackingActivity() {
    ViewObserver.startTrackingActivity(mockActivity)
    val observers =
        Whitebox.getInternalState<Map<Int, ViewObserver>>(ViewObserver::class.java, "observers")
    checkNotNull(observers)
    assertThat(observers).isNotEmpty
    val observer: ViewObserver? = observers[mockActivity.hashCode()]
    val isTracking = Whitebox.getInternalState<AtomicBoolean>(observer, "isTracking")
    assertThat(isTracking.get()).isTrue
    verify(mockViewTreeObserver).addOnGlobalLayoutListener(any())
  }

  @Test
  fun testStopTrackingActivity() {
    ViewObserver.startTrackingActivity(mockActivity)
    val observers =
        Whitebox.getInternalState<Map<Int, ViewObserver>>(ViewObserver::class.java, "observers")
    checkNotNull(observers)
    val observer: ViewObserver? = observers[mockActivity.hashCode()]

    ViewObserver.stopTrackingActivity(mockActivity)

    assertThat(observers).isEmpty()
    val isTracking = Whitebox.getInternalState<AtomicBoolean>(observer, "isTracking")
    assertThat(isTracking.get()).isFalse
    verify(mockViewTreeObserver).removeOnGlobalLayoutListener(any())
  }
}


/*
 * 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.suggestedevents

import android.os.Bundle
import android.view.View
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.GraphRequest
import com.facebook.appevents.codeless.internal.ViewHierarchy
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.eq
import org.mockito.kotlin.isNull
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,
    ViewHierarchy::class,
    PredictionHistoryManager::class,
    SuggestedEventViewHierarchy::class,
    SuggestedEventsManager::class)
class ViewOnClickListenerTest : FacebookPowerMockTestCase() {
  private val mockExecutor: Executor = FacebookSerialExecutor()
  private lateinit var mockGraphRequest: GraphRequest
  private lateinit var mockGraphRequestCompanion: GraphRequest.Companion
  private lateinit var param: Bundle
  private lateinit var mockHostView: View
  private lateinit var mockRootView: View
  private lateinit var mockViewListener: View.OnClickListener
  private var setListenerTimes = 0
  private var getListenerTimes = 0
  private lateinit var viewOnClickListener: ViewOnClickListener

  companion object {
    private const val APP_ID = "123"
    private const val EVENT_NAME = "some event"
    private const val PATH_ID = "path_id_123"
    private const val VIEW_TEXT = "view text"
  }
  @Before
  fun init() {
    mockGraphRequest = mock()
    mockGraphRequestCompanion = mock()
    mockHostView = mock()
    mockRootView = mock()
    mockViewListener = mock()

    PowerMockito.mockStatic(FacebookSdk::class.java)
    PowerMockito.mockStatic(ViewHierarchy::class.java)
    PowerMockito.mockStatic(PredictionHistoryManager::class.java)
    PowerMockito.mockStatic(SuggestedEventViewHierarchy::class.java)
    PowerMockito.mockStatic(SuggestedEventsManager::class.java)

    whenever(FacebookSdk.getExecutor()).thenReturn(mockExecutor)
    whenever(FacebookSdk.getApplicationId()).thenReturn(APP_ID)
    whenever(PredictionHistoryManager.queryEvent(any())).thenReturn(EVENT_NAME)
    whenever(PredictionHistoryManager.getPathID(any(), any())).thenReturn(PATH_ID)
    whenever(SuggestedEventViewHierarchy.getTextOfViewRecursively(any())).thenReturn(VIEW_TEXT)
    whenever(SuggestedEventsManager.isProductionEvents(any())).thenReturn(false)
    whenever(SuggestedEventsManager.isEligibleEvents(any())).thenReturn(true)

    Whitebox.setInternalState(GraphRequest::class.java, "Companion", mockGraphRequestCompanion)
    PowerMockito.`when`(
            mockGraphRequestCompanion.newPostRequest(isNull(), any(), isNull(), isNull()))
        .thenReturn(mockGraphRequest)
    whenever(mockGraphRequest::parameters.set(any())).thenAnswer {
      param = it.arguments[0] as Bundle
      Unit
    }

    whenever(ViewHierarchy.setOnClickListener(eq(mockHostView), any())).thenAnswer {
      viewOnClickListener = it.arguments[1] as ViewOnClickListener
      setListenerTimes++
      Unit
    }

    whenever(ViewHierarchy.getExistingOnClickListener(eq(mockHostView))).thenAnswer {
      getListenerTimes++
      mockViewListener
    }
  }
  @Test
  fun testAttachAndClick() {
    ViewOnClickListener.attachListener(mockHostView, mockRootView, "activity_name")

    assertThat(setListenerTimes).isEqualTo(1)
    assertThat(getListenerTimes).isEqualTo(1)
    assertThat(viewOnClickListener).isNotNull

    val mockView: View = mock()
    viewOnClickListener?.onClick(mockView)
    verify(mockViewListener).onClick(mockView)
    verify(mockGraphRequest).executeAndWait()
    assertThat(param).isNotNull
    assertThat(param?.get("event_name")).isEqualTo(EVENT_NAME)
    assertThat(param?.get("metadata"))
        .isEqualTo(JSONObject(mapOf("dense" to "", "button_text" to VIEW_TEXT)).toString())
  }
}


/*
 * 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.internal

import android.content.ContentResolver
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ProviderInfo
import android.os.Looper
import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
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(
    FacebookSdk::class, Utility::class, Looper::class, FacebookSignatureValidator::class)
class AttributionIdentifiersTest : FacebookPowerMockTestCase() {
  private lateinit var mockMainLooper: Looper
  private lateinit var mockId: AttributionIdentifiers
  private lateinit var mockContext: Context
  private lateinit var mockPackageManager: PackageManager
  private lateinit var mockAttributionId: String

  @Before
  fun init() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    PowerMockito.mockStatic(Looper::class.java)
    mockMainLooper = PowerMockito.mock(Looper::class.java)
    whenever(Looper.getMainLooper()).thenReturn(mockMainLooper)
    PowerMockito.mockStatic(Utility::class.java)

    mockId = PowerMockito.mock(AttributionIdentifiers::class.java)
    mockContext = PowerMockito.mock(Context::class.java)
    mockPackageManager = PowerMockito.mock(PackageManager::class.java)
    whenever(mockContext.packageManager).thenReturn(mockPackageManager)

    // bypass all signature validation for unit test
    PowerMockito.mockStatic(FacebookSignatureValidator::class.java)
    whenever(FacebookSignatureValidator.validateSignature(anyOrNull(), anyOrNull()))
        .thenReturn(true)

    AttributionIdentifiers.cachedIdentifiers = null
  }

  @Test
  fun `test is tracking limited`() {
    val mockAttributionIdentifierCompanion =
        PowerMockito.mock(AttributionIdentifiers.Companion::class.java)
    WhiteboxImpl.setInternalState(
        AttributionIdentifiers::class.java, "Companion", mockAttributionIdentifierCompanion)
    whenever(mockAttributionIdentifierCompanion.isTrackingLimited(any())).thenCallRealMethod()
    whenever(mockAttributionIdentifierCompanion.getAttributionIdentifiers(mockContext))
        .thenReturn(mockId)

    whenever(mockId.isTrackingLimited).thenReturn(true)
    assertThat(AttributionIdentifiers.isTrackingLimited(mockContext)).isTrue
    whenever(mockId.isTrackingLimited).thenReturn(false)
    assertThat(AttributionIdentifiers.isTrackingLimited(mockContext)).isFalse
  }

  @Test
  fun `test get attribution id on main thread`() {
    whenever(Looper.myLooper()).thenReturn(mockMainLooper)
    assertThat(AttributionIdentifiers.getAttributionIdentifiers(mockContext)).isNull()
  }

  @Test
  fun `test return new identifier from attribution id provider if not google play available`() {
    val newLooper = PowerMockito.mock(Looper::class.java)
    whenever(Looper.myLooper()).thenReturn(newLooper)
    whenever(Utility.getMethodQuietly(any<String>(), any<String>(), any())).thenReturn(null)
    setMockContextWithAttributionIdProvider()

    val obtainedId = AttributionIdentifiers.getAttributionIdentifiers(mockContext)
    assertThat(obtainedId?.attributionId).isEqualTo(mockAttributionId)
    assertThat(obtainedId).isEqualTo(AttributionIdentifiers.cachedIdentifiers)
  }

  @Test
  fun `test return new identifier from attribution id provider if bindService throws SecurityException`() {
    val newLooper = PowerMockito.mock(Looper::class.java)
    whenever(Looper.myLooper()).thenReturn(newLooper)
    whenever(mockContext.bindService(any(), any(), any())).thenThrow(SecurityException())
    whenever(Utility.getMethodQuietly(any<String>(), any<String>(), any())).thenReturn(null)
    setMockContextWithAttributionIdProvider()

    val obtainedId = AttributionIdentifiers.getAttributionIdentifiers(mockContext)
    assertThat(obtainedId?.attributionId).isEqualTo(mockAttributionId)
    assertThat(obtainedId).isEqualTo(AttributionIdentifiers.cachedIdentifiers)
  }

  private fun setMockContextWithAttributionIdProvider() {
    mockAttributionId = "aid123456789"
    val mockAttributionIdProviderInfo = ProviderInfo()
    mockAttributionIdProviderInfo.packageName = "com.facebook.katana"
    val debugApplicationInfo = ApplicationInfo()
    debugApplicationInfo.flags = debugApplicationInfo.flags or ApplicationInfo.FLAG_DEBUGGABLE
    val mockContentResolver = mock<ContentResolver>()
    val mockCursor = mock<android.database.Cursor>()
    whenever(mockCursor.getColumnIndex(any())).thenReturn(0)
    whenever(mockCursor.getString(0)).thenReturn(mockAttributionId)
    whenever(mockCursor.moveToFirst()).thenReturn(true)
    whenever(mockContext.applicationInfo).thenReturn(debugApplicationInfo)
    whenever(
            mockPackageManager.resolveContentProvider(
                AttributionIdentifiers.Companion.ATTRIBUTION_ID_CONTENT_PROVIDER, 0))
        .thenReturn(mockAttributionIdProviderInfo)
    whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
    whenever(mockContentResolver.query(any(), any(), anyOrNull(), anyOrNull(), anyOrNull()))
        .thenReturn(mockCursor)
  }
}


/*
 * 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.internal

import android.content.BroadcastReceiver
import android.content.Context
import android.content.IntentFilter
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import org.junit.Assert
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(LocalBroadcastManager::class, FacebookSdk::class)
class BoltsMeasurementEventListenerTest : FacebookPowerMockTestCase() {
  private lateinit var mockLocalBroadcastManager: LocalBroadcastManager
  private lateinit var mockContext: Context
  private lateinit var capturedReceiver: BroadcastReceiver
  private lateinit var capturedIntentFilter: IntentFilter

  @Before
  fun init() {
    mockContext = PowerMockito.mock(Context::class.java)
    whenever(mockContext.applicationContext).thenReturn(ApplicationProvider.getApplicationContext())

    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)

    mockLocalBroadcastManager = PowerMockito.mock(LocalBroadcastManager::class.java)
    whenever(mockLocalBroadcastManager.registerReceiver(any(), any())).thenAnswer {
      capturedReceiver = it.arguments[0] as BroadcastReceiver
      capturedIntentFilter = it.arguments[1] as IntentFilter
      Unit
    }
    PowerMockito.mockStatic(LocalBroadcastManager::class.java)
    PowerMockito.`when`(
            LocalBroadcastManager.getInstance(ApplicationProvider.getApplicationContext()))
        .thenReturn(mockLocalBroadcastManager)
  }

  @Test
  fun `test getInstance registers the receiver`() {
    val listener = BoltsMeasurementEventListener.getInstance(mockContext)
    Assert.assertEquals(listener, capturedReceiver)
    Assert.assertEquals(
        capturedIntentFilter?.getAction(0),
        BoltsMeasurementEventListener.MEASUREMENT_EVENT_NOTIFICATION_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.internal

import android.os.Bundle
import com.facebook.FacebookTestCase
import com.facebook.FacebookTestUtility
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONArray
import org.json.JSONObject
import org.junit.Assert
import org.junit.Test

class BundleJSONConverterTest : FacebookTestCase() {
  @Test
  fun testSimpleValues() {
    val arrayList = arrayListOf("1st", "2nd", "third")
    val innerBundle1 = Bundle()
    innerBundle1.putInt("inner", 1)
    val innerBundle2 = Bundle()
    innerBundle2.putString("inner", "2")
    innerBundle2.putStringArray("deep list", arrayOf("7", "8"))
    innerBundle1.putBundle("nested bundle", innerBundle2)
    val b = Bundle()
    b.putBoolean("boolValue", true)
    b.putInt("intValue", 7)
    b.putLong("longValue", 5_000_000_000L)
    b.putDouble("doubleValue", 3.14)
    b.putString("stringValue", "hello world")
    b.putStringArray("stringArrayValue", arrayOf("first", "second"))
    b.putStringArrayList("stringArrayListValue", arrayList)
    b.putBundle("nested", innerBundle1)
    val json = BundleJSONConverter.convertToJSON(b)
    Assert.assertNotNull(json)
    Assert.assertEquals(true, json.getBoolean("boolValue"))
    Assert.assertEquals(7, json.getInt("intValue"))
    Assert.assertEquals(5_000_000_000L, json.getLong("longValue"))
    Assert.assertEquals(
        3.14, json.getDouble("doubleValue"), FacebookTestUtility.DOUBLE_EQUALS_DELTA)
    Assert.assertEquals("hello world", json.getString("stringValue"))
    var jsonArray = json.getJSONArray("stringArrayValue")
    Assert.assertEquals(2, jsonArray.length())
    Assert.assertEquals("first", jsonArray.getString(0))
    Assert.assertEquals("second", jsonArray.getString(1))
    jsonArray = json.getJSONArray("stringArrayListValue")
    Assert.assertEquals(3, jsonArray.length())
    Assert.assertEquals("1st", jsonArray.getString(0))
    Assert.assertEquals("2nd", jsonArray.getString(1))
    Assert.assertEquals("third", jsonArray.getString(2))
    var innerJson = json.getJSONObject("nested")
    Assert.assertEquals(1, innerJson.getInt("inner"))
    innerJson = innerJson.getJSONObject("nested bundle")
    Assert.assertEquals("2", innerJson.getString("inner"))
    jsonArray = innerJson.getJSONArray("deep list")
    Assert.assertEquals(2, jsonArray.length())
    Assert.assertEquals("7", jsonArray.getString(0))
    Assert.assertEquals("8", jsonArray.getString(1))
    var finalBundle: Bundle? = BundleJSONConverter.convertToBundle(json)
    Assert.assertNotNull(finalBundle)
    checkNotNull(finalBundle)
    Assert.assertEquals(true, finalBundle.getBoolean("boolValue"))
    Assert.assertEquals(7, finalBundle.getInt("intValue"))
    Assert.assertEquals(5_000_000_000L, finalBundle.getLong("longValue"))
    Assert.assertEquals(
        3.14, finalBundle.getDouble("doubleValue"), FacebookTestUtility.DOUBLE_EQUALS_DELTA)
    Assert.assertEquals("hello world", finalBundle.getString("stringValue"))
    var stringList = finalBundle.getStringArrayList("stringArrayValue")
    checkNotNull(stringList)
    Assert.assertEquals(2, stringList.size)
    Assert.assertEquals("first", stringList[0])
    Assert.assertEquals("second", stringList[1])
    stringList = finalBundle.getStringArrayList("stringArrayListValue")
    checkNotNull(stringList)
    Assert.assertEquals(3, stringList.size)
    Assert.assertEquals("1st", stringList[0])
    Assert.assertEquals("2nd", stringList[1])
    Assert.assertEquals("third", stringList[2])
    val finalInnerBundle = finalBundle.getBundle("nested")
    checkNotNull(finalInnerBundle)
    Assert.assertEquals(1, finalInnerBundle.getInt("inner"))
    finalBundle = finalInnerBundle.getBundle("nested bundle")
    checkNotNull(finalBundle)
    Assert.assertEquals("2", finalBundle.getString("inner"))
    stringList = finalBundle.getStringArrayList("deep list")
    checkNotNull(stringList)
    Assert.assertEquals(2, stringList.size)
    Assert.assertEquals("7", stringList[0])
    Assert.assertEquals("8", stringList[1])
  }

  @Test
  fun testUnsupportedValues() {
    val b = Bundle()
    b.putShort("shortValue", 7.toShort())
    var exceptionCaught = false
    try {
      BundleJSONConverter.convertToJSON(b)
    } catch (a: IllegalArgumentException) {
      exceptionCaught = true
    }
    assertThat(exceptionCaught).isTrue
    val jsonArray = JSONArray()
    jsonArray.put(10)
    val json = JSONObject()
    json.put("arrayValue", jsonArray)
    exceptionCaught = false
    try {
      BundleJSONConverter.convertToBundle(json)
    } catch (a: IllegalArgumentException) {
      exceptionCaught = true
    }
    assertThat(exceptionCaught).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.internal

import android.content.Intent
import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.internal.CallbackManagerImpl.Companion.registerStaticCallback
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.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(FacebookSdk::class)
class CallbackManagerImplTest : FacebookPowerMockTestCase() {
  @Before
  fun before() {
    mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123456789")
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    // Reset the static state every time so tests don't interfere with each other.
    Whitebox.setInternalState(
        CallbackManagerImpl::class.java,
        "staticCallbacks",
        HashMap<Int, CallbackManagerImpl.Callback>())
  }

  @Test
  fun `test callback executed`() {
    var capturedResult = false
    val callbackManagerImpl = CallbackManagerImpl()
    callbackManagerImpl.registerCallback(
        CallbackManagerImpl.RequestCodeOffset.Login.toRequestCode(),
        object : CallbackManagerImpl.Callback {
          override fun onActivityResult(resultCode: Int, data: Intent?): Boolean {
            capturedResult = true
            return true
          }
        })
    callbackManagerImpl.onActivityResult(FacebookSdk.getCallbackRequestCodeOffset(), 1, Intent())
    assertThat(capturedResult).isTrue
  }

  @Test
  fun `test right callback executed`() {
    var capturedResult = false
    val callbackManagerImpl = CallbackManagerImpl()
    callbackManagerImpl.registerCallback(
        123,
        object : CallbackManagerImpl.Callback {
          override fun onActivityResult(resultCode: Int, data: Intent?): Boolean {
            capturedResult = true
            return true
          }
        })
    callbackManagerImpl.registerCallback(
        456,
        object : CallbackManagerImpl.Callback {
          override fun onActivityResult(resultCode: Int, data: Intent?): Boolean = false
        })
    callbackManagerImpl.onActivityResult(123, 1, Intent())
    assertThat(capturedResult).isTrue
  }

  @Test
  fun `test static callback executed`() {
    var capturedResult = false
    val callbackManagerImpl = CallbackManagerImpl()
    registerStaticCallback(
        CallbackManagerImpl.RequestCodeOffset.Login.toRequestCode(),
        object : CallbackManagerImpl.Callback {
          override fun onActivityResult(resultCode: Int, data: Intent?): Boolean {
            capturedResult = true
            return true
          }
        })
    callbackManagerImpl.onActivityResult(FacebookSdk.getCallbackRequestCodeOffset(), 1, Intent())
    assertThat(capturedResult).isTrue
  }

  @Test
  fun `test static callback skipped`() {
    var capturedResult = false
    var capturedResultStatic = false
    val callbackManagerImpl = CallbackManagerImpl()
    callbackManagerImpl.registerCallback(
        CallbackManagerImpl.RequestCodeOffset.Login.toRequestCode(),
        object : CallbackManagerImpl.Callback {
          override fun onActivityResult(resultCode: Int, data: Intent?): Boolean {
            capturedResult = true
            return true
          }
        })
    registerStaticCallback(
        CallbackManagerImpl.RequestCodeOffset.Login.toRequestCode(),
        object : CallbackManagerImpl.Callback {
          override fun onActivityResult(resultCode: Int, data: Intent?): Boolean {
            capturedResultStatic = true
            return true
          }
        })
    callbackManagerImpl.onActivityResult(FacebookSdk.getCallbackRequestCodeOffset(), 1, Intent())
    assertThat(capturedResult).isTrue
    assertThat(capturedResultStatic).isFalse
  }
}


/*
 * 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.internal

import android.content.Context
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import org.junit.Assert
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
import org.powermock.reflect.internal.WhiteboxImpl

@PrepareForTest(FacebookSdk::class)
class FacebookInitProviderTest : FacebookPowerMockTestCase() {
  private lateinit var provider: FacebookInitProvider
  private lateinit var mockContext: Context
  @Before
  fun init() {
    mockContext = PowerMockito.mock(Context::class.java)
    provider = FacebookInitProvider()
    WhiteboxImpl.setInternalState(provider, "mContext", mockContext as Any)
    PowerMockito.mockStatic(FacebookSdk::class.java)
  }

  @Test
  fun `test onCreate call FacebookSdk initialization`() {
    var capturedContext: Context? = null
    whenever(FacebookSdk.sdkInitialize(any())).thenAnswer {
      capturedContext = it.arguments[0] as Context
      Unit
    }
    provider.onCreate()
    Assert.assertNotNull(capturedContext)
    Assert.assertEquals(mockContext, capturedContext)
  }
}


/*
 * 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.internal

import com.facebook.FacebookRequestError
import com.facebook.FacebookTestCase
import org.json.JSONObject
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Test

class FacebookRequestErrorClassificationTest : FacebookTestCase() {
  private val errorClassificationJSON =
      "{" +
          "   \"android_sdk_error_categories\": [" +
          "      {" +
          "         \"name\": \"other\"," +
          "         \"items\": [" +
          "           { \"code\": 102, \"subcodes\": [ 459, 464 ] }," +
          "           { \"code\": 190, \"subcodes\": [ 459, 464 ] }" +
          "         ]" +
          "      }," +
          "      {" +
          "         \"name\": \"login_recoverable\"," +
          "         \"items\": [ { \"code\": 102 }, { \"code\": 190 } ]," +
          "         \"recovery_message\": \"Please log into this app again to reconnect your Facebook account.\"" +
          "      }," +
          "      {" +
          "         \"name\": \"transient\"," +
          "         \"items\": [ { \"code\": 1 }, { \"code\": 2 }, { \"code\": 4 }, { \"code\": 9 }, { \"code\": 17 }, { \"code\": 341 } ]" +
          "      }" +
          "   ]," +
          "   \"id\": \"233936543368280\"" +
          "}"

  @Test
  fun `test create from json`() {
    val serverResponse = JSONObject(errorClassificationJSON)
    val jsonArray = serverResponse.getJSONArray("android_sdk_error_categories")
    var errorClassification = FacebookRequestErrorClassification.createFromJSON(jsonArray)
    assertNotNull(errorClassification)
    errorClassification = checkNotNull(errorClassification)
    assertNull(errorClassification.getRecoveryMessage(FacebookRequestError.Category.OTHER))
    assertNull(errorClassification.getRecoveryMessage(FacebookRequestError.Category.TRANSIENT))
    assertNotNull(
        errorClassification.getRecoveryMessage(FacebookRequestError.Category.LOGIN_RECOVERABLE))
    assertEquals(2, errorClassification.otherErrors?.size)
    assertEquals(2, errorClassification.loginRecoverableErrors?.size)
    assertEquals(6, errorClassification.transientErrors?.size)
    // test subcodes
    assertEquals(2, errorClassification.otherErrors?.get(102)?.size)
    assertNull(errorClassification.loginRecoverableErrors?.get(102))
  }

  @Test
  fun `test classify category `() {
    val serverResponse = JSONObject(errorClassificationJSON)
    val jsonArray = serverResponse.getJSONArray("android_sdk_error_categories")
    var errorClassification = FacebookRequestErrorClassification.createFromJSON(jsonArray)
    errorClassification = checkNotNull(errorClassification)
    assertEquals(FacebookRequestError.Category.TRANSIENT, errorClassification.classify(1, 2, true))
    assertEquals(FacebookRequestError.Category.OTHER, errorClassification.classify(102, 459, false))
    assertEquals(
        FacebookRequestError.Category.LOGIN_RECOVERABLE,
        errorClassification.classify(190, 1, false))
    assertEquals(
        FacebookRequestError.Category.TRANSIENT, errorClassification.classify(341, 1, false))
  }

  @Test
  fun `test get default error classification `() {
    val errorClassification = FacebookRequestErrorClassification.defaultErrorClassification
    assertNull(errorClassification.getRecoveryMessage(FacebookRequestError.Category.OTHER))
    assertNull(errorClassification.getRecoveryMessage(FacebookRequestError.Category.TRANSIENT))
    assertNull(
        errorClassification.getRecoveryMessage(FacebookRequestError.Category.LOGIN_RECOVERABLE))

    assertNull(errorClassification.otherErrors)
    assertNotNull(errorClassification.transientErrors)
    assertNotNull(errorClassification.loginRecoverableErrors)
  }
}


/*
 * 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.internal

import android.app.Activity
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.Signature
import com.facebook.FacebookPowerMockTestCase
import com.facebook.internal.FacebookSignatureValidator.validateSignature
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
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

/** Tests for {@link com.facebook.internal.FacebookSignatureValidator}. */
@PrepareForTest(Utility::class)
class FacebookSignatureValidatorTest : FacebookPowerMockTestCase() {

  companion object {
    private const val PACKAGE_NAME = "com.facebook.orca"
    private const val APP_HASH = "8a3c4b262d721acd49a4bf97d5213199c86fa2b9"
  }

  @Mock private lateinit var mockActivity: Activity
  @Mock private lateinit var mockPackageManager: PackageManager
  @Mock private lateinit var mockApplicationInfo: ApplicationInfo

  @Before
  fun init() {
    mockStatic(Utility::class.java)
    whenever(mockActivity.packageManager).thenReturn(mockPackageManager)
    whenever(mockActivity.applicationInfo).thenReturn(mockApplicationInfo)
  }

  @Test
  fun testInvalidWhenAppNotInstalled() {
    setupPackageManagerForApp(isInstalled = false, hasValidSignature = false)
    assertThat(validateSignature(mockActivity, PACKAGE_NAME)).isFalse
  }

  @Test
  fun testInvalidWhenInstalledWithIncorrectSignature() {
    setupPackageManagerForApp(isInstalled = true, hasValidSignature = false)
    assertThat(validateSignature(mockActivity, PACKAGE_NAME)).isFalse
  }

  @Test
  fun testValidWhenInstalledWithCorrectSignature() {
    setupPackageManagerForApp(isInstalled = true, hasValidSignature = true)
    assertThat(validateSignature(mockActivity, PACKAGE_NAME)).isTrue
  }

  /**
   * Sets up the PackageManager to return what we expect depending on whether app is installed.
   *
   * @param isInstalled true to simulate that app is installed
   * @param hasValidSignature true to give the app a valid signature
   */
  private fun setupPackageManagerForApp(isInstalled: Boolean, hasValidSignature: Boolean) {
    if (isInstalled) {
      val packageInfo = PackageInfo()
      whenever(mockPackageManager.getPackageInfo(PACKAGE_NAME, PackageManager.GET_SIGNATURES))
          .thenReturn(packageInfo)
      val signature = mock(Signature::class.java)
      whenever(signature.toByteArray()).thenReturn(byteArrayOf())
      packageInfo.signatures = arrayOf(signature)

      if (hasValidSignature) {
        whenever(Utility.sha1hash(signature.toByteArray())).thenReturn(APP_HASH)
      }
    } else {
      whenever(mockPackageManager.getPackageInfo(PACKAGE_NAME, PackageManager.GET_SIGNATURES))
          .thenThrow(PackageManager.NameNotFoundException())
    }
  }
}


/*
 * 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.internal

import com.facebook.internal.FeatureManager.Feature
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Test

class FeatureManagerTest {

  @Test
  fun `test getting features from class names`() {
    assertEquals(Feature.Unknown, FeatureManager.getFeature("very.fake.class.name"))

    assertEquals(
        Feature.AAM, FeatureManager.getFeature("com.facebook.appevents.aam.MetadataIndexer"))
    assertEquals(
        Feature.AAM,
        FeatureManager.getFeature("com.facebook.appevents.aam.DoesNotExistButStillShouldPass"))

    assertEquals(
        Feature.CodelessEvents,
        FeatureManager.getFeature("com.facebook.appevents.codeless.CodelessManager"))
    assertEquals(
        Feature.CodelessEvents,
        FeatureManager.getFeature("com.facebook.appevents.codeless.DoesNotExistButStillShouldPass"))

    assertEquals(
        Feature.CloudBridge,
        FeatureManager.getFeature("com.facebook.appevents.cloudbridge.AppEventsCAPIManager"))
    assertEquals(
        Feature.CloudBridge,
        FeatureManager.getFeature(
            "com.facebook.appevents.cloudbridge.DoesNotExistButStillShouldPass"))

    assertEquals(
        Feature.ErrorReport,
        FeatureManager.getFeature(
            "com.facebook.internal.instrument.errorreport.ErrorReportHandler"))
    assertEquals(
        Feature.ErrorReport,
        FeatureManager.getFeature(
            "com.facebook.internal.instrument.errorreport.DoesNotExistButStillShouldPass"))

    assertEquals(
        Feature.PrivacyProtection,
        FeatureManager.getFeature("com.facebook.appevents.ml.ModelManager"))
    assertEquals(
        Feature.PrivacyProtection,
        FeatureManager.getFeature("com.facebook.appevents.ml.DoesNotExistButStillShouldPass"))

    assertEquals(
        Feature.SuggestedEvents,
        FeatureManager.getFeature("com.facebook.appevents.suggestedevents.FeatureExtractor"))
    assertEquals(
        Feature.SuggestedEvents,
        FeatureManager.getFeature(
            "com.facebook.appevents.suggestedevents.DoesNotExistButStillShouldPass"))

    assertEquals(
        Feature.RestrictiveDataFiltering,
        FeatureManager.getFeature(
            "com.facebook.appevents.restrictivedatafilter.RestrictiveDataManager"))
    assertNotEquals(
        Feature.RestrictiveDataFiltering,
        FeatureManager.getFeature(
            "com.facebook.appevents.restrictivedatafilter.DoesNotExistAndShouldNotPass"))

    assertEquals(
        Feature.IntelligentIntegrity,
        FeatureManager.getFeature("com.facebook.appevents.integrity.IntegrityManager"))
    assertNotEquals(
        Feature.IntelligentIntegrity,
        FeatureManager.getFeature("com.facebook.appevents.integrity.DoesNotExistAndShouldNotPass"))

    assertEquals(
      Feature.ProtectedMode,
      FeatureManager.getFeature("com.facebook.appevents.integrity.ProtectedModeManager"))
    assertNotEquals(
      Feature.ProtectedMode,
      FeatureManager.getFeature("com.facebook.appevents.integrity.DoesNotExistAndShouldNotPass"))

    assertEquals(
            Feature.MACARuleMatching,
            FeatureManager.getFeature("com.facebook.appevents.integrity.MACARuleMatchingManager"))
    assertNotEquals(
            Feature.MACARuleMatching,
            FeatureManager.getFeature("com.facebook.appevents.integrity.DoesNotExistAndShouldNotPass"))

    assertEquals(
        Feature.EventDeactivation,
        FeatureManager.getFeature(
            "com.facebook.appevents.eventdeactivation.EventDeactivationManager"))
    assertEquals(
        Feature.EventDeactivation,
        FeatureManager.getFeature(
            "com.facebook.appevents.eventdeactivation.DoesNotExistButStillShouldPass"))

    assertEquals(
        Feature.OnDeviceEventProcessing,
        FeatureManager.getFeature(
            "com.facebook.appevents.ondeviceprocessing.OnDeviceProcessingManager"))
    assertEquals(
        Feature.OnDeviceEventProcessing,
        FeatureManager.getFeature(
            "com.facebook.appevents.ondeviceprocessing.DoesNotExistButStillShouldPass"))

    assertEquals(
        Feature.IapLogging,
        FeatureManager.getFeature("com.facebook.appevents.iap.InAppPurchaseManager"))
    assertEquals(
        Feature.IapLogging,
        FeatureManager.getFeature("com.facebook.appevents.iap.DoesNotExistButStillShouldPass"))

    assertEquals(
        Feature.Monitoring, FeatureManager.getFeature("com.facebook.internal.logging.monitor"))
    assertEquals(
        Feature.Monitoring,
        FeatureManager.getFeature(
            "com.facebook.internal.logging.monitor.DoesNotExistButStillShouldPass"))
  }

  @Test
  fun `test features parents inside Core`() {
    assertEquals(Feature.Core, Feature.Core.parent)
    assertEquals(Feature.Core, Feature.AppEvents.parent)
    assertEquals(Feature.AppEvents, Feature.CodelessEvents.parent)
    assertEquals(Feature.AppEvents, Feature.CloudBridge.parent)
    assertEquals(Feature.AppEvents, Feature.RestrictiveDataFiltering.parent)
    assertEquals(Feature.AppEvents, Feature.AAM.parent)
    assertEquals(Feature.AppEvents, Feature.PrivacyProtection.parent)
    assertEquals(Feature.AppEvents, Feature.IapLogging.parent)
    assertEquals(Feature.IapLogging, Feature.IapLoggingLib2.parent)
    assertEquals(Feature.PrivacyProtection, Feature.SuggestedEvents.parent)
    assertEquals(Feature.PrivacyProtection, Feature.IntelligentIntegrity.parent)
    assertEquals(Feature.PrivacyProtection, Feature.ProtectedMode.parent)
    assertEquals(Feature.PrivacyProtection, Feature.MACARuleMatching.parent)
    assertEquals(Feature.AppEvents, Feature.EventDeactivation.parent)
    assertEquals(Feature.Core, Feature.Instrument.parent)
    assertEquals(Feature.Instrument, Feature.CrashReport.parent)
    assertEquals(Feature.CrashReport, Feature.CrashShield.parent)
    assertEquals(Feature.CrashReport, Feature.ThreadCheck.parent)
    assertEquals(Feature.Instrument, Feature.ErrorReport.parent)
    assertEquals(Feature.AppEvents, Feature.OnDeviceEventProcessing.parent)
    assertEquals(Feature.OnDeviceEventProcessing, Feature.OnDevicePostInstallEventProcessing.parent)
    assertEquals(Feature.Core, Feature.Monitoring.parent)
    assertEquals(Feature.Core, Feature.Megatron.parent)
    assertEquals(Feature.Core, Feature.Elora.parent)
  }

  @Test
  fun `test features parents inside Login`() {
    assertEquals(Feature.Core, Feature.Login.parent)
  }

  @Test
  fun `test features parents inside Share`() {
    assertEquals(Feature.Core, Feature.Share.parent)
  }

  @Test
  fun `test return unknown feature`() {
    assertEquals(Feature.Unknown, Feature.fromInt(0x12345678))
  }
}


/*
 * 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.internal

import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.internal.gatekeeper.GateKeeper
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicBoolean
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.After
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.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(FacebookSdk::class)
class FetchedAppGateKeepersManagerTest : FacebookPowerMockTestCase() {

  companion object {
    const val APPLICATION_NAME = "aa"
    const val GK1 = "FBSDKFeatureInstrument"
    const val GK2 = "app_events_killswitch"

    const val VALID_JSON =
        "{\n" +
            "  \"data\": [\n" +
            "    {\n" +
            "      \"gatekeepers\": [\n" +
            "        {\n" +
            "          \"key\": \"" +
            GK1 +
            "\",\n" +
            "          \"value\": true\n" +
            "        },\n" +
            "        {\n" +
            "          \"key\": \"" +
            GK2 +
            "\",\n" +
            "          \"value\": \"false\"\n" +
            "        }\n" +
            "      ]\n" +
            "    }\n" +
            "  ]\n" +
            "}"

    const val NON_BOOLEAN_VALUE_RESPONSE =
        "{\n" +
            "  \"data\": [\n" +
            "    {\n" +
            "      \"gatekeepers\": [\n" +
            "        {\n" +
            "          \"key\": \"" +
            GK1 +
            "\",\n" +
            "          \"value\": swag\n" +
            "        },\n" +
            "      ]\n" +
            "    }\n" +
            "  ]\n" +
            "}"

    const val EMPTY_GK_LIST_RESPONSE =
        "{\n" +
            "  \"data\": [\n" +
            "    {\n" +
            "      \"gatekeepers\": [\n" +
            "      ]\n" +
            "    }\n" +
            "  ]\n" +
            "}"

    const val EMPTY_RESPONSE = "{}"
    const val EMPTY_DATA_RESPONSE = "{\n" + "  \"data\": [\n" + "  ]\n" + "}"
  }
  private var asyncTaskExecutedTimes = 0

  @Before
  fun init() {
    mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn(APPLICATION_NAME)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    val mockExecutor = mock<Executor>()
    whenever(mockExecutor.execute(any())).thenAnswer {
      asyncTaskExecutedTimes++
      // reset isLoading
      Whitebox.setInternalState(
          FetchedAppGateKeepersManager::class.java, "isLoading", AtomicBoolean(false))
    }
    whenever(FacebookSdk.getExecutor()).thenReturn(mockExecutor)

    // because it is a static variable which holds a lot of state about the GKs, we need to reset it
    // every time
    Whitebox.setInternalState(
        FetchedAppGateKeepersManager::class.java,
        "fetchedAppGateKeepers",
        ConcurrentHashMap<String, JSONObject>())
  }

  @After
  fun clean() {
    FetchedAppGateKeepersManager.resetRuntimeGateKeeperCache()
  }

  @Test
  fun `parse valid json_ok`() {
    val test = JSONObject(VALID_JSON)
    val result = FetchedAppGateKeepersManager.parseAppGateKeepersFromJSON(APPLICATION_NAME, test)
    assertThat(result.getBoolean(GK2)).isFalse
    assertThat(result.getBoolean(GK1)).isTrue

    val map = FetchedAppGateKeepersManager.getGateKeepersForApplication(APPLICATION_NAME)
    assertThat(map[GK1]).isTrue
    assertThat(map[GK2]).isFalse

    val gk1 = FetchedAppGateKeepersManager.getGateKeeperForKey(GK1, APPLICATION_NAME, false)
    val gk2 = FetchedAppGateKeepersManager.getGateKeeperForKey(GK2, APPLICATION_NAME, true)
    assertThat(gk1).isTrue
    assertThat(gk2).isFalse
    // "Both getGateKeepersForApplication and getGateKeeperForKey call async"
    assertThat(asyncTaskExecutedTimes).isEqualTo(3)
  }

  @Test
  fun `parse value isnt boolean_fail`() {
    val test = JSONObject(NON_BOOLEAN_VALUE_RESPONSE)
    val result = FetchedAppGateKeepersManager.parseAppGateKeepersFromJSON(APPLICATION_NAME, test)
    assertThat(result.length()).isEqualTo(0)

    val map = FetchedAppGateKeepersManager.getGateKeepersForApplication(APPLICATION_NAME)
    // current parser filters out non boolean values, otherwise map will actually return the exact
    // value
    assertThat(map[GK1]).isNull()

    val gk = FetchedAppGateKeepersManager.getGateKeeperForKey(GK1, APPLICATION_NAME, false)
    assertThat(gk).isFalse
    // "Both getGateKeepersForApplication and getGateKeeperForKey call async"
    assertThat(asyncTaskExecutedTimes).isEqualTo(2)
  }

  @Test
  fun `parse empty list of gks_fail`() {
    val test = JSONObject(EMPTY_GK_LIST_RESPONSE)
    val result = FetchedAppGateKeepersManager.parseAppGateKeepersFromJSON(APPLICATION_NAME, test)
    assertThat(result.length()).isEqualTo(0)

    val map = FetchedAppGateKeepersManager.getGateKeepersForApplication(APPLICATION_NAME)
    assertThat(map.size).isEqualTo(0)

    val gk = FetchedAppGateKeepersManager.getGateKeeperForKey("anything", APPLICATION_NAME, false)
    assertThat(gk).isFalse()
    // "Both getGateKeepersForApplication and getGateKeeperForKey call async"
    assertThat(asyncTaskExecutedTimes).isEqualTo(2)
  }

  @Test
  fun `parse empty response_fail`() {
    val test = JSONObject(EMPTY_RESPONSE)
    val result = FetchedAppGateKeepersManager.parseAppGateKeepersFromJSON(APPLICATION_NAME, test)
    assertThat(result.length()).isEqualTo(0)

    val map = FetchedAppGateKeepersManager.getGateKeepersForApplication(APPLICATION_NAME)
    assertThat(map.size).isEqualTo(0)

    val gk = FetchedAppGateKeepersManager.getGateKeeperForKey("anything", APPLICATION_NAME, false)
    assertThat(gk).isFalse
    // "Both getGateKeepersForApplication and getGateKeeperForKey call async"
    assertThat(asyncTaskExecutedTimes).isEqualTo(2)
  }

  @Test
  fun `parse empty data response of gks_fail`() {
    val test = JSONObject(EMPTY_DATA_RESPONSE)
    val result = FetchedAppGateKeepersManager.parseAppGateKeepersFromJSON(APPLICATION_NAME, test)
    assertThat(result.length()).isEqualTo(0)

    val map = FetchedAppGateKeepersManager.getGateKeepersForApplication(APPLICATION_NAME)
    assertThat(map.size).isEqualTo(0)

    val gk = FetchedAppGateKeepersManager.getGateKeeperForKey("anything", APPLICATION_NAME, false)
    assertThat(gk).isFalse
    // "Both getGateKeepersForApplication and getGateKeeperForKey call async"
    assertThat(asyncTaskExecutedTimes).isEqualTo(2)
  }

  @Test
  fun `null in gk map is still default value_ok`() {
    val map = mapOf<String, JSONObject?>(APPLICATION_NAME to null)
    Whitebox.setInternalState(
        FetchedAppGateKeepersManager::class.java, "fetchedAppGateKeepers", map)
    val gk = FetchedAppGateKeepersManager.getGateKeeperForKey("anything", APPLICATION_NAME, false)
    val gk1 = FetchedAppGateKeepersManager.getGateKeeperForKey("anything", APPLICATION_NAME, true)
    assertThat(gk).isFalse
    assertThat(gk1).isTrue
    assertThat(asyncTaskExecutedTimes).isEqualTo(2)
  }

  @Test
  fun `set gate keeper value`() {
    val test = JSONObject(VALID_JSON)
    FetchedAppGateKeepersManager.parseAppGateKeepersFromJSON(APPLICATION_NAME, test)
    FetchedAppGateKeepersManager.getGateKeepersForApplication(APPLICATION_NAME)

    FetchedAppGateKeepersManager.setRuntimeGateKeeper(gateKeeper = GateKeeper(GK1, true))
    val map1 = FetchedAppGateKeepersManager.getGateKeepersForApplication(APPLICATION_NAME)
    assertThat(map1.getValue(GK1)).isTrue
    FetchedAppGateKeepersManager.setRuntimeGateKeeper(gateKeeper = GateKeeper(GK1, false))
    val map2 = FetchedAppGateKeepersManager.getGateKeepersForApplication(APPLICATION_NAME)
    assertThat(map2.getValue(GK1)).isFalse
  }
}


/*
 * 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.internal

import com.facebook.FacebookPowerMockTestCase
import java.util.EnumSet
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test

class FetchedAppSettingsManagerTest : FacebookPowerMockTestCase() {
  private val validJson =
      "{\n" +
          "  \"supports_implicit_sdk_logging\": true,\n" +
          "  \"suggested_events_setting\": \"{}\",\n" +
          "  \"aam_rules\": \"{}\",\n" +
          "  \"app_events_feature_bitmask\": 65541,\n" +
          "  \"app_events_session_timeout\": 60,\n" +
          "  \"seamless_login\": 1,\n" +
          "  \"smart_login_bookmark_icon_url\": \"swag\",\n" +
          "  \"smart_login_menu_icon_url\": \"yolo\",\n" +
          "  \"android_dialog_configs\": \"garbage\"\n" +
          "}"

  private val invalidValueTypesJson =
      "{\n" +
          "  \"smart_login_bookmark_icon_url\": true,\n" +
          "  \"supports_implicit_sdk_logging\": \"true\",\n" +
          "  \"suggested_events_setting\": \"[]\",\n" +
          "  \"aam_rules\": \"hello\",\n" +
          "  \"app_events_session_timeout\": 6.4\n" +
          "}"

  @Test
  fun `parse valid json`() {
    val test = JSONObject(validJson)
    val result = FetchedAppSettingsManager.parseAppSettingsFromJSON("aa", test)

    assertThat(result.supportsImplicitLogging()).isTrue
    assertEquals("{}", result.suggestedEventsSetting)
    assertEquals("{}", result.rawAamRules)
    assertEquals(60, result.sessionTimeoutInSeconds)
    assertEquals(EnumSet.of(SmartLoginOption.Enabled), result.smartLoginOptions)
    assertEquals("swag", result.smartLoginBookmarkIconURL)
    assertEquals("yolo", result.smartLoginMenuIconURL)
    assertThat(result.dialogConfigurations.isEmpty()).isTrue

    // defaults
    assertThat(result.nuxEnabled).isFalse
    assertEquals("", result.nuxContent)
    assertThat(result.dialogConfigurations.isEmpty()).isTrue
    assertEquals("", result.restrictiveDataSetting)
  }

  @Test
  fun `parse invalid value types`() {
    val test = JSONObject(invalidValueTypesJson)
    val result = FetchedAppSettingsManager.parseAppSettingsFromJSON("aa", test)

    assertTrue(
        result.supportsImplicitLogging()) // actually allows case-insensitive value of true/false
    assertEquals(
        "[]", result.suggestedEventsSetting) // raw string is saved, callers job to figure out :)
    assertEquals("hello", result.rawAamRules)
    assertEquals(6, result.sessionTimeoutInSeconds) // will cast to int even tho its float/double
    assertEquals("", result.smartLoginMenuIconURL)
  }
}


/*
 * 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.internal

import android.net.Uri
import com.facebook.FacebookPowerMockTestCase
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONObject
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(FetchedAppSettingsManager::class)
class FetchedAppSettingsTest : FacebookPowerMockTestCase() {
  @Test
  fun `parse valid json`() {
    val parseDialogConfig =
        FetchedAppSettings.DialogFeatureConfig.parseDialogConfig(JSONObject(validJson))
    checkNotNull(parseDialogConfig)
    assertThat(parseDialogConfig.dialogName).isEqualTo(TEST_ACTION_NAME)
    assertThat(parseDialogConfig.featureName).isEqualTo(TEST_FEATURE_NAME)
    assertThat(parseDialogConfig.fallbackUrl)
        .isEqualTo(Uri.parse("/connect/dialog/MPlatformAppInvitesJSDialog"))
    assertThat(parseDialogConfig.versionSpec).containsExactly(2014_07_01, 2014_07_02, 2014_07_03)
  }

  @Test
  fun `parse valid json with versions in string`() {
    val parseDialogConfig =
        FetchedAppSettings.DialogFeatureConfig.parseDialogConfig(
            JSONObject(validJsonWithInvalidVersionsInString))
    checkNotNull(parseDialogConfig)

    assertThat(parseDialogConfig.dialogName).isEqualTo(TEST_ACTION_NAME)
    assertThat(parseDialogConfig.featureName).isEqualTo(TEST_FEATURE_NAME)
    assertThat(parseDialogConfig.fallbackUrl)
        .isEqualTo(Uri.parse("/connect/dialog/MPlatformAppInvitesJSDialog"))
    assertThat(parseDialogConfig.versionSpec).containsExactly(NativeProtocol.NO_PROTOCOL_AVAILABLE)
  }

  @Test
  fun `parse no delimiter in name`() {
    val parseDialogConfig =
        FetchedAppSettings.DialogFeatureConfig.parseDialogConfig(
            JSONObject(invalidjsonNoDelimiterName))
    assertThat(parseDialogConfig).isNull()
  }

  @Test
  fun `parse no feature name`() {
    val parseDialogConfig =
        FetchedAppSettings.DialogFeatureConfig.parseDialogConfig(
            JSONObject(invalidJsonNoFeatureName))
    assertThat(parseDialogConfig).isNull()
  }

  @Test
  fun `parse no dialog name`() {
    val parseDialogConfig =
        FetchedAppSettings.DialogFeatureConfig.parseDialogConfig(
            JSONObject(invalidJsonNoDialogName))
    assertThat(parseDialogConfig).isNull()
  }

  @Test
  fun `parse too many delimiters in name`() {
    val parseDialogConfig =
        FetchedAppSettings.DialogFeatureConfig.parseDialogConfig(
            JSONObject(invalidJsonMultipleDelimiters))
    assertThat(parseDialogConfig).isNull()
  }

  @Test
  fun `parse no fallback uri`() {
    val parseDialogConfig =
        FetchedAppSettings.DialogFeatureConfig.parseDialogConfig(JSONObject(validJsonNoFallbackUri))
    checkNotNull(parseDialogConfig)
    assertThat(parseDialogConfig.fallbackUrl).isNull()

    // rest ok
    assertThat(parseDialogConfig.dialogName).isEqualTo(TEST_ACTION_NAME)
    assertThat(parseDialogConfig.featureName).isEqualTo(TEST_FEATURE_NAME)
    assertThat(parseDialogConfig.versionSpec).containsExactly(2014_07_01, 2014_07_02, 2014_07_03)
  }

  @Test
  fun `parse no versions`() {
    val parseDialogConfig =
        FetchedAppSettings.DialogFeatureConfig.parseDialogConfig(JSONObject(validJsonNoVersions))
    checkNotNull(parseDialogConfig)
    assertThat(parseDialogConfig.versionSpec).isNull()

    // rest ok
    assertThat(parseDialogConfig.fallbackUrl)
        .isEqualTo(Uri.parse("/connect/dialog/MPlatformAppInvitesJSDialog"))
    assertThat(parseDialogConfig.dialogName).isEqualTo(TEST_ACTION_NAME)
    assertThat(parseDialogConfig.featureName).isEqualTo(TEST_FEATURE_NAME)
  }

  @Test
  fun `test getting dialog feature configuration with invalid feature and action names`() {
    assertThat(FetchedAppSettings.getDialogFeatureConfig(APP_ID, "", "")).isNull()
    assertThat(FetchedAppSettings.getDialogFeatureConfig(APP_ID, "BROWSER", "")).isNull()
    assertThat(FetchedAppSettings.getDialogFeatureConfig(APP_ID, "", "Login")).isNull()
  }

  @Test
  fun `test getting dialog feature configuration`() {
    PowerMockito.mockStatic(FetchedAppSettingsManager::class.java)
    val mockFetchedAppSettings = mock<FetchedAppSettings>()

    val mockFeature =
        checkNotNull(
            FetchedAppSettings.DialogFeatureConfig.parseDialogConfig(JSONObject(validJson)))
    val mockDialogConfigurations =
        mapOf(TEST_ACTION_NAME to mapOf(TEST_FEATURE_NAME to mockFeature))
    whenever(mockFetchedAppSettings.dialogConfigurations).thenReturn(mockDialogConfigurations)
    whenever(FetchedAppSettingsManager.getAppSettingsWithoutQuery(APP_ID))
        .thenReturn(mockFetchedAppSettings)

    val feature =
        FetchedAppSettings.getDialogFeatureConfig(APP_ID, TEST_ACTION_NAME, TEST_FEATURE_NAME)
    assertThat(feature).isEqualTo(mockFeature)
    assertThat(
            FetchedAppSettings.getDialogFeatureConfig(
                APP_ID, "unknown action name", TEST_FEATURE_NAME))
        .isNull()
    assertThat(
            FetchedAppSettings.getDialogFeatureConfig(
                APP_ID, TEST_ACTION_NAME, "unknown feature name"))
        .isNull()
  }

  companion object {
    private const val APP_ID = "123456789"
    private const val TEST_ACTION_NAME = "com.facebook.platform.action.request.APPINVITES_DIALOG"
    private const val TEST_FEATURE_NAME = "APP_INVITES_DIALOG"
    private const val validJson =
        "{\n" +
            "  \"name\": \"com.facebook.platform.action.request.APPINVITES_DIALOG|APP_INVITES_DIALOG\",\n" +
            "  \"url\": \"/connect/dialog/MPlatformAppInvitesJSDialog\",\n" +
            "  \"versions\": [20140701, 20140702, 20140703]" +
            "}"

    private const val validJsonWithInvalidVersionsInString =
        "{\n" +
            "  \"name\": \"com.facebook.platform.action.request.APPINVITES_DIALOG|APP_INVITES_DIALOG\",\n" +
            "  \"url\": \"/connect/dialog/MPlatformAppInvitesJSDialog\",\n" +
            "  \"versions\": [\"20140701x\"]" +
            "}"

    private const val invalidjsonNoDelimiterName =
        "{\n" +
            "  \"name\": \"com.facebook.platform.action.request.APPINVITES_DIALOG\",\n" +
            "  \"url\": \"/connect/dialog/MPlatformAppInvitesJSDialog\",\n" +
            "  \"versions\": [20140701, 20140702, 20140703]" +
            "}"

    private const val invalidJsonNoFeatureName =
        "{\n" +
            "  \"name\": \"com.facebook.platform.action.request.APPINVITES_DIALOG|\",\n" +
            "  \"url\": \"/connect/dialog/MPlatformAppInvitesJSDialog\",\n" +
            "  \"versions\": [20140701, 20140702, 20140703]" +
            "}"

    private const val invalidJsonNoDialogName =
        "{\n" +
            "  \"name\": \"|APP_INVITES_DIALOG\",\n" +
            "  \"url\": \"/connect/dialog/MPlatformAppInvitesJSDialog\",\n" +
            "  \"versions\": [20140701, 20140702, 20140703]" +
            "}"

    private const val validJsonNoFallbackUri =
        "{\n" +
            "  \"name\": \"com.facebook.platform.action.request.APPINVITES_DIALOG|APP_INVITES_DIALOG\",\n" +
            "  \"versions\": [20140701, 20140702, 20140703]" +
            "}"

    private const val validJsonNoVersions =
        "{\n" +
            "  \"name\": \"com.facebook.platform.action.request.APPINVITES_DIALOG|APP_INVITES_DIALOG\",\n" +
            "  \"url\": \"/connect/dialog/MPlatformAppInvitesJSDialog\"\n" +
            "}"

    private const val invalidJsonMultipleDelimiters =
        "{\n" +
            "  \"name\": \"com.facebook.platform.action.request.APPINVITES_DIALOG|APP_INVITES_DIALOG|anything\",\n" +
            "  \"url\": \"/connect/dialog/MPlatformAppInvitesJSDialog\"\n" +
            "}"
  }
}