blob: 1bf84cc79b8968270420162bbb8269fd14c1cd35 [file] [log] [blame]
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.camera.camera2.pipe.compat
import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL
import android.hardware.camera2.CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL
import android.hardware.camera2.CaptureRequest
import android.os.Build
import android.os.Looper
import android.util.Size
import android.view.Surface
import androidx.camera.camera2.pipe.CameraGraph
import androidx.camera.camera2.pipe.CameraStream
import androidx.camera.camera2.pipe.Request
import androidx.camera.camera2.pipe.RequestTemplate
import androidx.camera.camera2.pipe.StreamFormat
import androidx.camera.camera2.pipe.graph.StreamGraphImpl
import androidx.camera.camera2.pipe.testing.FakeCameraDeviceWrapper
import androidx.camera.camera2.pipe.testing.FakeCaptureSequenceListener
import androidx.camera.camera2.pipe.testing.FakeThreads
import androidx.camera.camera2.pipe.testing.RobolectricCameraPipeTestRunner
import androidx.camera.camera2.pipe.testing.RobolectricCameras
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Shadows
import org.robolectric.annotation.Config
import org.robolectric.annotation.internal.DoNotInstrument
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(RobolectricCameraPipeTestRunner::class)
@DoNotInstrument
@Config(minSdk = Build.VERSION_CODES.P, maxSdk = 29)
internal class Camera2CaptureSequenceProcessorTest {
// TODO: This fails with "Failed to allocate native CameraMetadata" on robolectric prior
// to Android P. Update the test class to include support for older versions when a new
// version of robolectric is dropped into AndroidX.
private val mainLooper = Shadows.shadowOf(Looper.getMainLooper())
private val cameraId = RobolectricCameras.create(
mapOf(INFO_SUPPORTED_HARDWARE_LEVEL to INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
)
private val testCamera = RobolectricCameras.open(cameraId)
private val stream1Config = CameraStream.Config.create(
Size(640, 480),
StreamFormat.YUV_420_888
)
private val stream2Config = CameraStream.Config.create(
Size(1024, 768),
StreamFormat.RAW_PRIVATE
)
private val graphConfig = CameraGraph.Config(
camera = testCamera.cameraId,
streams = listOf(stream1Config, stream2Config),
)
private val streamGraph = StreamGraphImpl(
testCamera.metadata,
graphConfig
)
private val surface1 = Surface(
SurfaceTexture(0).also {
val output = stream1Config.outputs.first()
it.setDefaultBufferSize(output.size.width, output.size.height)
}
)
private val surface2 = Surface(
SurfaceTexture(0).also {
val output = stream2Config.outputs.first()
it.setDefaultBufferSize(output.size.width, output.size.height)
}
)
private val stream1 = streamGraph[stream1Config]!!
private val stream2 = streamGraph[stream2Config]!!
private val fakeCameraDevice = FakeCameraDeviceWrapper(testCamera)
private val fakeCaptureSession = fakeCameraDevice.createFakeCaptureSession()
@After
fun teardown() {
mainLooper.idle()
RobolectricCameras.clear()
}
@Test
fun robolectricCanBuildCaptureRequest() {
val requestBuilder = fakeCameraDevice.fakeCamera.cameraDevice.createCaptureRequest(1)
// Parameters
requestBuilder.set(
CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH
)
requestBuilder.set(
CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
)
requestBuilder.set(
CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO
)
// Surfaces
requestBuilder.addTarget(surface1)
requestBuilder.addTarget(surface2)
val request = requestBuilder.build()
assertThat(request).isNotNull()
// TODO: Add support for checking parameters when robolectric supports it.
// assertThat(request[CaptureRequest.CONTROL_AE_MODE])
// .isEqualTo(CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH)
// assertThat(request[CaptureRequest.CONTROL_AF_MODE])
// .isEqualTo(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO)
}
@Test
fun requestIsCreatedAndSubmitted() = runTest {
val captureSequenceProcessor = Camera2CaptureSequenceProcessor(
fakeCaptureSession,
FakeThreads.fromTestScope(this),
RequestTemplate(1),
mapOf(
stream1.id to surface1,
stream2.id to surface2
)
)
val sequence = captureSequenceProcessor.build(
isRepeating = false,
requests = listOf(Request(listOf(stream1.id, stream2.id))),
defaultParameters = mapOf<Any, Any?>(
CaptureRequest.CONTROL_AE_MODE to CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH,
CaptureRequest.CONTROL_AF_MODE to CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
),
requiredParameters = mapOf<Any, Any?>(
CaptureRequest.CONTROL_AF_MODE to CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO
),
listeners = listOf(),
sequenceListener = FakeCaptureSequenceListener()
)
val result = captureSequenceProcessor.submit(sequence!!)
assertThat(result).isGreaterThan(0)
assertThat(fakeCaptureSession.lastCapture).hasSize(1)
assertThat(fakeCaptureSession.lastRepeating).isNull()
// TODO: Add support for checking parameters when robolectric supports it.
}
@Test
fun requestIsSubmittedWithPartialSurfaces() = runTest {
val captureSequenceProcessor = Camera2CaptureSequenceProcessor(
fakeCaptureSession,
FakeThreads.fromTestScope(this),
RequestTemplate(1),
mapOf(
stream1.id to surface1
)
)
val captureSequence = captureSequenceProcessor.build(
isRepeating = false,
requests = listOf(Request(listOf(stream1.id, stream2.id))),
defaultParameters = mapOf<Any, Any?>(),
requiredParameters = mapOf<Any, Any?>(),
listeners = emptyList(),
sequenceListener = FakeCaptureSequenceListener()
)
assertThat(captureSequence).isNotNull()
val result = captureSequenceProcessor.submit(captureSequence!!)
assertThat(result).isGreaterThan(0)
}
@Test
fun requestIsNotSubmittedWithEmptySurfaceList() = runTest {
val captureSequenceProcessor = Camera2CaptureSequenceProcessor(
fakeCaptureSession,
FakeThreads.fromTestScope(this),
RequestTemplate(1),
mapOf(
stream1.id to surface1
)
)
// Key part is that only stream1 has a surface, but stream2 is requested.
val captureSequence = captureSequenceProcessor.build(
isRepeating = false,
requests = listOf(Request(listOf(stream2.id))),
defaultParameters = mapOf<Any, Any?>(),
requiredParameters = mapOf<Any, Any?>(),
listeners = emptyList(),
sequenceListener = FakeCaptureSequenceListener()
)
assertThat(captureSequence).isNull()
}
}