1 /* <lambda>null2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package androidx.camera.camera2.internal 17 18 import android.os.Build 19 import androidx.arch.core.executor.testing.InstantTaskExecutorRule 20 import androidx.camera.core.CameraState 21 import androidx.camera.core.CameraState.ERROR_CAMERA_IN_USE 22 import androidx.camera.core.CameraState.ERROR_MAX_CAMERAS_IN_USE 23 import androidx.camera.core.CameraState.StateError 24 import androidx.camera.core.CameraState.Type 25 import androidx.camera.core.impl.CameraInternal 26 import androidx.camera.core.impl.CameraStateRegistry 27 import androidx.camera.core.impl.utils.executor.CameraXExecutors 28 import androidx.camera.testing.fakes.FakeCamera 29 import androidx.camera.testing.impl.fakes.FakeCameraCoordinator 30 import androidx.lifecycle.Observer 31 import com.google.common.truth.Truth.assertThat 32 import org.junit.Rule 33 import org.junit.Test 34 import org.junit.runner.RunWith 35 import org.robolectric.RobolectricTestRunner 36 import org.robolectric.annotation.Config 37 import org.robolectric.annotation.internal.DoNotInstrument 38 39 @RunWith(RobolectricTestRunner::class) 40 @DoNotInstrument 41 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP) 42 internal class CameraStateMachineTest { 43 44 @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() 45 46 private val cameraCoordinator = FakeCameraCoordinator() 47 48 /** Wrapper method that initializes the required test parameters, then runs the test's body. */ 49 private fun runTest(body: (CameraStateMachine, StateObserver) -> Unit) { 50 val registry = CameraStateRegistry(cameraCoordinator, 1) 51 val stateMachine = CameraStateMachine(registry) 52 val stateObserver = StateObserver() 53 stateMachine.stateLiveData.observeForever(stateObserver) 54 55 // Test body 56 body.invoke(stateMachine, stateObserver) 57 58 stateMachine.stateLiveData.removeObserver(stateObserver) 59 } 60 61 @Test 62 fun shouldEmitClosedStateInitially() = runTest { _, stateObserver -> 63 stateObserver.assertHasState(CameraState.create(Type.CLOSED)).assertHasNoMoreStates() 64 } 65 66 @Test 67 fun shouldNotEmitNewState_whenStateHasNotChanged() = runTest { stateMachine, stateObserver -> 68 stateMachine.updateState(CameraInternal.State.OPENING, null) 69 stateMachine.updateState(CameraInternal.State.OPENING, null) 70 71 stateObserver 72 .assertHasState(CameraState.create(Type.CLOSED)) 73 .assertHasState(CameraState.create(Type.OPENING)) 74 .assertHasNoMoreStates() 75 } 76 77 @Test 78 fun shouldNotEmitNewState_whenStateAndErrorHaveNotChanged() = 79 runTest { stateMachine, stateObserver -> 80 stateMachine.updateState( 81 CameraInternal.State.OPENING, 82 StateError.create(ERROR_CAMERA_IN_USE) 83 ) 84 stateMachine.updateState( 85 CameraInternal.State.OPENING, 86 StateError.create(ERROR_CAMERA_IN_USE) 87 ) 88 89 stateObserver 90 .assertHasState(CameraState.create(Type.CLOSED)) 91 .assertHasState( 92 CameraState.create(Type.OPENING, StateError.create(ERROR_CAMERA_IN_USE)) 93 ) 94 .assertHasNoMoreStates() 95 } 96 97 @Test 98 fun shouldEmitNewState_whenStateChanges() = runTest { stateMachine, stateObserver -> 99 stateMachine.updateState(CameraInternal.State.OPENING, null) 100 stateMachine.updateState(CameraInternal.State.OPEN, null) 101 102 stateObserver 103 .assertHasState(CameraState.create(Type.CLOSED)) 104 .assertHasState(CameraState.create(Type.OPENING)) 105 .assertHasState(CameraState.create(Type.OPEN)) 106 .assertHasNoMoreStates() 107 } 108 109 @Test 110 fun shouldNotEmitNewState_whenInConfiguredState() = runTest { stateMachine, stateObserver -> 111 stateMachine.updateState(CameraInternal.State.OPENING, null) 112 stateMachine.updateState(CameraInternal.State.OPEN, null) 113 stateMachine.updateState(CameraInternal.State.CONFIGURED, null) 114 115 stateObserver 116 .assertHasState(CameraState.create(Type.CLOSED)) 117 .assertHasState(CameraState.create(Type.OPENING)) 118 .assertHasState(CameraState.create(Type.OPEN)) 119 .assertHasNoMoreStates() 120 } 121 122 @Test 123 fun shouldEmitNewState_whenErrorChanges() = runTest { stateMachine, stateObserver -> 124 stateMachine.updateState( 125 CameraInternal.State.OPENING, 126 StateError.create(ERROR_CAMERA_IN_USE) 127 ) 128 stateMachine.updateState( 129 CameraInternal.State.OPENING, 130 StateError.create(ERROR_MAX_CAMERAS_IN_USE) 131 ) 132 133 stateObserver 134 .assertHasState(CameraState.create(Type.CLOSED)) 135 .assertHasState( 136 CameraState.create(Type.OPENING, StateError.create(ERROR_CAMERA_IN_USE)) 137 ) 138 .assertHasState( 139 CameraState.create(Type.OPENING, StateError.create(ERROR_MAX_CAMERAS_IN_USE)) 140 ) 141 .assertHasNoMoreStates() 142 } 143 144 @Test 145 fun shouldEmitOpeningState_whenCameraIsOpening_whileAnotherIsClosing() { 146 val registry = CameraStateRegistry(cameraCoordinator, 1) 147 val stateMachine = CameraStateMachine(registry) 148 149 // Create, open then start closing first camera 150 val camera1 = FakeCamera() 151 registry.registerCamera(camera1, CameraXExecutors.directExecutor(), {}, {}) 152 registry.tryOpenCamera(camera1) 153 registry.markCameraState(camera1, CameraInternal.State.OPEN) 154 registry.markCameraState(camera1, CameraInternal.State.CLOSING) 155 156 // Create and try to open second camera. Since the first camera is still closing, its 157 // internal state will move to PENDING_OPEN 158 val camera2 = FakeCamera() 159 registry.registerCamera(camera2, CameraXExecutors.directExecutor(), {}, {}) 160 registry.tryOpenCamera(camera2) 161 registry.markCameraState(camera2, CameraInternal.State.PENDING_OPEN) 162 163 // Get the second camera's public state 164 stateMachine.updateState(CameraInternal.State.PENDING_OPEN, null) 165 val newState = stateMachine.stateLiveData.value 166 assertThat(newState).isEqualTo(CameraState.create(Type.OPENING)) 167 } 168 169 class StateObserver : Observer<CameraState> { 170 171 private val states = mutableListOf<CameraState>() 172 private var index = 0 173 174 override fun onChanged(value: CameraState) { 175 states.add(value) 176 } 177 178 fun assertHasState(expectedState: CameraState): StateObserver { 179 val state = states[index++] 180 assertThat(state).isEqualTo(expectedState) 181 return this 182 } 183 184 fun assertHasNoMoreStates() { 185 assertThat(index).isEqualTo(states.size) 186 } 187 } 188 } 189