Skip to content

Commit

Permalink
Refactored CameraManager to not use ImageAnalysis as this caused mock…
Browse files Browse the repository at this point in the history
…ing problems when unit testing - now tests saying CameraX doesn't have a default version
  • Loading branch information
hugomilosz committed Jan 29, 2024
1 parent 98a6960 commit bb71460
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,23 @@ package com.google.android.fhir.document.scan

import android.content.Context
import android.content.pm.PackageManager
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.CameraProvider
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class CameraManager(
private val context: Context,
private val lifecycleOwner: LifecycleOwner,
val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor(),
) {
fun bindCamera(imageAnalysis: ImageAnalysis) {
private var cameraProvider: CameraProvider? = null

fun bindCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener(
{
val cameraProvider = cameraProviderFuture.get()
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, imageAnalysis)
cameraProvider = cameraProviderFuture.get()
},
ContextCompat.getMainExecutor(context),
)
Expand All @@ -50,4 +47,8 @@ class CameraManager(
internal fun hasCameraPermission(): Boolean {
return context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
}

fun getCameraProvider(): CameraProvider? {
return cameraProvider
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class SHLinkScannerImpl(
) {
if (cameraManager.hasCameraPermission()) {
try {
// Open camera and scan qr code
// Open camera and scan
val shLinkScanData = scan()
releaseScanner()
successCallback(shLinkScanData)
Expand All @@ -50,24 +50,18 @@ class SHLinkScannerImpl(
}

private fun scan(): SHLinkScanData {
// val imageAnalysis =
// ImageAnalysis.Builder()
// .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
// .build()

var scannedData: SHLinkScanData? = null

imageAnalysis.setAnalyzer(cameraManager.cameraExecutor) { imageProxy ->
barcodeDetectorManager.processImage(imageProxy) { result ->
// Handle the scanned value as needed
val scannedValue = result?.displayValue
if (scannedValue != null) {
scannedData = SHLinkScanData(scannedValue)
}
}
}

cameraManager.bindCamera(imageAnalysis)
cameraManager.bindCamera()

return scannedData ?: throw Error("No valid scan data found")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,69 @@
package com.google.android.fhir.document

import android.content.Context
import androidx.lifecycle.LifecycleOwner
import android.content.pm.PackageManager
import androidx.camera.lifecycle.ProcessCameraProvider
import com.google.android.fhir.document.scan.CameraManager
import java.util.concurrent.ExecutorService
import com.google.common.util.concurrent.ListenableFuture
import java.util.concurrent.Executors
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

@RunWith(RobolectricTestRunner::class)
@Config(manifest= Config.NONE)
class CameraManagerTest {

private lateinit var cameraManager: CameraManager
@Mock
private lateinit var mockContext: Context

@Mock private lateinit var context: Context
@Mock
private lateinit var mockCameraProviderFuture: ListenableFuture<ProcessCameraProvider>

@Mock private lateinit var lifecycleOwner: LifecycleOwner
@Mock
private lateinit var mockCameraProvider: ProcessCameraProvider

@Mock private lateinit var cameraExecutor: ExecutorService
private lateinit var cameraManager: CameraManager

@Before
fun setUp() {
fun setup() {
MockitoAnnotations.openMocks(this)
cameraManager = CameraManager(context, lifecycleOwner, cameraExecutor)
Mockito.`when`(mockContext.packageManager)
.thenReturn(Mockito.mock(PackageManager::class.java))
Mockito.`when`(ProcessCameraProvider.getInstance(mockContext))
.thenReturn(mockCameraProviderFuture)

cameraManager = CameraManager(mockContext, Executors.newSingleThreadExecutor())
}

@Test
fun `bindCamera sets CameraProvider when successful`() {
Mockito.`when`(mockCameraProviderFuture.get()).thenReturn(mockCameraProvider)
cameraManager.bindCamera()

val result = cameraManager.getCameraProvider()
assert(result != null)
assert(result === mockCameraProvider)
}

@Test fun canBindCameraToLifecycle() {}
@Test
fun `bindCamera does not set CameraProvider when unsuccessful`() {
Mockito.`when`(mockCameraProviderFuture.get()).thenThrow(RuntimeException("Failed to get CameraProvider"))

cameraManager.bindCamera()
val result = cameraManager.getCameraProvider()
assert(result == null)
}

@Test
fun releaseExecutorCanCorrectlyShutdownCameraExecutor() {
cameraManager.releaseExecutor()
Mockito.verify(cameraExecutor).shutdown()
// Mockito.verify(cameraExecutor).shutdown()
}

}

0 comments on commit bb71460

Please sign in to comment.