1 /* 2 * Copyright (C) 2024 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 com.google.jetpackcamera.feature.preview.ui 17 18 import androidx.compose.runtime.MutableState 19 import androidx.compose.runtime.mutableStateOf 20 import androidx.compose.ui.graphics.Color 21 import androidx.compose.ui.graphics.toArgb 22 import androidx.compose.ui.test.assertHeightIsAtLeast 23 import androidx.compose.ui.test.assertWidthIsAtLeast 24 import androidx.compose.ui.test.getBoundsInRoot 25 import androidx.compose.ui.test.hasTestTag 26 import androidx.compose.ui.test.junit4.createComposeRule 27 import androidx.compose.ui.test.onRoot 28 import androidx.compose.ui.unit.height 29 import androidx.compose.ui.unit.width 30 import com.google.jetpackcamera.feature.preview.ScreenFlash 31 import com.google.jetpackcamera.feature.preview.rules.MainDispatcherRule 32 import com.google.jetpackcamera.feature.preview.workaround.captureToImage 33 import kotlinx.coroutines.ExperimentalCoroutinesApi 34 import kotlinx.coroutines.test.StandardTestDispatcher 35 import kotlinx.coroutines.test.TestScope 36 import kotlinx.coroutines.test.advanceUntilIdle 37 import kotlinx.coroutines.test.runTest 38 import org.junit.Assert.assertEquals 39 import org.junit.Before 40 import org.junit.Rule 41 import org.junit.Test 42 import org.junit.runner.RunWith 43 import org.robolectric.RobolectricTestRunner 44 import org.robolectric.annotation.Config 45 import org.robolectric.annotation.GraphicsMode 46 import org.robolectric.shadows.ShadowPixelCopy 47 48 // TODO: After device tests are added to github workflow, remove the tests here since they are 49 // duplicated in androidTest and fits there better 50 @OptIn(ExperimentalCoroutinesApi::class) 51 @RunWith(RobolectricTestRunner::class) 52 class ScreenFlashComponentsKtTest { 53 private val testScope = TestScope() 54 private val testDispatcher = StandardTestDispatcher(testScope.testScheduler) 55 56 @get:Rule 57 val mainDispatcherRule = MainDispatcherRule(testDispatcher) 58 59 @get:Rule 60 val composeTestRule = createComposeRule() 61 62 private val screenFlashUiState: MutableState<ScreenFlash.ScreenFlashUiState> = 63 mutableStateOf(ScreenFlash.ScreenFlashUiState()) 64 65 @Before setUpnull66 fun setUp() { 67 composeTestRule.setContent { 68 ScreenFlashScreen( 69 screenFlashUiState = screenFlashUiState.value, 70 onInitialBrightnessCalculated = {} 71 ) 72 } 73 } 74 75 @Test <lambda>null76 fun screenFlashOverlay_doesNotExistByDefault() = runTest { 77 advanceUntilIdle() 78 composeTestRule.onNode(hasTestTag("ScreenFlashOverlay")).assertDoesNotExist() 79 } 80 81 @Test <lambda>null82 fun screenFlashOverlay_existsAfterStateIsEnabled() = runTest { 83 screenFlashUiState.value = ScreenFlash.ScreenFlashUiState(enabled = true) 84 85 advanceUntilIdle() 86 composeTestRule.onNode(hasTestTag("ScreenFlashOverlay")).assertExists() 87 } 88 89 @Test <lambda>null90 fun screenFlashOverlay_doesNotExistWhenDisabledAfterEnabled() = runTest { 91 screenFlashUiState.value = ScreenFlash.ScreenFlashUiState(enabled = true) 92 screenFlashUiState.value = ScreenFlash.ScreenFlashUiState(enabled = false) 93 94 advanceUntilIdle() 95 composeTestRule.onNode(hasTestTag("ScreenFlashOverlay")).assertDoesNotExist() 96 } 97 98 @Test <lambda>null99 fun screenFlashOverlay_sizeFillsMaxSize() = runTest { 100 screenFlashUiState.value = ScreenFlash.ScreenFlashUiState(enabled = true) 101 102 advanceUntilIdle() 103 val rootBounds = composeTestRule.onRoot().getBoundsInRoot() 104 composeTestRule.onNode(hasTestTag("ScreenFlashOverlay")) 105 .assertWidthIsAtLeast(rootBounds.width) 106 composeTestRule.onNode(hasTestTag("ScreenFlashOverlay")) 107 .assertHeightIsAtLeast(rootBounds.height) 108 } 109 110 @Test 111 @GraphicsMode(GraphicsMode.Mode.NATIVE) 112 @Config(shadows = [ShadowPixelCopy::class]) <lambda>null113 fun screenFlashOverlay_fullWhiteWhenEnabled() = runTest { 114 screenFlashUiState.value = ScreenFlash.ScreenFlashUiState(enabled = true) 115 116 advanceUntilIdle() 117 val overlayScreenShot = 118 composeTestRule.onNode(hasTestTag("ScreenFlashOverlay")).captureToImage() 119 120 // check a few pixels near center instead of whole image to save time 121 val overlayPixels = IntArray(4) 122 overlayScreenShot.readPixels( 123 overlayPixels, 124 overlayScreenShot.width / 2, 125 overlayScreenShot.height / 2, 126 2, 127 2 128 ) 129 overlayPixels.forEach { 130 assertEquals(Color.White.toArgb(), it) 131 } 132 } 133 } 134