/*
 * 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 java.io.ByteArrayInputStream
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.util.Random
import java.util.UUID
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
import kotlin.math.min
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert
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

@PrepareForTest(FacebookSdk::class)
class FileLruCacheTest : FacebookPowerMockTestCase() {
  companion object {
    private val random = Random()
    private val mockExecutor = FacebookSerialExecutor()
    private const val CACHE_CLEAR_TIMEOUT: Long = 100

    private fun clearFileLruCache(cache: FileLruCache) {
      // since the cache clearing happens in a separate thread, we need to wait until
      // the clear is complete before we can check for the existence of the old files
      val lock = ReentrantLock()
      val condition = lock.newCondition()
      lock.withLock {
        cache.clearCache()
        FacebookSdk.getExecutor().execute {
          lock.withLock { lock.withLock { condition.signalAll() } }
        }
        condition.await(CACHE_CLEAR_TIMEOUT, TimeUnit.MILLISECONDS)
      }
      Thread.sleep(CACHE_CLEAR_TIMEOUT)
    }

    private fun deleteLruCacheDirectory(cache: FileLruCache) {
      val directory = File(cache.location)
      directory.delete()
    }

    private fun clearAndDeleteLruCacheDirectory(cache: FileLruCache) {
      clearFileLruCache(cache)
      deleteLruCacheDirectory(cache)
    }
  }

  @Before
  fun before() {
    mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isFullyInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123456789")
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    whenever(FacebookSdk.getExecutor()).thenReturn(mockExecutor)
    val tmp = File(UUID.randomUUID().toString())
    tmp.mkdir()
    tmp.deleteOnExit()
    whenever(FacebookSdk.getCacheDir()).thenReturn(tmp)
  }

  @Test
  fun testCacheOutputStream() {
    val dataSize = 1_024
    val data = generateBytes(dataSize)
    val key = "a"

    // Limit to 2x to allow for extra header data
    val cache = FileLruCache("testCacheOutputStream", limitCacheSize(2 * dataSize))
    try {
      put(cache, key, data)
      checkValue(cache, key, data)
    } finally {
      clearAndDeleteLruCacheDirectory(cache)
    }
  }

  @Test
  fun testCacheInputStream() {
    val dataSize = 1_024
    val data = generateBytes(dataSize)
    val key = "a"
    val stream: InputStream = ByteArrayInputStream(data)

    // Limit to 2x to allow for extra header data
    val cache = FileLruCache("testCacheInputStream", limitCacheSize(2 * dataSize))
    try {
      clearFileLruCache(cache)
      val wrapped = cache.interceptAndPut(key, stream)
      consumeAndClose(wrapped)
      checkValue(cache, key, data)
    } finally {
      clearAndDeleteLruCacheDirectory(cache)
    }
  }

  @Test
  fun testCacheClear() {
    val dataSize = 1_024
    val data = generateBytes(dataSize)
    val key = "a"

    // Limit to 2x to allow for extra header data
    val cache = FileLruCache("testCacheClear", limitCacheSize(2 * dataSize))
    try {
      clearFileLruCache(cache)
      put(cache, key, data)
      checkValue(cache, key, data)
      clearFileLruCache(cache)
      Assert.assertEquals(false, hasValue(cache, key))
      Assert.assertEquals(0, cache.sizeInBytesForTest())
    } finally {
      clearAndDeleteLruCacheDirectory(cache)
    }
  }

  @Test
  fun testCacheClearMidBuffer() {
    val dataSize = 1_024
    val data = generateBytes(dataSize)
    val key = "a"
    val key2 = "b"

    // Limit to 2x to allow for extra header data
    val cache = FileLruCache("testCacheClear", limitCacheSize(2 * dataSize))
    try {
      clearFileLruCache(cache)
      put(cache, key, data)
      checkValue(cache, key, data)
      val stream = cache.openPutStream(key2)
      Thread.sleep(200)
      clearFileLruCache(cache)
      stream.write(data)
      stream.close()
      Assert.assertEquals(false, hasValue(cache, key))
      Assert.assertEquals(false, hasValue(cache, key2))
      Assert.assertEquals(0, cache.sizeInBytesForTest())
    } finally {
      clearAndDeleteLruCacheDirectory(cache)
    }
  }

  @Test
  fun testSizeInBytes() {
    val count = 17
    val dataSize = 53
    val cacheSize = count * dataSize
    val data = generateBytes(dataSize)

    // Limit to 2x to allow for extra header data
    val cache = FileLruCache("testSizeInBytes", limitCacheSize(2 * cacheSize))
    try {
      clearFileLruCache(cache)
      for (i in 0 until count) {
        put(cache, i, data)

        // The size reported by sizeInBytes includes a version/size token as well
        // as a JSON blob that records the name.  Verify that the cache size is larger
        // than the data content but not more than twice as large.  This guarantees
        // that sizeInBytes is doing at least approximately the right thing.
        val totalDataSize = (i + 1) * dataSize
        assertThat(cache.sizeInBytesForTest() > totalDataSize).isTrue
        assertThat(cache.sizeInBytesForTest() < 2 * totalDataSize).isTrue
      }
      for (i in 0 until count) {
        checkValue(cache, i.toString(), data)
      }
    } finally {
      clearAndDeleteLruCacheDirectory(cache)
    }
  }

  @Test
  fun testCacheSizeLimit() {
    val count = 64
    val dataSize = 32
    val cacheSize = count * dataSize / 2
    val data = generateBytes(dataSize)

    // Here we do not set the limit to 2x to make sure we hit the limit well before we have
    // added all the data.
    val cache = FileLruCache("testCacheSizeLimit", limitCacheSize(cacheSize))
    try {
      clearFileLruCache(cache)
      for (i in 0 until count) {
        put(cache, i, data)

        // See comment in testSizeInBytes for why this is not an exact calculation.
        //
        // This changes verification such that the final cache size lands somewhere
        // between half and full quota.
        val totalDataSize = (i + 1) * dataSize
        assertThat(cache.sizeInBytesForTest() > min(totalDataSize, cacheSize / 2)).isTrue
        assertThat(cache.sizeInBytesForTest() < min(2 * totalDataSize, cacheSize)).isTrue
      }

      // sleep for a bit to make sure the trim finishes
      Thread.sleep(200)

      // Verify that some keys exist and others do not
      var hasValueExists = false
      var hasNoValueExists = false
      for (i in 0 until count) {
        val key = Integer.valueOf(i).toString()
        if (hasValue(cache, key)) {
          hasValueExists = true
          checkValue(cache, key, data)
        } else {
          hasNoValueExists = true
        }
      }
      Assert.assertEquals(true, hasValueExists)
      Assert.assertEquals(true, hasNoValueExists)
    } finally {
      clearAndDeleteLruCacheDirectory(cache)
    }
  }

  @Test
  fun testCacheCountLimit() {
    val count = 64
    val dataSize = 32
    val cacheCount = count / 2
    val data = generateBytes(dataSize)

    // Here we only limit by count, and we allow half of the entries.
    val cache = FileLruCache("testCacheCountLimit", limitCacheCount(cacheCount))
    try {
      clearFileLruCache(cache)
      for (i in 0 until count) {
        put(cache, i, data)
      }

      // sleep for a bit to make sure the trim finishes
      Thread.sleep(200)

      // Verify that some keys exist and others do not
      var hasValueExists = false
      var hasNoValueExists = false
      for (i in 0 until count) {
        if (hasValue(cache, i)) {
          hasValueExists = true
          checkValue(cache, i, data)
        } else {
          hasNoValueExists = true
        }
      }
      Assert.assertEquals(true, hasValueExists)
      Assert.assertEquals(true, hasNoValueExists)
    } finally {
      clearAndDeleteLruCacheDirectory(cache)
    }
  }

  @Test
  fun testCacheLru() {
    val keepCount = 10
    val otherCount = 5
    val dataSize = 64
    val data = generateBytes(dataSize)

    // Limit by count, and allow all the keep keys plus one other.
    val cache = FileLruCache("testCacheLru", limitCacheCount(keepCount + 1))
    try {
      clearFileLruCache(cache)
      for (i in 0 until keepCount) {
        put(cache, i, data)
      }
      for (i in 0 until otherCount) {
        put(cache, keepCount + i, data)
        Thread.sleep(1_000)

        // By verifying all the keep keys, they should be LRU and survive while the others do not.
        for (keepIndex in 0 until keepCount) {
          checkValue(cache, keepIndex, data)
        }
      }

      // All but the last other key should have been pushed out
      for (i in 0 until otherCount - 1) {
        val key = (keepCount + i).toString()
        Assert.assertEquals(false, hasValue(cache, key))
      }
    } finally {
      clearAndDeleteLruCacheDirectory(cache)
    }
  }

  @Test
  fun testConcurrentWritesToSameKey() {
    val count = 5
    val dataSize = 81
    val threadCount = 31
    val iterationCount = 10
    val data = generateBytes(dataSize)
    val cache = FileLruCache("testConcurrentWritesToSameKey", limitCacheCount(count + 1))
    try {
      clearFileLruCache(cache)
      val run = Runnable {
        repeat(iterationCount) {
          for (i in 0 until count) {
            put(cache, i, data)
          }
        }
      }

      val threads = (0 until threadCount).map { Thread(run) }
      for (thread in threads) {
        thread.start()
      }
      for (thread in threads) {
        thread.join(10 * 1_000L, 0)
      }

      // Verify that the file state ended up consistent in the end
      for (i in 0 until count) {
        checkValue(cache, i, data)
      }
    } finally {
      clearAndDeleteLruCacheDirectory(cache)
    }
  }

  private fun generateBytes(n: Int): ByteArray {
    val bytes = ByteArray(n)
    random.nextBytes(bytes)
    return bytes
  }

  private fun limitCacheSize(n: Int): FileLruCache.Limits {
    val limits = FileLruCache.Limits()
    limits.byteCount = n
    return limits
  }

  private fun limitCacheCount(n: Int): FileLruCache.Limits {
    val limits = FileLruCache.Limits()
    limits.fileCount = n
    return limits
  }

  private fun put(cache: FileLruCache, i: Int, data: ByteArray) {
    put(cache, i.toString(), data)
  }

  private fun put(cache: FileLruCache, key: String, data: ByteArray) {
    try {
      val stream = cache.openPutStream(key)
      Assert.assertNotNull(stream)
      stream.write(data)
      stream.close()
    } catch (e: IOException) {
      // Fail test and print Exception
      Assert.assertNull(e)
    }
  }

  private fun checkValue(cache: FileLruCache, i: Int, expected: ByteArray) {
    checkValue(cache, i.toString(), expected)
  }

  private fun checkValue(cache: FileLruCache, key: String, expected: ByteArray) {
    try {
      val stream = cache[key]
      Assert.assertNotNull(stream)
      checkNotNull(stream)
      checkInputStream(expected, stream)
      stream.close()
    } catch (e: IOException) {
      // Fail test and print Exception
      Assert.assertNull(e)
    }
  }

  private fun hasValue(cache: FileLruCache, i: Int): Boolean {
    return hasValue(cache, i.toString())
  }

  private fun hasValue(cache: FileLruCache, key: String): Boolean {
    var stream: InputStream? = null
    try {
      stream = cache[key]
    } catch (e: IOException) {
      // Fail test and print Exception
      Assert.assertNull(e)
    }
    return stream != null
  }

  private fun checkInputStream(expected: ByteArray, actual: InputStream) {
    try {
      for (i in expected.indices) {
        val b = actual.read()
        Assert.assertEquals(expected[i].toInt() and 0xff, b)
      }
      val eof = actual.read()
      Assert.assertEquals(-1, eof)
    } catch (e: IOException) {
      // Fail test and print Exception
      Assert.assertNull(e)
    }
  }

  private fun consumeAndClose(stream: InputStream) {
    try {
      val buffer = ByteArray(1_024)
      while (stream.read(buffer) > -1) {
        // these bytes intentionally ignored
      }
      stream.close()
    } catch (e: IOException) {
      // Fail test and print Exception
      Assert.assertNull(e)
    }
  }
}


/*
 * 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 android.net.Uri
import com.facebook.FacebookPowerMockTestCase
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.eq
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito.mock
import org.powermock.api.mockito.PowerMockito.whenNew
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(
    ImageDownloader::class,
    ImageDownloader.DownloaderContext::class,
    ImageDownloader.RequestKey::class,
    WorkQueue.WorkItem::class)
class ImageDownloaderTest : FacebookPowerMockTestCase() {
  private lateinit var mockRequest: ImageRequest
  private lateinit var mockWorkItem: WorkQueue.WorkItem
  private lateinit var mockDownloaderContext: ImageDownloader.DownloaderContext
  private lateinit var mockRequestKey: ImageDownloader.RequestKey
  private lateinit var mockContext: Context
  private val url = Uri.parse("https://graph.facebook.com/handle")
  private val tag = "tag"

  @Before
  fun init() {
    mockRequest = mock(ImageRequest::class.java)
    mockContext = mock(Context::class.java)

    whenever(mockRequest.imageUri).thenReturn(url)
    whenever(mockRequest.callerTag).thenReturn(tag)
    whenever(mockRequest.isCachedRedirectAllowed).thenReturn(true)
    whenever(mockRequest.context).thenReturn(mockContext)

    mockWorkItem = mock(WorkQueue.WorkItem::class.java)
    whenever(mockWorkItem.cancel()).thenReturn(false)

    mockDownloaderContext = mock(ImageDownloader.DownloaderContext::class.java)
    whenNew(ImageDownloader.DownloaderContext::class.java)
        .withArguments(eq(mockRequest))
        .thenReturn(mockDownloaderContext)
    mockDownloaderContext.workItem = mockWorkItem

    mockRequestKey = mock(ImageDownloader.RequestKey::class.java)
    whenNew(ImageDownloader.RequestKey::class.java)
        .withArguments(eq(url), eq(tag))
        .thenReturn(mockRequestKey)
    mockRequestKey.uri = url
    mockRequestKey.tag = tag
  }

  // @Test - DISABLED
  // This test causes very frequent failures on GitHub Actions
  // The line that fails is in the second block where
  // pendingRequest.size is expeted to be 1 but is actually 0
  fun `test remove before and after test adding request`() {
    // remove request without adding request
    var isCancelled = ImageDownloader.cancelRequest(mockRequest)
    assertThat(isCancelled).isFalse
    var pendingRequest = ImageDownloader.getPendingRequests()
    assertEquals(0, pendingRequest.size)

    ImageDownloader.downloadAsync(mockRequest)
    pendingRequest = ImageDownloader.getPendingRequests()
    assertEquals(1, pendingRequest.size)

    var (key, downloaderContext) = pendingRequest.entries.first()
    assertEquals(mockRequestKey, key)
    assertEquals(mockDownloaderContext, downloaderContext)

    // cancel request
    isCancelled = ImageDownloader.cancelRequest(mockRequest)
    assertTrue(isCancelled)
    pendingRequest = ImageDownloader.getPendingRequests()
    assertEquals(1, pendingRequest.size)

    val entry = pendingRequest.entries.first()
    key = entry.key
    downloaderContext = entry.value
    assertEquals(mockRequestKey, key)
    assertEquals(mockDownloaderContext, downloaderContext)
  }
}


/*
 * 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.util.common.assertThrows
import java.net.URLEncoder
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
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 ImageRequestTest : FacebookPowerMockTestCase() {

  private val heightParam = "height"
  private val widthParam = "width"
  private val accessTokenParam = "access_token"
  private val migrationParam = "migration_overrides"

  private val apiVersion = "xyz"
  private val appId = "9999"
  private val clientToken = "abcd"
  private val accessToken = "accessToken"
  private val userId = "0000"
  private val height = 12
  private val width = 34
  private val migrateValueEncoded = URLEncoder.encode("{october_2012:true}", "utf-8")

  @Before
  fun init() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getGraphApiVersion()).thenReturn(apiVersion)
  }

  @Test
  fun `all valid ok`() {
    val uri = ImageRequest.getProfilePictureUri(userId, width, height, accessToken)
    val expectedUri =
        "https://graph.null/$apiVersion/$userId/picture?height=$height&width=$width&migration_overrides=$migrateValueEncoded&access_token=$accessToken"

    assertNotNull(uri)
    assertNotNull(uri.path)
    assertNotNull(uri.getQueryParameter(widthParam))
    assertNotNull(uri.getQueryParameter(heightParam))
    assertNotNull(uri.getQueryParameter(accessTokenParam))
    assertNotNull(uri.getQueryParameter(migrationParam))
    assertEquals(expectedUri, uri.toString())
  }

  @Test
  fun `all valid ok no access token`() {
    whenever(FacebookSdk.getClientToken()).thenReturn(clientToken)
    whenever(FacebookSdk.getApplicationId()).thenReturn(appId)
    val uri = ImageRequest.getProfilePictureUri(userId, width, height)
    val expectedToken = URLEncoder.encode("$appId|$clientToken", "utf-8")
    val expectedUri =
        "https://graph.null/$apiVersion/$userId/picture?height=$height&width=$width&migration_overrides=$migrateValueEncoded&access_token=$expectedToken"

    assertNotNull(uri)
    assertNotNull(uri.path)
    assertNotNull(uri.getQueryParameter(widthParam))
    assertNotNull(uri.getQueryParameter(heightParam))
    assertNotNull(uri.getQueryParameter(accessTokenParam))
    assertNotNull(uri.getQueryParameter(migrationParam))
    assertEquals(expectedUri, uri.toString())
  }

  @Test
  fun `valid no client token`() {
    whenever(FacebookSdk.getClientToken()).thenReturn("")
    whenever(FacebookSdk.getApplicationId()).thenReturn(appId)
    val uri = ImageRequest.getProfilePictureUri(userId, width, height, "")
    val expectedUri =
        "https://graph.null/$apiVersion/$userId/picture?height=$height&width=$width&migration_overrides=$migrateValueEncoded"

    assertNotNull(uri)
    assertNotNull(uri.path)
    assertNotNull(uri.getQueryParameter(widthParam))
    assertNotNull(uri.getQueryParameter(heightParam))
    assertNull(uri.getQueryParameter(accessTokenParam))
    assertNotNull(uri.getQueryParameter(migrationParam))
    assertEquals(expectedUri, uri.toString())
  }

  @Test
  fun `valid no application id`() {
    whenever(FacebookSdk.getClientToken()).thenReturn(clientToken)
    whenever(FacebookSdk.getApplicationId()).thenReturn("")
    val uri = ImageRequest.getProfilePictureUri(userId, width, height, "")
    val expectedUri =
        "https://graph.null/$apiVersion/$userId/picture?height=$height&width=$width&migration_overrides=$migrateValueEncoded"

    assertNotNull(uri)
    assertNotNull(uri.path)
    assertNotNull(uri.getQueryParameter(widthParam))
    assertNotNull(uri.getQueryParameter(heightParam))
    assertNull(uri.getQueryParameter(accessTokenParam))
    assertNotNull(uri.getQueryParameter(migrationParam))
    assertEquals(expectedUri, uri.toString())
  }

  @Test
  fun `valid no height`() {
    val uri = ImageRequest.getProfilePictureUri(userId, width, 0, accessToken)
    val expectedUri =
        "https://graph.null/$apiVersion/$userId/picture?width=$width&migration_overrides=$migrateValueEncoded&access_token=$accessToken"

    assertNotNull(uri)
    assertNotNull(uri.path)
    assertNotNull(uri.getQueryParameter(widthParam))
    assertNull(uri.getQueryParameter(heightParam))
    assertNotNull(uri.getQueryParameter(accessTokenParam))
    assertNotNull(uri.getQueryParameter(migrationParam))
    assertEquals(expectedUri, uri.toString())
  }

  @Test
  fun `valid no width`() {
    val uri = ImageRequest.getProfilePictureUri(userId, 0, height, accessToken)
    val expectedUri =
        "https://graph.null/$apiVersion/$userId/picture?height=$height&migration_overrides=$migrateValueEncoded&access_token=$accessToken"

    assertNotNull(uri)
    assertNotNull(uri.path)
    assertNull(uri.getQueryParameter(widthParam))
    assertNotNull(uri.getQueryParameter(heightParam))
    assertNotNull(uri.getQueryParameter(accessTokenParam))
    assertNotNull(uri.getQueryParameter(migrationParam))
    assertEquals(expectedUri, uri.toString())
  }

  @Test
  fun `exception when no height and no width`() {
    assertThrows<IllegalArgumentException> {
      ImageRequest.getProfilePictureUri(userId, 0, 0, accessToken)
    }
  }

  @Test
  fun `all valid ok builder`() {
    val profilePictureUri = ImageRequest.getProfilePictureUri(userId, width, height, accessToken)
    val imageRequestBuilder =
        ImageRequest.Builder(ApplicationProvider.getApplicationContext(), profilePictureUri)
    val callBack = PowerMockito.mock(ImageRequest.Callback::class.java)
    val callerTag = "1234abc"
    val imageRequest =
        imageRequestBuilder
            .setAllowCachedRedirects(true)
            .setCallback(callBack)
            .setCallerTag(callerTag)
            .build()

    assertEquals(ApplicationProvider.getApplicationContext(), imageRequest.context)
    assertEquals(profilePictureUri, imageRequest.imageUri)
    assertEquals(true, imageRequest.isCachedRedirectAllowed)
    assertEquals(callBack, imageRequest.callback)
    assertEquals(callerTag, imageRequest.callerTag)
  }
}


/*
 * 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 com.facebook.FacebookSdk
import java.io.File
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import java.util.UUID
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.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(FacebookSdk::class)
class ImageResponseCacheTest : FacebookPowerMockTestCase() {
  private lateinit var mockCache: FileLruCache
  private lateinit var mockInputStream: InputStream
  private lateinit var mockHttpURLConnection: HttpURLConnection
  private lateinit var mockCacheDir: File

  private val url = "https://graph.facebook.com/handle"
  private val cdnUrl = "http://fake-dfs.fbcdn.net/handle"
  private val noCachedCdnUrl = "http://fake-dfs.fbcdn.net/handle-no-cahce"

  @Before
  fun init() {
    mockCacheDir = File(UUID.randomUUID().toString())
    mockCacheDir.deleteOnExit()
    mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getCacheDir()).thenReturn(mockCacheDir)

    mockCache = mock()
    mockInputStream = mock()
    mockHttpURLConnection = mock()

    whenever(mockCache.get(cdnUrl)).thenReturn(mockInputStream)
    whenever(mockCache.interceptAndPut(any(), any())).thenReturn(mockInputStream)
    whenever(mockHttpURLConnection.url).thenReturn(URL(cdnUrl))

    Whitebox.setInternalState(ImageResponseCache::class.java, "imageCache", mockCache)
  }

  @Test
  fun `test get cache`() {
    val imageCache = ImageResponseCache.getCache()
    assertThat(imageCache).isEqualTo(mockCache)
  }

  @Test
  fun `test get cached image stream from invalid url`() {
    val uri: Uri = Uri.parse(url)
    val stream = ImageResponseCache.getCachedImageStream(uri)
    assertThat(stream).isNull()
  }

  @Test
  fun `test get cached image stream from cdn url`() {
    val uri: Uri = Uri.parse(cdnUrl)
    val stream = ImageResponseCache.getCachedImageStream(uri)
    assertThat(stream).isEqualTo(mockInputStream)
  }

  @Test
  fun `test get non-cached image stream from cdn url`() {
    val uri: Uri = Uri.parse(noCachedCdnUrl)
    val stream = ImageResponseCache.getCachedImageStream(uri)
    assertThat(stream).isNull()
  }

  @Test
  fun `test intercept and cache image`() {
    whenever(mockHttpURLConnection.responseCode).thenReturn(HttpURLConnection.HTTP_OK)
    val stream = ImageResponseCache.interceptAndCacheImageStream(mockHttpURLConnection)
    assertThat(stream).isEqualTo(mockInputStream)
  }

  @Test
  fun `test intercept and cache image with bad connection`() {
    whenever(mockHttpURLConnection.responseCode).thenReturn(HttpURLConnection.HTTP_BAD_REQUEST)
    val stream = ImageResponseCache.interceptAndCacheImageStream(mockHttpURLConnection)
    assertThat(stream).isNull()
  }

  @Test
  fun `test clearing cache will clear the base file cache`() {
    ImageResponseCache.clearCache()
    verify(mockCache).clearCache()
  }

  @Test
  fun `test get cache will create a FileLruCache`() {
    Whitebox.setInternalState(ImageResponseCache::class.java, "imageCache", null as FileLruCache?)
    val cache = ImageResponseCache.getCache()
    assertThat(cache).isNotNull
    assertThat(mockCacheDir.isDirectory).isTrue
    mockCacheDir.deleteRecursively()
  }
}


/*
 * 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.android.installreferrer.api.InstallReferrerClient
import com.android.installreferrer.api.InstallReferrerStateListener
import com.android.installreferrer.api.ReferrerDetails
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.MockSharedPreference
import com.facebook.util.common.anyObject
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert.assertEquals
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, InstallReferrerClient::class, InstallReferrerClient.Builder::class)
class InstallReferrerUtilTest : FacebookPowerMockTestCase() {
  private lateinit var mockApplicationContext: Context
  private lateinit var mockInstallReferrerClient: InstallReferrerClient
  private lateinit var mockInstallReferrerClientBuilder: InstallReferrerClient.Builder
  private lateinit var mockReferrerDetails: ReferrerDetails
  private lateinit var mockSharedPreference: MockSharedPreference
  private val emptyCallback =
      object : InstallReferrerUtil.Callback {
        override fun onReceiveReferrerUrl(s: String?) = Unit
      }

  @Before
  fun init() {
    mockSharedPreference = MockSharedPreference()
    mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isFullyInitialized()).thenReturn(true)
    mockApplicationContext = mock(Context::class.java)
    whenever(
            mockApplicationContext.getSharedPreferences(
                FacebookSdk.APP_EVENT_PREFERENCES, Context.MODE_PRIVATE))
        .thenReturn(mockSharedPreference)
    whenever(FacebookSdk.getApplicationContext()).thenReturn(mockApplicationContext)

    mockReferrerDetails = mock(ReferrerDetails::class.java)
    mockInstallReferrerClient = mock(InstallReferrerClient::class.java)
    whenever(mockInstallReferrerClient.installReferrer).thenReturn(mockReferrerDetails)
    mockInstallReferrerClientBuilder = mock(InstallReferrerClient.Builder::class.java)
    whenever(mockInstallReferrerClientBuilder.build()).thenReturn(mockInstallReferrerClient)
    mockStatic(InstallReferrerClient::class.java)
    whenever(InstallReferrerClient.newBuilder(mockApplicationContext))
        .thenReturn(mockInstallReferrerClientBuilder)
  }

  @Test
  fun `test connection with fb referer return`() {
    var connectionCounter = 0
    var didReceivedReferrerUrl = false
    val referrerUrl = "facebook.com"
    whenever(mockReferrerDetails.installReferrer).thenReturn(referrerUrl)
    whenever(mockInstallReferrerClient.startConnection(anyObject())).then {
      connectionCounter += 1
      val listener = it.getArgument<InstallReferrerStateListener>(0)
      listener.onInstallReferrerSetupFinished(InstallReferrerClient.InstallReferrerResponse.OK)
      return@then null
    }

    val callback =
        object : InstallReferrerUtil.Callback {
          override fun onReceiveReferrerUrl(s: String?) {
            assertEquals(s, referrerUrl)
            didReceivedReferrerUrl = true
          }
        }

    InstallReferrerUtil.tryUpdateReferrerInfo(callback)
    assertEquals(
        1,
        connectionCounter,
    )
    assertThat(didReceivedReferrerUrl).isTrue
  }

  @Test
  fun `test connection twice`() {
    var connectionCounter = 0
    whenever(mockInstallReferrerClient.startConnection(anyObject())).then {
      connectionCounter += 1
      val listener = it.getArgument<InstallReferrerStateListener>(0)
      listener.onInstallReferrerSetupFinished(InstallReferrerClient.InstallReferrerResponse.OK)
      return@then null
    }
    InstallReferrerUtil.tryUpdateReferrerInfo(emptyCallback)
    InstallReferrerUtil.tryUpdateReferrerInfo(emptyCallback)
    assertEquals(1, connectionCounter)
  }

  @Test
  fun `test service unavailable`() {
    var connectionCounter = 0
    whenever(mockInstallReferrerClient.startConnection(anyObject())).then {
      connectionCounter += 1
      val listener = it.getArgument<InstallReferrerStateListener>(0)
      listener.onInstallReferrerSetupFinished(
          InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE)
      return@then null
    }
    repeat(3) { InstallReferrerUtil.tryUpdateReferrerInfo(emptyCallback) }
    assertEquals(3, connectionCounter)
  }
}


/*
 * 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 com.facebook.FacebookSdk
import com.facebook.util.common.CaptureExecutor
import java.util.concurrent.Callable
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert
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

object ReturnTrue : Callable<Boolean> {
  override fun call() = true
}

object ReturnNull : Callable<Object> {
  override fun call() = null
}

@PrepareForTest(FacebookSdk::class)
class LockOnGetVariableTest : FacebookPowerMockTestCase() {
  private lateinit var executor: CaptureExecutor
  @Before
  fun init() {
    executor = CaptureExecutor()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getExecutor()).thenReturn(executor)
  }

  @Test
  fun `test wait for value`() {
    val lock = LockOnGetVariable(ReturnTrue)
    var fetchValue = false
    val thread = Thread { fetchValue = lock.value ?: false }
    thread.start()
    assertThat(fetchValue).isFalse
    Assert.assertNotNull(executor.capturedRunnable)
    executor.capturedRunnable?.run()

    // wait for at most 10 second before checking the result
    thread.join(10_000)
    assertThat(fetchValue).isTrue
  }

  @Test
  fun `test null value`() {
    val lock = LockOnGetVariable(ReturnNull)
    var fetchValue: Object? = Object()
    val thread = Thread { fetchValue = lock.value }
    thread.start()
    Assert.assertNotNull(fetchValue)
    Assert.assertNotNull(executor.capturedRunnable)
    executor.capturedRunnable?.run()

    // wait for at most 10 second before checking the result
    thread.join(10_000)
    Assert.assertNull(fetchValue)
  }
}


/*
 * 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.util.Log
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.LoggingBehavior
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.eq
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class, Log::class)
class LoggerTest : FacebookPowerMockTestCase() {
  private val tag = "TEST_TAG"
  private val content = "test content"
  private val fbToken = "fb_token"
  private val igToken = "ig_token"
  private val contentWithFBToken = "{\"access_token\":\"$fbToken\", \"content\":\"Facebook\"}"
  private val contentWithIGToken = "{\"access_token\":\"$igToken\", \"content\":\"Instagram\"}"

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

    whenever(FacebookSdk.isLoggingBehaviorEnabled(LoggingBehavior.APP_EVENTS)).thenReturn(true)
    whenever(FacebookSdk.isLoggingBehaviorEnabled(LoggingBehavior.GRAPH_API_DEBUG_INFO))
        .thenReturn(false)

    Logger.registerAccessToken(fbToken)
    Logger.registerStringToReplace(igToken, "removed")
  }

  @Test
  fun `test easy log`() {
    var callCount = 0
    whenever(Log.println(eq(Log.DEBUG), eq(Logger.LOG_TAG_BASE + tag), eq(content))).then {
      callCount++
    }
    Logger.log(LoggingBehavior.APP_EVENTS, tag, content)
    assertEquals(1, callCount)
  }

  @Test
  fun `test format log`() {
    var callCount = 0
    whenever(Log.println(eq(Log.DEBUG), eq(Logger.LOG_TAG_BASE + tag), eq(content))).then {
      callCount++
    }
    Logger.log(LoggingBehavior.APP_EVENTS, tag, "%s %s", "test", "content")
    assertEquals(1, callCount)
  }

  @Test
  fun `test disable behaviour log`() {
    var callCount = 0
    whenever(Log.println(eq(Log.DEBUG), eq(Logger.LOG_TAG_BASE + tag), eq(content))).then {
      callCount++
    }
    Logger.log(LoggingBehavior.GRAPH_API_DEBUG_INFO, tag, content)
    assertEquals(0, callCount)
  }

  @Test
  fun `test register access token`() {
    var callCount = 0
    val newContent = contentWithFBToken.replace(fbToken, "ACCESS_TOKEN_REMOVED")

    whenever(Log.println(eq(Log.DEBUG), eq(Logger.LOG_TAG_BASE + tag), eq(newContent))).then {
      callCount++
    }

    Logger.log(LoggingBehavior.APP_EVENTS, tag, contentWithFBToken)
    assertEquals(1, callCount)
  }

  @Test
  fun `test register string`() {
    var callCount = 0
    val newContent = contentWithIGToken.replace(igToken, "removed")

    whenever(Log.println(eq(Log.DEBUG), eq(Logger.LOG_TAG_BASE + tag), eq(newContent))).then {
      callCount++
    }
    Logger.log(LoggingBehavior.APP_EVENTS, tag, contentWithIGToken)
    assertEquals(1, callCount)
  }

  @Test(expected = IllegalArgumentException::class)
  fun `test invalid priority`() {
    val logger = Logger(LoggingBehavior.APP_EVENTS, tag)
    logger.priority = 0
  }

  @Test
  fun `test new logger`() {
    var callCount = 0
    var newContent = contentWithFBToken.replace(fbToken, "ACCESS_TOKEN_REMOVED")

    val logger = Logger(LoggingBehavior.APP_EVENTS, tag)
    logger.append(contentWithFBToken)
    assertEquals(newContent, logger.getContents())

    newContent = newContent + String.format("  %s:\t%s\n", "key", "value")
    logger.appendKeyValue("key", "value")
    assertEquals(newContent, logger.getContents())

    whenever(Log.println(eq(Log.DEBUG), eq(Logger.LOG_TAG_BASE + tag), eq(newContent))).then {
      callCount++
    }

    logger.log()
    assertEquals(1, callCount)
  }

  @Test
  fun `test log multiple time`() {
    var callCount = 0
    var newContent = contentWithFBToken.replace(fbToken, "ACCESS_TOKEN_REMOVED")

    val logger = Logger(LoggingBehavior.APP_EVENTS, tag)
    logger.append(contentWithFBToken)
    assertEquals(newContent, logger.getContents())

    whenever(Log.println(eq(Log.DEBUG), eq(Logger.LOG_TAG_BASE + tag), eq(newContent))).then {
      callCount++
    }

    logger.log()
    assertEquals(1, callCount)

    assertEquals("", logger.getContents())
    logger.log()
    assertEquals(1, callCount)
  }
}


/*
 * 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.graphics.Bitmap
import android.net.Uri
import androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookException
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import java.io.File
import java.io.FileNotFoundException
import java.util.UUID
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
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 NativeAppCallAttachmentStoreTest : FacebookPowerMockTestCase() {
  private lateinit var callId: UUID
  private lateinit var testDirectory: File

  companion object {
    private const val ATTACHMENT_NAME = "hello"
    private const val UNEXPECTED_ATTACHMENT_URI = "test://nothissiteintheworld.test/abcd"
    private const val CONTENT_URI = "content://testcontent.test/1234"
    private const val MEDIA_URI = "content://media/testcontent.test/1234"
  }

  @Before
  fun init() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.getApplicationId()).thenReturn("123456789")
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    NativeAppCallAttachmentStore.ensureAttachmentsDirectoryExists()

    callId = UUID.randomUUID()
    testDirectory = File(UUID.randomUUID().toString())
    testDirectory.mkdir()
  }

  @After
  fun clean() {
    NativeAppCallAttachmentStore.cleanupAllAttachments()
    testDirectory.deleteRecursively()
  }

  @Test
  fun `test add attachments of bitmap`() {
    val attachment = NativeAppCallAttachmentStore.createAttachment(callId, createBitmap())
    val attachmentUrl = attachment.attachmentUrl
    assertThat(attachmentUrl).isNotNull
    NativeAppCallAttachmentStore.addAttachments(listOf(attachment))

    // Url format: base/call_id/filename
    val attachmentFilename = attachmentUrl.split("/").last()
    val retrievedAttachment =
        NativeAppCallAttachmentStore.getAttachmentFile(callId, attachmentFilename, false)
    checkNotNull(retrievedAttachment)
    assertThat(retrievedAttachment.exists()).isTrue
  }

  @Test(expected = FacebookException::class)
  fun `test create attachments with unexpected uri`() {
    NativeAppCallAttachmentStore.createAttachment(callId, Uri.parse(UNEXPECTED_ATTACHMENT_URI))
  }

  @Test
  fun `test create attachments with content uri`() {
    val attachment = NativeAppCallAttachmentStore.createAttachment(callId, Uri.parse(CONTENT_URI))
    assertThat(attachment.originalUri.toString()).isEqualTo(CONTENT_URI)
  }

  @Test
  fun `test create attachments with media uri`() {
    val attachment = NativeAppCallAttachmentStore.createAttachment(callId, Uri.parse(MEDIA_URI))
    assertThat(attachment.originalUri.toString()).isEqualTo(MEDIA_URI)
    assertThat(attachment.attachmentUrl).isEqualTo(MEDIA_URI)
  }

  @Test
  fun `test get attachments directory`() {
    val dir = NativeAppCallAttachmentStore.getAttachmentsDirectory()
    checkNotNull(dir)
    assertThat(dir.canonicalPath).contains(NativeAppCallAttachmentStore.ATTACHMENTS_DIR_NAME)
  }

  @Test
  fun `test get attachments directory for call`() {
    val dir = NativeAppCallAttachmentStore.getAttachmentsDirectoryForCall(callId, false)
    checkNotNull(dir)
    assertThat(dir.canonicalPath).contains(NativeAppCallAttachmentStore.ATTACHMENTS_DIR_NAME)
    assertThat(dir.canonicalPath).contains(callId.toString())
  }

  @Test
  fun `test get attachment file`() {
    val dir = NativeAppCallAttachmentStore.getAttachmentFile(callId, ATTACHMENT_NAME, false)
    checkNotNull(dir)
    assertThat(dir.canonicalPath).contains(NativeAppCallAttachmentStore.ATTACHMENTS_DIR_NAME)
    assertThat(dir.canonicalPath).contains(callId.toString())
    assertThat(dir.canonicalPath).contains(ATTACHMENT_NAME)
  }

  @Test
  fun `test adding empty and null attachment list won't crash`() {
    NativeAppCallAttachmentStore.addAttachments(null)
    NativeAppCallAttachmentStore.addAttachments(listOf())
  }

  @Test
  fun `test adding an attachment from url file`() {
    // create attachment input file
    val inputFile = File(testDirectory.path + "/input")
    with(inputFile.outputStream()) {
      this.write(7)
      this.close()
    }
    val attachment = NativeAppCallAttachmentStore.createAttachment(callId, Uri.fromFile(inputFile))

    // add attachment to the store
    NativeAppCallAttachmentStore.addAttachments(listOf(attachment))
    val retrievedAttachmentFile =
        NativeAppCallAttachmentStore.openAttachment(callId, attachment.attachmentName)
    checkNotNull(retrievedAttachmentFile)
    with(retrievedAttachmentFile.inputStream()) {
      val data = this.read()
      assertThat(data).isEqualTo(7)
    }
  }

  @Test(expected = FacebookException::class)
  fun `test adding an attachment from a non-exist file will throw an exception`() {
    val inputFile = File(testDirectory.path, "input")
    val attachment = NativeAppCallAttachmentStore.createAttachment(callId, Uri.fromFile(inputFile))

    NativeAppCallAttachmentStore.addAttachments(listOf(attachment))
  }

  @Test(expected = FileNotFoundException::class)
  fun `test open an attachment of the empty name will throw an exception`() {
    NativeAppCallAttachmentStore.openAttachment(callId, "")
  }

  @Test
  fun `test cleaning up attachment will delete added attachments`() {
    val attachment = NativeAppCallAttachmentStore.createAttachment(callId, createBitmap())
    NativeAppCallAttachmentStore.addAttachments(listOf(attachment))
    checkNotNull(
        NativeAppCallAttachmentStore.getAttachmentFile(callId, attachment.attachmentName, false))

    NativeAppCallAttachmentStore.cleanupAttachmentsForCall(callId)

    val retrievedAttachmentFile =
        NativeAppCallAttachmentStore.getAttachmentFile(callId, attachment.attachmentName, false)
    assertThat(retrievedAttachmentFile?.exists()).isFalse
  }

  private fun createBitmap(): Bitmap {
    return Bitmap.createBitmap(20, 20, Bitmap.Config.ALPHA_8)
  }
}


/*
 * 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 android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
import android.os.Bundle
import com.facebook.FacebookException
import com.facebook.FacebookOperationCanceledException
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.login.DefaultAudience
import com.facebook.util.common.AuthenticationTokenTestUtil
import java.util.TreeSet
import java.util.UUID
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.core.classloader.annotations.PrepareForTest

@PrepareForTest(FacebookSdk::class, FacebookSignatureValidator::class)
class NativeProtocolTest : FacebookPowerMockTestCase() {

  private val mockAppID = "123456789"
  private val mockPackageName = "com.example"
  private lateinit var mockCallId: String
  private lateinit var mockNativeAppInfo: NativeProtocol.NativeAppInfo

  override fun setup() {
    super.setup()
    mockNativeAppInfo = mock()
    val mockAvailableVersions = TreeSet<Int>()
    mockAvailableVersions.add(NativeProtocol.PROTOCOL_VERSION_20210906)
    whenever(mockNativeAppInfo.getAvailableVersions()).thenReturn(mockAvailableVersions)
    whenever(mockNativeAppInfo.getPackage()).thenReturn("com.facebook.test")
    whenever(mockNativeAppInfo.getLoginActivity()).thenReturn("com.facebook.test.LoginActivity")
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn(mockAppID)
    whenever(FacebookSdk.getApplicationName()).thenReturn(mockPackageName)
    mockCallId = UUID.randomUUID().toString()
  }

  @Test
  fun `sdk version older than app with version spec open`() {
    // Base case where a feature was enabled a while ago and the SDK and Native app have been
    // updated since then.
    val versionSpec = intArrayOf(3)
    val latestSdkVersion = 7
    val availableFbAppVersions = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8)

    val resultVersion =
        NativeProtocol.computeLatestAvailableVersionFromVersionSpec(
            getTreeSetFromIntArray(availableFbAppVersions), latestSdkVersion, versionSpec)

    assertThat(resultVersion).isEqualTo(7)
  }

  @Test
  fun `sdk version newer than app with version spec open`() {
    // Base case where a feature was enabled a while ago and the SDK and Native app have been
    // updated since then.
    val versionSpec = intArrayOf(3)
    val latestSdkVersion = 8
    val availableFbAppVersions = intArrayOf(1, 2, 3, 4, 5, 6, 7)

    val resultVersion =
        NativeProtocol.computeLatestAvailableVersionFromVersionSpec(
            getTreeSetFromIntArray(availableFbAppVersions), latestSdkVersion, versionSpec)

    assertThat(resultVersion).isEqualTo(7)
  }

  @Test
  fun `sdk version older than app with version spec disabled`() {
    // Case where a feature was enabled AND disabled a while ago and the SDK and Native app have
    // been updated since then.
    val versionSpec = intArrayOf(1, 3, 7, 8)
    val latestSdkVersion = 7
    val availableFbAppVersions = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8)

    val resultVersion =
        NativeProtocol.computeLatestAvailableVersionFromVersionSpec(
            getTreeSetFromIntArray(availableFbAppVersions), latestSdkVersion, versionSpec)

    assertThat(resultVersion).isEqualTo(NativeProtocol.NO_PROTOCOL_AVAILABLE)
  }

  @Test
  fun `sdk version newer than app with version spec disabled`() {
    // Case where a feature was enabled AND disabled a while ago and the SDK and Native app have
    // been updated since then.
    val versionSpec = intArrayOf(1, 3, 6, 7)
    val latestSdkVersion = 8
    val availableFbAppVersions = intArrayOf(1, 2, 3, 4, 5, 6, 7)

    val resultVersion =
        NativeProtocol.computeLatestAvailableVersionFromVersionSpec(
            getTreeSetFromIntArray(availableFbAppVersions), latestSdkVersion, versionSpec)

    assertThat(resultVersion).isEqualTo(NativeProtocol.NO_PROTOCOL_AVAILABLE)
  }

  @Test
  fun `test sdk version older than app with version spec newer and enabled`() {
    // Case where the sdk and app are older, but the app is still enabled
    val versionSpec = intArrayOf(1, 3, 7, 9, 10, 11, 12, 13)
    val latestSdkVersion = 7
    val availableFbAppVersions = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8)

    val resultVersion =
        NativeProtocol.computeLatestAvailableVersionFromVersionSpec(
            getTreeSetFromIntArray(availableFbAppVersions), latestSdkVersion, versionSpec)

    assertThat(resultVersion).isEqualTo(7)
  }

  @Test
  fun `test sdk version newer than app with version spec newer and enabled`() {
    // Case where the sdk and app are older, but the app is still enabled
    val versionSpec = intArrayOf(1, 3, 7, 9, 10, 11, 12, 13)
    val latestSdkVersion = 8
    val availableFbAppVersions = intArrayOf(1, 2, 3, 4, 5, 6, 7)

    val resultVersion =
        NativeProtocol.computeLatestAvailableVersionFromVersionSpec(
            getTreeSetFromIntArray(availableFbAppVersions), latestSdkVersion, versionSpec)

    assertThat(resultVersion).isEqualTo(7)
  }

  @Test
  fun `test sdk version older than app with version spec newer and disabled`() {
    // Case where the sdk and app are older, and the app is a disabled version
    val versionSpec = intArrayOf(1, 3, 7, 8, 10, 11, 12, 13)
    val latestSdkVersion = 7
    val availableFbAppVersions = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8)

    val resultVersion =
        NativeProtocol.computeLatestAvailableVersionFromVersionSpec(
            getTreeSetFromIntArray(availableFbAppVersions), latestSdkVersion, versionSpec)

    assertThat(resultVersion).isEqualTo(NativeProtocol.NO_PROTOCOL_AVAILABLE)
  }

  @Test
  fun `test sdk version newer than app with version spec newer and disabled`() {
    // Case where the sdk and app are older, and the app is a disabled version
    val versionSpec = intArrayOf(1, 3, 6, 7, 10, 11, 12, 13)
    val latestSdkVersion = 8
    val availableFbAppVersions = intArrayOf(1, 2, 3, 4, 5, 6, 7)

    val resultVersion =
        NativeProtocol.computeLatestAvailableVersionFromVersionSpec(
            getTreeSetFromIntArray(availableFbAppVersions), latestSdkVersion, versionSpec)

    assertThat(resultVersion).isEqualTo(NativeProtocol.NO_PROTOCOL_AVAILABLE)
  }

  fun getTreeSetFromIntArray(array: IntArray): TreeSet<Int> {
    val treeSet = TreeSet<Int>()
    for (a in array) {
      treeSet.add(a)
    }
    return treeSet
  }

  @Test
  fun `native intent generation for FB app`() {
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext)
    val intents =
        NativeProtocol.createProxyAuthIntents(
            mockContext,
            mockAppID,
            listOf<String>(), // permissions
            "", // e2e
            false, // isRerequest
            false, // isForPublish
            DefaultAudience.FRIENDS, // defaultAudience
            "", // clientState
            "", // authType
            false, // ignoreAppSwitchToLoggedOut
            null, // messengerPageId
            false, // resetMessengerState
            false, // isFamilyLogin
            false, // shouldSkipAccountDedupe
            AuthenticationTokenTestUtil.NONCE,
            "codeChallenge",
            "S256")

    assertThat(intents.size).isEqualTo(2)
    val katanaIntent = intents[0]
    assertThat(katanaIntent.getComponent()?.getClassName())
        .isEqualTo("com.facebook.katana.ProxyAuth")
    assertThat(katanaIntent.getStringExtra(NativeProtocol.FACEBOOK_PROXY_AUTH_APP_ID_KEY))
        .isEqualTo(mockAppID)
    assertThat(katanaIntent.getStringExtra(ServerProtocol.DIALOG_PARAM_RESPONSE_TYPE))
        .isEqualTo(ServerProtocol.DIALOG_RESPONSE_TYPE_ID_TOKEN_AND_SIGNED_REQUEST)
    assertThat(katanaIntent.getBooleanExtra(ServerProtocol.DIALOG_PARAM_SKIP_DEDUPE, false))
        .isFalse()
    // TODO T111412069
    //    assertThat(katanaIntent.getStringExtra(ServerProtocol.DIALOG_PARAM_CODE_CHALLENGE_METHOD))
    //        .isEqualTo("S256")

    val wakizashiIntent = intents[1]
    assertThat(wakizashiIntent.getComponent()?.getClassName())
        .isEqualTo("com.facebook.katana.ProxyAuth")
    assertThat(wakizashiIntent.getStringExtra(NativeProtocol.FACEBOOK_PROXY_AUTH_APP_ID_KEY))
        .isEqualTo(mockAppID)
    assertThat(wakizashiIntent.getStringExtra(ServerProtocol.DIALOG_PARAM_RESPONSE_TYPE))
        .isEqualTo(ServerProtocol.DIALOG_RESPONSE_TYPE_ID_TOKEN_AND_SIGNED_REQUEST)
    assertThat(wakizashiIntent.getBooleanExtra(ServerProtocol.DIALOG_PARAM_SKIP_DEDUPE, false))
        .isFalse()
    // TODO T111412069
    // assertThat(wakizashiIntent.getStringExtra(ServerProtocol.DIALOG_PARAM_CODE_CHALLENGE_METHOD))
    //        .isEqualTo("S256")
  }

  // TODO T111412069
  //  @Test
  //  fun `native intent generation for FB app without code challenge method will use S256 as the
  // default method`() {
  //    val mockContext = mock<Context>()
  //    setUpMockingForNativeIntentGeneration(mockContext)
  //    val intents =
  //        NativeProtocol.createProxyAuthIntents(
  //            mockContext,
  //            mockAppID,
  //            listOf<String>(), // permissions
  //            "", // e2e
  //            false, // isRerequest
  //            false, // isForPublish
  //            DefaultAudience.FRIENDS, // defaultAudience
  //            "", // clientState
  //            "", // authType
  //            false, // ignoreAppSwitchToLoggedOut
  //            null, // messengerPageId
  //            false, // resetMessengerState
  //            false, // isFamilyLogin
  //            false, // shouldSkipAccountDedupe
  //            AuthenticationTokenTestUtil.NONCE,
  //            "codeChallenge")
  //
  //    assertThat(intents.size).isEqualTo(2)
  //    val katanaIntent = intents[0]
  //    assertThat(katanaIntent.getStringExtra(ServerProtocol.DIALOG_PARAM_CODE_CHALLENGE_METHOD))
  //        .isEqualTo("S256")
  //
  //    val wakizashiIntent = intents[1]
  //
  // assertThat(wakizashiIntent.getStringExtra(ServerProtocol.DIALOG_PARAM_CODE_CHALLENGE_METHOD))
  //        .isEqualTo("S256")
  //  }

  @Test
  fun `native intent generation for FB app with null code challenge method will not have code challenge method`() {
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext)
    val intents =
        NativeProtocol.createProxyAuthIntents(
            mockContext,
            mockAppID,
            listOf<String>(), // permissions
            "", // e2e
            false, // isRerequest
            false, // isForPublish
            DefaultAudience.FRIENDS, // defaultAudience
            "", // clientState
            "", // authType
            false, // ignoreAppSwitchToLoggedOut
            null, // messengerPageId
            false, // resetMessengerState
            false, // isFamilyLogin
            false, // shouldSkipAccountDedupe
            AuthenticationTokenTestUtil.NONCE,
            null)

    assertThat(intents.size).isEqualTo(2)
    val katanaIntent = intents[0]
    assertThat(katanaIntent.getStringExtra(ServerProtocol.DIALOG_PARAM_CODE_CHALLENGE_METHOD))
        .isNull()

    val wakizashiIntent = intents[1]
    assertThat(wakizashiIntent.getStringExtra(ServerProtocol.DIALOG_PARAM_CODE_CHALLENGE_METHOD))
        .isNull()
  }

  @Test
  fun `native intent generation for IG app`() {
    val mockMessengerPageId = "123456789"
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext)
    val instagramIntent =
        NativeProtocol.createInstagramIntent(
            mockContext,
            mockAppID,
            listOf<String>(), // permissions
            "", // e2e
            false, // isRerequest
            false, // isForPublish
            DefaultAudience.FRIENDS, // defaultAudience
            "", // clientState
            "", // authType
            mockMessengerPageId, // messengerPageId
            true, // resetMessengerState
            true, // isFamilyLogin
            true) // shouldSkipAccountDedupe

    assertThat(instagramIntent?.getComponent()?.getClassName())
        .isEqualTo("com.instagram.platform.AppAuthorizeActivity")
    assertThat(instagramIntent?.getStringExtra(NativeProtocol.FACEBOOK_PROXY_AUTH_APP_ID_KEY))
        .isEqualTo(mockAppID)
    assertThat(instagramIntent?.getStringExtra(ServerProtocol.DIALOG_PARAM_RESPONSE_TYPE))
        .isEqualTo(ServerProtocol.DIALOG_RESPONSE_TYPE_TOKEN_AND_SCOPES)
    assertThat(instagramIntent?.getBooleanExtra(ServerProtocol.DIALOG_PARAM_SKIP_DEDUPE, false))
        .isTrue()
    assertThat(instagramIntent?.getStringExtra(ServerProtocol.DIALOG_PARAM_MESSENGER_PAGE_ID))
        .isEqualTo(mockMessengerPageId)
    assertThat(
            instagramIntent?.getBooleanExtra(
                ServerProtocol.DIALOG_PARAM_RESET_MESSENGER_STATE, false))
        .isTrue()
  }

  @Test
  fun `test validate null intent for service intent`() {
    val mockContext = mock<Context>()
    setUpMockingForServiceIntentGeneration(mockContext)
    assertThat(NativeProtocol.validateServiceIntent(mockContext, null, mock())).isNull()
  }

  @Test
  fun `test validate service intent when signature validation passes`() {
    val mockContext = mock<Context>()
    setUpMockingForServiceIntentGeneration(mockContext)
    val intent = mock<Intent>()
    assertThat(NativeProtocol.validateServiceIntent(mockContext, intent, mock())).isEqualTo(intent)
  }

  @Test
  fun `test validate service intent when signature validation fails`() {
    val mockContext = mock<Context>()
    setUpMockingForServiceIntentGeneration(mockContext, signatureValidationResult = false)
    val intent = mock<Intent>()
    assertThat(NativeProtocol.validateServiceIntent(mockContext, intent, mock())).isNull()
  }

  @Test
  fun `test validate null intent for activity intent`() {
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext)
    assertThat(NativeProtocol.validateServiceIntent(mockContext, null, mock())).isNull()
  }

  @Test
  fun `test validate activity intent when signature validation passes`() {
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext)
    val intent = mock<Intent>()
    assertThat(NativeProtocol.validateActivityIntent(mockContext, intent, mock())).isEqualTo(intent)
  }

  @Test
  fun `test validate activity intent when signature validation fails`() {
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext, false)
    val intent = mock<Intent>()
    assertThat(NativeProtocol.validateActivityIntent(mockContext, intent, mock())).isNull()
  }

  @Test
  fun `test create token refresh intent from a valid context`() {
    val mockContext = mock<Context>()
    setUpMockingForServiceIntentGeneration(mockContext)
    assertThat(NativeProtocol.createPlatformServiceIntent(mockContext)).isNotNull
  }

  @Test
  fun `test latest known protocol version is compatible with bucketed intent`() {
    val latestVersion = NativeProtocol.getLatestKnownVersion()
    assertThat(NativeProtocol.isVersionCompatibleWithBucketedIntent(latestVersion))
  }

  @Test
  fun `test create token refresh intent from a context without service intent available`() {
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext)
    assertThat(NativeProtocol.createPlatformServiceIntent(mockContext)).isNull()
  }

  @Test
  fun `test create platform activity intent with valid native app`() {
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext)
    val extras = Bundle()
    extras.putString("test_param_field", "test_param_value")
    val versionResult =
        NativeProtocol.ProtocolVersionQueryResult.create(
            mockNativeAppInfo, NativeProtocol.PROTOCOL_VERSION_20210906)

    val intent =
        NativeProtocol.createPlatformActivityIntent(
            mockContext, mockCallId, NativeProtocol.ACTION_LIKE_DIALOG, versionResult, extras)

    checkNotNull(intent)
    assertThat(intent.action).isEqualTo(NativeProtocol.INTENT_ACTION_PLATFORM_ACTIVITY)
    assertThat(intent.`package`).isEqualTo(mockNativeAppInfo.getPackage())
    assertThat(intent.getStringExtra(NativeProtocol.EXTRA_APPLICATION_ID)).isEqualTo(mockAppID)
    val protocolMethodArgs =
        checkNotNull(intent.getBundleExtra(NativeProtocol.EXTRA_PROTOCOL_METHOD_ARGS))
    assertThat(protocolMethodArgs.getString("test_param_field")).isEqualTo("test_param_value")
  }

  @Test
  fun `test create platform activity intent without native app query result`() {
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext)

    val intent =
        NativeProtocol.createPlatformActivityIntent(
            mockContext, mockCallId, NativeProtocol.ACTION_LIKE_DIALOG, null, null)

    assertThat(intent).isNull()
  }

  @Test
  fun `test create platform activity intent with no native app in the query result`() {
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext)
    val emptyResult = NativeProtocol.ProtocolVersionQueryResult.createEmpty()

    val intent =
        NativeProtocol.createPlatformActivityIntent(
            mockContext, mockCallId, NativeProtocol.ACTION_LIKE_DIALOG, emptyResult, null)

    assertThat(intent).isNull()
  }

  @Test
  fun `test create protocol result intent with results`() {
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext)
    val extras = Bundle()
    extras.putString("test_param_field", "test_param_value")
    val versionResult =
        NativeProtocol.ProtocolVersionQueryResult.create(
            mockNativeAppInfo, NativeProtocol.PROTOCOL_VERSION_20210906)
    val requestIntent =
        NativeProtocol.createPlatformActivityIntent(
            mockContext, mockCallId, NativeProtocol.ACTION_LIKE_DIALOG, versionResult, extras)
    val results = Bundle()
    results.putString("test_result_field", "test_result_value")

    val resultIntent =
        checkNotNull(
            NativeProtocol.createProtocolResultIntent(checkNotNull(requestIntent), results, null))

    assertThat(resultIntent.getIntExtra(NativeProtocol.EXTRA_PROTOCOL_VERSION, 0))
        .isEqualTo(versionResult.protocolVersion)
    assertThat(
            resultIntent
                .getBundleExtra(NativeProtocol.EXTRA_PROTOCOL_BRIDGE_ARGS)
                ?.getString(NativeProtocol.BRIDGE_ARG_ACTION_ID_STRING))
        .isEqualTo(mockCallId)
    assertThat(
            resultIntent
                .getBundleExtra(NativeProtocol.EXTRA_PROTOCOL_METHOD_RESULTS)
                ?.getString("test_result_field"))
        .isEqualTo("test_result_value")
  }

  @Test
  fun `test create protocol result intent with error`() {
    val mockContext = mock<Context>()
    setUpMockingForNativeIntentGeneration(mockContext)
    val extras = Bundle()
    extras.putString("test_param_field", "test_param_value")
    val versionResult =
        NativeProtocol.ProtocolVersionQueryResult.create(
            mockNativeAppInfo, NativeProtocol.PROTOCOL_VERSION_20210906)
    val requestIntent =
        NativeProtocol.createPlatformActivityIntent(
            mockContext, mockCallId, NativeProtocol.ACTION_LIKE_DIALOG, versionResult, extras)
    val error = FacebookException("test error message")

    val resultIntent =
        checkNotNull(
            NativeProtocol.createProtocolResultIntent(checkNotNull(requestIntent), null, error))

    assertThat(resultIntent.getIntExtra(NativeProtocol.EXTRA_PROTOCOL_VERSION, 0))
        .isEqualTo(versionResult.protocolVersion)
    assertThat(
            resultIntent
                .getBundleExtra(NativeProtocol.EXTRA_PROTOCOL_BRIDGE_ARGS)
                ?.getString(NativeProtocol.BRIDGE_ARG_ACTION_ID_STRING))
        .isEqualTo(mockCallId)
    val errorBundle =
        resultIntent
            .getBundleExtra(NativeProtocol.EXTRA_PROTOCOL_BRIDGE_ARGS)
            ?.getBundle(NativeProtocol.BRIDGE_ARG_ERROR_BUNDLE)
    checkNotNull(errorBundle)
    assertThat(errorBundle.getString(NativeProtocol.BRIDGE_ARG_ERROR_DESCRIPTION))
        .isEqualTo(error.toString())
  }

  @Test
  fun `test parse error data from bridge arg error data bundle`() {
    val data = Bundle()
    val errorType = "TestError"
    val errorDescription = "test error description"
    data.putString(NativeProtocol.BRIDGE_ARG_ERROR_TYPE, errorType)
    data.putString(NativeProtocol.BRIDGE_ARG_ERROR_DESCRIPTION, errorDescription)
    val exception = NativeProtocol.getExceptionFromErrorData(data)
    checkNotNull(exception)
    assertThat(exception.message).isEqualTo(errorDescription)
  }

  @Test
  fun `test parse error data from bridge arg canceled error bundle`() {
    val data = Bundle()
    val errorType = "USERCANCELED" // full capitalized only for testing
    val errorDescription = "test error description"
    data.putString(NativeProtocol.BRIDGE_ARG_ERROR_TYPE, errorType)
    data.putString(NativeProtocol.BRIDGE_ARG_ERROR_DESCRIPTION, errorDescription)
    val exception = NativeProtocol.getExceptionFromErrorData(data)
    checkNotNull(exception)
    assertThat(exception).isInstanceOf(FacebookOperationCanceledException::class.java)
    assertThat(exception.message).isEqualTo(errorDescription)
  }

  @Test
  fun `test recognizing error result intent with bridge arguments and obtain error data`() {
    val errorType = "TestError"
    val errorDescription = "test error description"
    val intent = Intent()
    val bridgeArgs = Bundle()
    val errorBundle = Bundle()
    errorBundle.putString(NativeProtocol.BRIDGE_ARG_ERROR_TYPE, errorType)
    errorBundle.putString(NativeProtocol.BRIDGE_ARG_ERROR_DESCRIPTION, errorDescription)
    bridgeArgs.putBundle(NativeProtocol.BRIDGE_ARG_ERROR_BUNDLE, errorBundle)
    intent.putExtra(NativeProtocol.EXTRA_PROTOCOL_VERSION, NativeProtocol.PROTOCOL_VERSION_20171115)
    intent.putExtra(NativeProtocol.EXTRA_PROTOCOL_BRIDGE_ARGS, bridgeArgs)

    assertThat(NativeProtocol.isErrorResult(intent)).isTrue
    val parsedException =
        NativeProtocol.getExceptionFromErrorData(
            NativeProtocol.getErrorDataFromResultIntent(intent))
    checkNotNull(parsedException)
    assertThat(parsedException.message).isEqualTo(errorDescription)
  }

  fun setUpMockingForNativeIntentGeneration(
      mockContext: Context,
      signatureValidationResult: Boolean = true
  ) {
    PowerMockito.mockStatic(FacebookSignatureValidator::class.java)
    whenever(FacebookSignatureValidator.validateSignature(any(), any()))
        .thenReturn(signatureValidationResult)
    val mockPackageManager = mock<PackageManager>()
    val mockResolveInfo = mock<ResolveInfo>()
    val mockActivityInfo = mock<ActivityInfo>()
    mockActivityInfo.packageName = mockPackageName
    mockResolveInfo.activityInfo = mockActivityInfo
    whenever(mockContext.getPackageManager()).thenReturn(mockPackageManager)
    whenever(mockPackageManager.resolveActivity(any(), any())).thenReturn(mockResolveInfo)
  }

  fun setUpMockingForServiceIntentGeneration(
      mockContext: Context,
      signatureValidationResult: Boolean = true
  ) {
    PowerMockito.mockStatic(FacebookSignatureValidator::class.java)
    whenever(FacebookSignatureValidator.validateSignature(any(), any()))
        .thenReturn(signatureValidationResult)
    val mockPackageManager = mock<PackageManager>()
    val mockResolveInfo = mock<ResolveInfo>()
    val mockServiceInfo = mock<ServiceInfo>()
    mockServiceInfo.packageName = mockPackageName
    mockResolveInfo.serviceInfo = mockServiceInfo
    whenever(mockContext.packageManager).thenReturn(mockPackageManager)
    whenever(mockPackageManager.resolveService(any(), any())).thenReturn(mockResolveInfo)
  }
}


/*
 * 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 androidx.test.core.app.ApplicationProvider
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
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
import org.powermock.reflect.Whitebox

@PrepareForTest(FacebookSdk::class)
class ServerProtocolTest : FacebookPowerMockTestCase() {
  private val mockExecutor = FacebookSerialExecutor()

  private val callId = "1337"
  private val version = 420
  private val appId = "9999"

  @Before
  fun init() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    Whitebox.setInternalState(FacebookSdk::class.java, "executor", mockExecutor)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
  }

  @Test
  fun `no valid keyhash gives null`() {
    PowerMockito.`when`(
            FacebookSdk.getApplicationSignature(ApplicationProvider.getApplicationContext()))
        .thenReturn("")
    val params =
        ServerProtocol.getQueryParamsForPlatformActivityIntentWebFallback(callId, version, Bundle())
    assertNull(params)
  }

  @Test
  fun `all valid ok`() {
    PowerMockito.`when`(
            FacebookSdk.getApplicationSignature(ApplicationProvider.getApplicationContext()))
        .thenReturn("abc123")
    whenever(FacebookSdk.getApplicationId()).thenReturn(appId)
    val params =
        ServerProtocol.getQueryParamsForPlatformActivityIntentWebFallback(callId, version, Bundle())
    assertNotNull(params)
    checkNotNull(params)
    assertNotNull(params.getString(ServerProtocol.FALLBACK_DIALOG_PARAM_KEY_HASH))
    assertNotNull(params.getString(ServerProtocol.FALLBACK_DIALOG_PARAM_APP_ID))
    assertNotNull(params.getInt(ServerProtocol.FALLBACK_DIALOG_PARAM_VERSION))
    assertNotNull(params.getString(ServerProtocol.DIALOG_PARAM_DISPLAY))
    assertNotNull(params.getString(ServerProtocol.FALLBACK_DIALOG_PARAM_BRIDGE_ARGS))
    assertNotNull(params.getString(ServerProtocol.FALLBACK_DIALOG_PARAM_METHOD_ARGS))
  }

  @Test
  fun `no valid bundle gives null`() {
    val b = Bundle()
    b.putShort("shortValue", 7.toShort())
    PowerMockito.`when`(
            FacebookSdk.getApplicationSignature(ApplicationProvider.getApplicationContext()))
        .thenReturn("abc123")
    val params =
        ServerProtocol.getQueryParamsForPlatformActivityIntentWebFallback(callId, version, b)
    assertNull(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.internal

import java.util.EnumSet
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Test

class SmartLoginOptionTest {

  @Test
  fun `test SmartLoginOption parseOptions method`() {
    assertEquals(SmartLoginOption.parseOptions(0), EnumSet.noneOf(SmartLoginOption::class.java))
    assertEquals(SmartLoginOption.parseOptions(1), EnumSet.of(SmartLoginOption.Enabled))
    assertEquals(SmartLoginOption.parseOptions(2), EnumSet.of(SmartLoginOption.RequireConfirm))
    assertNotEquals(SmartLoginOption.parseOptions(3), EnumSet.of(SmartLoginOption.Enabled))
  }
}


/*
 * 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 com.facebook.FacebookSdk
import java.io.File
import java.io.IOException
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyString
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
import org.powermock.reflect.Whitebox

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

  companion object {
    private const val TO_URI_STRING = "http://facebook.com"
    private const val TO_URI_STRING_LONG =
        "http://facebook.com/this_is_the_very_very_very_loooooooooooooooooooooooooooooooooooooooooooooo000000000000000000000000000000000000000oooog_address_to_test_the_cache"
    private const val FROM_URI_STRING = "http://fbtest.com"
    private const val FROM_URI_STRING2 = "http://fb.com"
  }

  @Mock private lateinit var fromUri: Uri
  @Mock private lateinit var fromUri2: Uri
  @Mock private lateinit var toUri: Uri
  @Mock private lateinit var toUriLong: Uri
  private lateinit var testCacheFilePath: File

  @Before
  fun init() {
    testCacheFilePath = File(java.util.UUID.randomUUID().toString())
    testCacheFilePath.mkdir()

    mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getExecutor()).thenReturn(FacebookSerialExecutor())
    whenever(FacebookSdk.getCacheDir()).thenReturn(testCacheFilePath)

    whenever(fromUri.toString()).thenReturn(FROM_URI_STRING)
    whenever(fromUri2.toString()).thenReturn(FROM_URI_STRING2)
    whenever(toUri.toString()).thenReturn(TO_URI_STRING)
    whenever(toUriLong.toString()).thenReturn(TO_URI_STRING_LONG)

    val nullCache: FileLruCache? = null
    Whitebox.setInternalState(UrlRedirectCache::class.java, "urlRedirectFileLruCache", nullCache)
  }

  @After
  fun clean() {
    UrlRedirectCache.clearCache()
    testCacheFilePath.deleteRecursively()
  }

  @Test
  fun `test read and write cache with normal input`() {
    UrlRedirectCache.cacheUriRedirect(fromUri, toUri)
    UrlRedirectCache.cacheUriRedirect(fromUri2, toUriLong)
    val fetchedResultForURL = UrlRedirectCache.getRedirectedUri(fromUri)
    assertEquals(fetchedResultForURL.toString(), toUri.toString())

    val fetchedResultForURL2 = UrlRedirectCache.getRedirectedUri(fromUri2)
    assertEquals(fetchedResultForURL2.toString(), toUriLong.toString())
  }

  @Test
  fun `test read after clearing the cache`() {
    UrlRedirectCache.cacheUriRedirect(fromUri, toUri)
    UrlRedirectCache.clearCache()
    val fetchedResultForURL = UrlRedirectCache.getRedirectedUri(fromUri)
    assertNull(fetchedResultForURL)
  }

  @Test
  fun `test read and write cache with null`() {
    UrlRedirectCache.cacheUriRedirect(fromUri, null)
    val fetchedResultForURL = UrlRedirectCache.getRedirectedUri(fromUri)
    assertNull(fetchedResultForURL)

    val fetchedResultForNull = UrlRedirectCache.getRedirectedUri(null)
    assertNull(fetchedResultForNull)
  }

  @Test
  fun `test write the cache twice`() {
    UrlRedirectCache.cacheUriRedirect(fromUri, toUriLong)
    UrlRedirectCache.cacheUriRedirect(fromUri, toUri)
    val fetchedResultForURL = UrlRedirectCache.getRedirectedUri(fromUri)
    assertEquals(fetchedResultForURL.toString(), toUri.toString())
  }

  @Test
  fun `test write the cache twice with same address`() {
    UrlRedirectCache.cacheUriRedirect(fromUri, toUri)
    UrlRedirectCache.cacheUriRedirect(fromUri, toUri)
    val fetchedResultForURL = UrlRedirectCache.getRedirectedUri(fromUri)
    assertEquals(fetchedResultForURL.toString(), toUri.toString())
  }

  @Test
  fun `test redirect to itself`() {
    UrlRedirectCache.cacheUriRedirect(fromUri, fromUri)
    val fetchedResultForURL = UrlRedirectCache.getRedirectedUri(fromUri)
    assertEquals(fetchedResultForURL.toString(), fetchedResultForURL.toString())
  }

  @Test
  fun `test loop in redirect`() {
    UrlRedirectCache.cacheUriRedirect(fromUri, fromUri2)
    UrlRedirectCache.cacheUriRedirect(fromUri2, toUri)
    UrlRedirectCache.cacheUriRedirect(toUri, fromUri)
    val fetchedResultForURL = UrlRedirectCache.getRedirectedUri(fromUri)
    assertNull(fetchedResultForURL)
  }

  @Test
  fun `test multiple redirects`() {
    UrlRedirectCache.cacheUriRedirect(fromUri, fromUri2)
    UrlRedirectCache.cacheUriRedirect(fromUri2, toUri)
    val fetchedResultForURL = UrlRedirectCache.getRedirectedUri(fromUri)
    assertEquals(fetchedResultForURL.toString(), toUri.toString())
  }

  @Test
  fun `test FileLRUCache is broken`() {
    val mockFileLruCache = mock(FileLruCache::class.java)
    whenever(mockFileLruCache.get(anyString(), anyString()))
        .thenThrow(IOException("mock exception"))
    whenever(mockFileLruCache.openPutStream(anyString(), anyString()))
        .thenThrow(IOException("mock exception"))

    Whitebox.setInternalState(
        UrlRedirectCache::class.java, "urlRedirectFileLruCache", mockFileLruCache)

    UrlRedirectCache.cacheUriRedirect(fromUri, toUri)
    val fetchedResultForURL = UrlRedirectCache.getRedirectedUri(fromUri)
    assertNull(fetchedResultForURL)
  }
}


/*
 * 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 android.os.Build
import android.os.Bundle
import android.os.Parcel
import androidx.test.core.app.ApplicationProvider
import com.facebook.AccessToken
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.GraphRequest
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import java.util.Date
import kotlin.collections.HashMap
import org.assertj.core.api.Assertions.assertThat
import org.json.JSONArray
import org.json.JSONObject
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
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.api.mockito.PowerMockito.mockStatic
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.reflect.Whitebox

@PrepareForTest(
    AccessToken::class,
    FacebookSdk::class,
    GraphRequest::class,
    FeatureManager::class,
    GoogleApiAvailability::class,
)
class UtilityTest : FacebookPowerMockTestCase() {

  private val mockTokenString = "A token of my esteem"
  private val mockAppID = "1234"
  private val mockClientToken = "5678"

  @Before
  override fun setup() {
    super.setup()
    PowerMockito.mockStatic(FacebookSdk::class.java)
    whenever(FacebookSdk.isInitialized()).thenReturn(true)
    whenever(FacebookSdk.getApplicationId()).thenReturn(mockAppID)
    whenever(FacebookSdk.getClientToken()).thenReturn(mockClientToken)
    whenever(FacebookSdk.getApplicationContext())
        .thenReturn(ApplicationProvider.getApplicationContext())
    whenever(FacebookSdk.getGraphDomain()).thenCallRealMethod()
    whenever(FacebookSdk.getFacebookDomain()).thenCallRealMethod()
    whenever(FacebookSdk.getGraphApiVersion()).thenCallRealMethod()
    PowerMockito.mockStatic(FeatureManager::class.java)
  }

  @Test
  fun testGetGraphMeRequestWithCacheAsyncNoGraphDomain() {
    createTestForGetGraphMeRequestWithCacheAsync(null, "id,name,first_name,middle_name,last_name")
  }

  @Test
  fun testGetGraphMeRequestWithCacheAsyncNullGraphDomain() {
    createTestForGetGraphMeRequestWithCacheAsync(
        "facebook", "id,name,first_name,middle_name,last_name")
  }

  @Test
  fun testGetGraphMeRequestWithCacheAsyncGGGraphDomain() {
    createTestForGetGraphMeRequestWithCacheAsync(
        "gaming", "id,name,first_name,middle_name,last_name")
  }

  @Test
  fun testGetGraphMeRequestWithCacheAsyncIGGraphDomain() {
    createTestForGetGraphMeRequestWithCacheAsync("instagram", "id,name,profile_picture")
  }

  @Test
  fun testGetGraphDomainFromTokenDomain() {
    var graphDomain = Utility.getGraphDomainFromTokenDomain("facebook")
    assertThat(graphDomain).isEqualTo("facebook.com")

    graphDomain = Utility.getGraphDomainFromTokenDomain("gaming")
    assertThat(graphDomain).isEqualTo("fb.gg")

    graphDomain = Utility.getGraphDomainFromTokenDomain("instagram")
    assertThat(graphDomain).isEqualTo("instagram.com")
  }

  fun createTestForGetGraphMeRequestWithCacheAsync(graphDomain: String?, expectedFields: String) {
    val mockGraphRequestCompanion = mock<GraphRequest.Companion>()
    val mockGraphRequest = mock<GraphRequest>()
    whenever(mockGraphRequestCompanion.newMeRequest(anyOrNull(), anyOrNull()))
        .thenReturn(mockGraphRequest)
    Whitebox.setInternalState(GraphRequest::class.java, "Companion", mockGraphRequestCompanion)

    val accessToken = mock<AccessToken>()
    whenever(accessToken.graphDomain).thenReturn(graphDomain)
    val mockTokenCompanionObject = mock<AccessToken.Companion>()
    Whitebox.setInternalState(AccessToken::class.java, "Companion", mockTokenCompanionObject)
    whenever(mockTokenCompanionObject.getCurrentAccessToken()).thenReturn(accessToken)

    val bundleArgumentCaptor = argumentCaptor<Bundle>()
    Utility.getGraphMeRequestWithCacheAsync(mockTokenString, mock())

    verify(mockGraphRequestCompanion).newMeRequest(isNull(), isNull())
    verify(mockGraphRequest).parameters = bundleArgumentCaptor.capture()
    val parameters = bundleArgumentCaptor.firstValue
    assertThat(parameters.getString(GraphRequest.FIELDS_PARAM)).isEqualTo(expectedFields)
    assertThat(parameters.getString("access_token")).isEqualTo(mockTokenString)
  }

  @Test
  fun testStringMapToParcel() {
    // Test null
    assertNull(roundtrip(null))
    val map = HashMap<String?, String?>()

    // Test empty
    assertEquals(0, roundtrip(map)?.size)

    // Test regular
    map["a"] = "100"
    map["b"] = null
    map["c"] = "hello"
    val result = roundtrip(map)
    assertEquals(3, result?.size)
    assertEquals(map, result)
    assertEquals("100", result?.get("a"))
    assertNull(result?.get("b"))
    assertEquals("hello", result?.get("c"))
  }

  private fun roundtrip(input: Map<String?, String?>?): Map<String?, String?>? {
    val parcel = Parcel.obtain()
    return try {
      Utility.writeStringMapToParcel(parcel, input)
      parcel.setDataPosition(0)
      Utility.readStringMapFromParcel(parcel)
    } finally {
      parcel.recycle()
    }
  }

  @Test
  fun testUriBuild() {
    val path = "v10.0/dialog/access"

    val parameters = Bundle()
    parameters.putString("f1", "v1")
    parameters.putString("f2", "v2")
    parameters.putString("f3", "www.facebook.com")

    var uri = Utility.buildUri(ServerProtocol.getDialogAuthority(), path, null)
    assertEquals("https://m.facebook.com/v10.0/dialog/access", uri.toString())

    uri = Utility.buildUri(ServerProtocol.getDialogAuthority(), path, parameters)
    assertEquals(
        "https://m.facebook.com/v10.0/dialog/access?f1=v1&f2=v2&f3=www.facebook.com",
        uri.toString())

    // Test parseUrlQueryString
    val query = Utility.parseUrlQueryString(uri.query)
    assertEquals(parameters.toString(), query.toString())
  }

  @Test
  fun testBuildBundle() {
    val parameters = Bundle()
    Utility.putCommaSeparatedStringList(parameters, "k1", listOf("12", "34", "56", "78"))

    assertEquals("12,34,56,78", parameters.get("k1"))

    val barray = arrayOf(true, false).toBooleanArray()
    Utility.putJSONValueInBundle(parameters, "k2", barray)
    assertArrayEquals(barray, parameters.get("k2") as BooleanArray)

    Utility.putJSONValueInBundle(parameters, "k3", "test")
    assertThat(parameters.getString("k3")).isEqualTo("test")
  }

  @Test
  fun `test putting numbers and number arrays to bundle as JSON values`() {
    val doubleValue = 1.23
    val doubleArray = doubleArrayOf(1.2, 1.3)
    val intValue = 42
    val intArray = intArrayOf(4, 2)
    val longValue = 42L
    val longArray = longArrayOf(4, 2)
    val bundle = Bundle()

    Utility.putJSONValueInBundle(bundle, "dv", doubleValue)
    Utility.putJSONValueInBundle(bundle, "da", doubleArray)
    Utility.putJSONValueInBundle(bundle, "iv", intValue)
    Utility.putJSONValueInBundle(bundle, "ia", intArray)
    Utility.putJSONValueInBundle(bundle, "lv", longValue)
    Utility.putJSONValueInBundle(bundle, "la", longArray)

    assertThat(bundle.getDouble("dv")).isEqualTo(doubleValue)
    assertThat(bundle.getDoubleArray("da")).isEqualTo(doubleArray)
    assertThat(bundle.getInt("iv")).isEqualTo(intValue)
    assertThat(bundle.getIntArray("ia")).isEqualTo(intArray)
    assertThat(bundle.getLong("lv")).isEqualTo(longValue)
    assertThat(bundle.getLongArray("la")).isEqualTo(longArray)
  }

  @Test
  fun `test putting JSON array and object to bundle`() {
    val bundle = Bundle()
    val jsonArray = JSONArray()
    jsonArray.put("v1")
    jsonArray.put(42)
    val jsonObject = JSONObject()
    jsonObject.put("k1", "v1")
    Utility.putJSONValueInBundle(bundle, "array", jsonArray)
    Utility.putJSONValueInBundle(bundle, "object", jsonObject)
    assertThat(bundle.getString("array")).isEqualTo(jsonArray.toString())
    assertThat(bundle.getString("object")).isEqualTo(jsonObject.toString())
  }

  @Test
  fun `test removing a JSON value from bundle`() {
    val bundle = Bundle()
    bundle.putString("k1", "123")
    Utility.putJSONValueInBundle(bundle, "k1", null)
    assertThat(bundle.get("k1")).isNull()
  }

  @Test
  fun `test inserting an invalid JSON value to bundle`() {
    val bundle = Bundle()
    assertThat(Utility.putJSONValueInBundle(bundle, "k1", Any())).isFalse
    assertThat(bundle.get("k1")).isNull()
  }

  @Test
  fun `test getting bundle long as date to have correct result`() {
    // a non-zero base date to test whether the computation is correct
    val baseDate = Date(37)
    val bundle = Bundle()
    bundle.putLong("k", 100L)
    val result = Utility.getBundleLongAsDate(bundle, "k", baseDate)
    assertThat(result?.time).isEqualTo(100L * 1000 + 37)
  }

  @Test
  fun `test getting bundle string as date to have correct result`() {
    val baseDate = Date(37)
    val bundle = Bundle()
    bundle.putString("k", "100")
    val result = Utility.getBundleLongAsDate(bundle, "k", baseDate)
    assertThat(result?.time).isEqualTo(100L * 1000 + 37)
  }

  @Test
  fun `test getting invalid bundle value as date to get null`() {
    val baseDate = Date(37)
    val bundle = Bundle()
    bundle.putString("k", "value")
    assertThat(Utility.getBundleLongAsDate(bundle, "k", baseDate)).isNull()
    assertThat(Utility.getBundleLongAsDate(null, "k", baseDate)).isNull()
  }

  @Test
  fun `test getting 0 from bundle as date to get largest value`() {
    val baseDate = Date(37)
    val bundle = Bundle()
    bundle.putLong("k", 0)
    val result = Utility.getBundleLongAsDate(bundle, "k", baseDate)
    assertThat(result?.time).isEqualTo(Long.MAX_VALUE)
  }

  @Test
  fun testConvertJSONObjectToStringMap() {
    val validJson = "{\"k1\": true, \"k2\": \"value\"}"
    val result = Utility.convertJSONObjectToStringMap(JSONObject(validJson))
    assertEquals(mapOf("k1" to "true", "k2" to "value"), result)
  }

  @Test
  fun testSetAppEventAttributionParametersWithoutServiceUpdateCompliance() {
    whenever(FeatureManager.isEnabled(FeatureManager.Feature.ServiceUpdateCompliance))
        .thenReturn(false)

    val params: JSONObject = JSONObject()
    val mockAnonId = "fb_mock_anonID"
    val mockAttributionID = "fb_mock_attributionID"
    val mockContext = mock<Context>()
    val mockIdentifiers = PowerMockito.mock(AttributionIdentifiers::class.java)
    whenever(mockIdentifiers.attributionId).thenReturn(mockAttributionID)
    Utility.setAppEventAttributionParameters(
        params, mockIdentifiers, mockAnonId, false, mockContext)
    assertEquals(params["anon_id"], mockAnonId)
    assertEquals(params["attribution"], mockAttributionID)
  }

  @Test
  fun testSetAppEventAttributionParametersWithServiceUpdateCompliance() {
    if (Build.VERSION.SDK_INT < 31) {
      // Skipping test, SDK version is lower than 31
      return
    }

    whenever(FeatureManager.isEnabled(FeatureManager.Feature.ServiceUpdateCompliance))
        .thenReturn(true)

    val googleApiAvailability = PowerMockito.mock(GoogleApiAvailability::class.java)
    mockStatic(GoogleApiAvailability::class.java)
    whenever(GoogleApiAvailability.getInstance()).thenReturn(googleApiAvailability)
    whenever(googleApiAvailability.isGooglePlayServicesAvailable(any()))
        .thenReturn(ConnectionResult.SUCCESS)

    val params: JSONObject = JSONObject()
    val mockAnonId = "fb_mock_anonID"
    val mockAttributionID = "fb_mock_attributionID"
    val mockContext = mock<Context>()
    val mockIdentifiers = PowerMockito.mock(AttributionIdentifiers::class.java)
    whenever(mockIdentifiers.attributionId).thenReturn(mockAttributionID)
    Utility.setAppEventAttributionParameters(
        params, mockIdentifiers, mockAnonId, false, mockContext)
    assertNull(params["anon_id"])
    assertNull(params["attribution"])
  }
}


/*
 * 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 android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.internal.Validate.hasAppID
import com.facebook.internal.Validate.hasClientToken
import com.facebook.internal.Validate.notEmpty
import com.facebook.internal.Validate.notNull
import com.facebook.internal.Validate.notNullOrEmpty
import com.facebook.internal.Validate.oneOf
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
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

@PrepareForTest(FacebookSdk::class)
class ValidateTest : FacebookPowerMockTestCase() {
  private val appID = "123"
  private val clientToken = "mockClientToken"
  private val packageName = "com.test"
  private lateinit var mockContext: Context
  private lateinit var mockPackageManager: PackageManager

  @Before
  fun before() {
    PowerMockito.mockStatic(FacebookSdk::class.java)
    mockContext = mock()
    mockPackageManager = mock()
    whenever(mockContext.packageManager).thenReturn(mockPackageManager)
    whenever(mockContext.packageName).thenReturn(packageName)
  }

  @Test
  fun testNotNullOnNonNull() {
    notNull("A string", "name")
  }

  @Test(expected = NullPointerException::class)
  fun testNotNullOnNull() {
    notNull(null, "name")
  }

  @Test
  fun testNotEmptyOnNonEmpty() {
    notEmpty(listOf("hi"), "name")
  }

  @Test(expected = IllegalArgumentException::class)
  fun testNotEmptylOnEmpty() {
    notEmpty(listOf<String>(), "name")
  }

  @Test
  fun testNotNullOrEmptyOnNonEmpty() {
    notNullOrEmpty("hi", "name")
  }

  @Test(expected = IllegalArgumentException::class)
  fun testNotNullOrEmptyOnEmpty() {

    notNullOrEmpty("", "name")
  }

  @Test(expected = IllegalArgumentException::class)
  fun testNotNullOrEmptyOnNull() {
    notNullOrEmpty(null, "name")
  }

  @Test
  fun testOneOfOnValid() {
    oneOf("hi", "name", "hi", "there")
  }

  @Test(expected = IllegalArgumentException::class)
  fun testOneOfOnInvalid() {

    oneOf("hit", "name", "hi", "there")
  }

  @Test
  fun testOneOfOnValidNull() {
    oneOf(null, "name", "hi", "there", null)
  }

  @Test(expected = IllegalArgumentException::class)
  fun testOneOfOnInvalidNull() {

    oneOf(null, "name", "hi", "there")
  }

  @Test
  fun testHasAppID() {
    whenever(FacebookSdk.getApplicationId()).thenReturn(appID)
    assertThat(hasAppID()).isEqualTo(appID)
  }

  @Test(expected = IllegalStateException::class)
  fun testHasNoAppID() {
    whenever(FacebookSdk.getApplicationId()).thenReturn(null)
    hasAppID()
  }

  @Test
  fun testHasClientToken() {
    whenever(FacebookSdk.getClientToken()).thenReturn(clientToken)
    assertThat(hasClientToken()).isEqualTo(clientToken)
  }

  @Test(expected = IllegalStateException::class)
  fun testHasNotClientToken() {
    whenever(FacebookSdk.getClientToken()).thenReturn(null)
    hasClientToken()
  }

  @Test
  fun `test hasCustomTabRedirectActivity will query activities for action view`() {
    val redirectUri = "http://facebook.com"
    Validate.hasCustomTabRedirectActivity(mockContext, redirectUri)

    val intentCaptor = argumentCaptor<Intent>()
    verify(mockPackageManager).queryIntentActivities(intentCaptor.capture(), any())
    val capturedIntent = intentCaptor.firstValue
    assertThat(capturedIntent.action).isEqualTo(Intent.ACTION_VIEW)
    assertThat(capturedIntent.data.toString()).isEqualTo(redirectUri)
    assertThat(capturedIntent.hasCategory(Intent.CATEGORY_BROWSABLE)).isTrue
  }

  @Test
  fun `test hasCustomTabRedirectActivity returns true if CustomTabActivity is available in the package`() {
    val redirectUri = "http://facebook.com"
    val mockActivityInfo = mock<ActivityInfo>()
    mockActivityInfo.name = "com.facebook.CustomTabActivity"
    mockActivityInfo.packageName = packageName
    val mockResolveInfo = mock<ResolveInfo>()
    mockResolveInfo.activityInfo = mockActivityInfo
    whenever(mockPackageManager.queryIntentActivities(any(), any()))
        .thenReturn(mutableListOf(mockResolveInfo))

    assertThat(Validate.hasCustomTabRedirectActivity(mockContext, redirectUri)).isTrue
  }

  @Test
  fun `test hasCustomTabRedirectActivity returns false if CustomTabActivity is available in another package`() {
    val redirectUri = "http://facebook.com"
    val mockActivityInfo = mock<ActivityInfo>()
    mockActivityInfo.name = "com.facebook.CustomTabActivity"
    mockActivityInfo.packageName = "com.anotherapp"
    val mockResolveInfo = mock<ResolveInfo>()
    mockResolveInfo.activityInfo = mockActivityInfo
    whenever(mockPackageManager.queryIntentActivities(any(), any()))
        .thenReturn(mutableListOf(mockResolveInfo))

    assertThat(Validate.hasCustomTabRedirectActivity(mockContext, redirectUri)).isFalse
  }
}