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 17 package com.android.systemui.haptics.slider.compose.ui 18 19 import androidx.compose.foundation.gestures.Orientation 20 import androidx.compose.foundation.interaction.DragInteraction 21 import androidx.compose.foundation.interaction.InteractionSource 22 import androidx.test.ext.junit.runners.AndroidJUnit4 23 import androidx.test.filters.SmallTest 24 import com.android.systemui.SysuiTestCase 25 import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig 26 import com.android.systemui.haptics.slider.SliderEventType 27 import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig 28 import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory 29 import com.android.systemui.kosmos.testScope 30 import com.android.systemui.lifecycle.activateIn 31 import com.android.systemui.testKosmos 32 import com.google.common.truth.Truth.assertThat 33 import kotlinx.coroutines.ExperimentalCoroutinesApi 34 import kotlinx.coroutines.flow.MutableStateFlow 35 import kotlinx.coroutines.flow.asStateFlow 36 import kotlinx.coroutines.test.runCurrent 37 import kotlinx.coroutines.test.runTest 38 import org.junit.Before 39 import org.junit.Test 40 import org.junit.runner.RunWith 41 42 @OptIn(ExperimentalCoroutinesApi::class) 43 @SmallTest 44 @RunWith(AndroidJUnit4::class) 45 class SliderHapticsViewModelTest : SysuiTestCase() { 46 47 private val kosmos = testKosmos() 48 private val testScope = kosmos.testScope 49 private val interactionSource = DragInteractionSourceTest() 50 private val underTest = 51 kosmos.sliderHapticsViewModelFactory.create( 52 interactionSource, 53 0f..1f, 54 Orientation.Horizontal, 55 SliderHapticFeedbackConfig(), 56 SeekableSliderTrackerConfig(), 57 ) 58 59 @Before setUpnull60 fun setUp() { 61 underTest.activateIn(testScope) 62 } 63 64 @Test onActivated_startsRunningnull65 fun onActivated_startsRunning() = 66 testScope.runTest { 67 // WHEN the view-model is activated 68 testScope.runCurrent() 69 70 // THEN the view-model starts running 71 assertThat(underTest.isRunning).isTrue() 72 } 73 74 @Test onDragStart_goesToUserStartedDraggingnull75 fun onDragStart_goesToUserStartedDragging() = 76 testScope.runTest { 77 // WHEN a drag interaction starts 78 interactionSource.setDragInteraction(DragInteraction.Start()) 79 runCurrent() 80 81 // THEN the current slider event type shows that the user started dragging 82 assertThat(underTest.currentSliderEventType) 83 .isEqualTo(SliderEventType.STARTED_TRACKING_TOUCH) 84 } 85 86 @Test onValueChange_whileUserStartedDragging_goesToUserDraggingnull87 fun onValueChange_whileUserStartedDragging_goesToUserDragging() = 88 testScope.runTest { 89 // WHEN a drag interaction starts 90 interactionSource.setDragInteraction(DragInteraction.Start()) 91 runCurrent() 92 93 // WHEN a value changes in the slider 94 underTest.onValueChange(0.5f) 95 96 // THEN the current slider event type shows that the user is dragging 97 assertThat(underTest.currentSliderEventType) 98 .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_USER) 99 } 100 101 @Test onValueChange_whileUserDragging_staysInUserDraggingnull102 fun onValueChange_whileUserDragging_staysInUserDragging() = 103 testScope.runTest { 104 // WHEN a drag interaction starts and the user keeps dragging 105 interactionSource.setDragInteraction(DragInteraction.Start()) 106 runCurrent() 107 underTest.onValueChange(0.5f) 108 109 // WHEN value changes continue to occur due to dragging 110 underTest.onValueChange(0.6f) 111 112 // THEN the current slider event type reflects that the user continues to drag 113 assertThat(underTest.currentSliderEventType) 114 .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_USER) 115 } 116 117 @Test onValueChange_whileNOTHING_goesToProgramStartedDraggingnull118 fun onValueChange_whileNOTHING_goesToProgramStartedDragging() = 119 testScope.runTest { 120 // WHEN a value change occurs without a drag interaction 121 underTest.onValueChange(0.5f) 122 123 // THEN the current slider event type shows that the program started dragging 124 assertThat(underTest.currentSliderEventType) 125 .isEqualTo(SliderEventType.STARTED_TRACKING_PROGRAM) 126 } 127 128 @Test onValueChange_whileProgramStartedDragging_goesToProgramDraggingnull129 fun onValueChange_whileProgramStartedDragging_goesToProgramDragging() = 130 testScope.runTest { 131 // WHEN the program starts dragging 132 underTest.onValueChange(0.5f) 133 134 // WHEN the program continues to make value changes 135 underTest.onValueChange(0.6f) 136 137 // THEN the current slider event type shows that program is dragging 138 assertThat(underTest.currentSliderEventType) 139 .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM) 140 } 141 142 @Test onValueChange_whileProgramDragging_staysInProgramDraggingnull143 fun onValueChange_whileProgramDragging_staysInProgramDragging() = 144 testScope.runTest { 145 // WHEN the program starts and continues to drag 146 underTest.onValueChange(0.5f) 147 underTest.onValueChange(0.6f) 148 149 // WHEN value changes continue to occur 150 underTest.onValueChange(0.7f) 151 152 // THEN the current slider event type shows that the program is dragging the slider 153 assertThat(underTest.currentSliderEventType) 154 .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM) 155 } 156 157 @Test onValueChangeEnded_goesToNOTHINGnull158 fun onValueChangeEnded_goesToNOTHING() = 159 testScope.runTest { 160 // WHEN changes end in the slider 161 underTest.onValueChangeEnded() 162 163 // THEN the current slider event type always resets to NOTHING 164 assertThat(underTest.currentSliderEventType).isEqualTo(SliderEventType.NOTHING) 165 } 166 167 private class DragInteractionSourceTest : InteractionSource { 168 private val _interactions = MutableStateFlow<DragInteraction>(IdleDrag) 169 override val interactions = _interactions.asStateFlow() 170 setDragInteractionnull171 fun setDragInteraction(interaction: DragInteraction) { 172 _interactions.value = interaction 173 } 174 } 175 176 private object IdleDrag : DragInteraction 177 } 178