1 /* 2 * Copyright (C) 2020 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.deskclock.timer; 18 19 import android.content.Context; 20 import android.text.TextUtils; 21 import android.view.View; 22 import android.widget.TextView; 23 24 import androidx.annotation.IdRes; 25 import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner; 26 import androidx.test.platform.app.InstrumentationRegistry; 27 import androidx.test.rule.ActivityTestRule; 28 29 import com.android.deskclock.DeskClock; 30 import com.android.deskclock.R; 31 import com.android.deskclock.data.DataModel; 32 import com.android.deskclock.data.Timer; 33 import com.android.deskclock.widget.MockFabContainer; 34 35 import org.junit.After; 36 import org.junit.Before; 37 import org.junit.Rule; 38 import org.junit.Test; 39 import org.junit.runner.RunWith; 40 41 import java.util.Locale; 42 43 import static android.text.format.DateUtils.HOUR_IN_MILLIS; 44 import static android.text.format.DateUtils.MINUTE_IN_MILLIS; 45 import static android.text.format.DateUtils.SECOND_IN_MILLIS; 46 import static android.view.View.INVISIBLE; 47 import static android.view.View.VISIBLE; 48 import static org.junit.Assert.assertArrayEquals; 49 import static org.junit.Assert.assertEquals; 50 import static org.junit.Assert.assertFalse; 51 import static org.junit.Assert.assertNotNull; 52 import static org.junit.Assert.assertNull; 53 import static org.junit.Assert.assertTrue; 54 55 /** 56 * Exercise the user interface that collects new timer lengths. 57 */ 58 @RunWith(AndroidJUnit4ClassRunner.class) 59 public class TimerSetupViewTest { 60 61 private MockFabContainer fabContainer; 62 private TimerSetupView timerSetupView; 63 64 private TextView timeView; 65 private View deleteView; 66 67 private Locale defaultLocale; 68 69 @Rule 70 public ActivityTestRule<DeskClock> rule = new ActivityTestRule<>(DeskClock.class, true); 71 72 @Before setUp()73 public void setUp() { 74 defaultLocale = Locale.getDefault(); 75 Locale.setDefault(new Locale("en", "US")); 76 Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 77 final TimerFragment fragment = new TimerFragment(); 78 rule.getActivity().getSupportFragmentManager() 79 .beginTransaction().add(fragment, null).commit(); 80 Runnable selectTabRunnable = () -> { 81 fragment.selectTab(); 82 fabContainer = new MockFabContainer(fragment, context); 83 fragment.setFabContainer(fabContainer); 84 85 // Fetch the child views the tests will manipulate. 86 final View view = fragment.getView(); 87 assertNotNull(view); 88 89 timerSetupView = view.findViewById(R.id.timer_setup); 90 assertNotNull(timerSetupView); 91 assertEquals(VISIBLE, timerSetupView.getVisibility()); 92 93 timeView = timerSetupView.findViewById(R.id.timer_setup_time); 94 timeView.setActivated(true); 95 deleteView = timerSetupView.findViewById(R.id.timer_setup_delete); 96 }; 97 InstrumentationRegistry.getInstrumentation().runOnMainSync(selectTabRunnable); 98 } 99 100 @After tearDown()101 public void tearDown() { 102 fabContainer = null; 103 timerSetupView = null; 104 105 timeView = null; 106 deleteView = null; 107 108 Locale.setDefault(defaultLocale); 109 } 110 validateDefaultState()111 private void validateDefaultState() { 112 assertIsReset(); 113 } 114 115 @Test validateDefaultState_TimersExist()116 public void validateDefaultState_TimersExist() { 117 Runnable addTimerRunnable = () -> { 118 Timer timer = DataModel.getDataModel().addTimer(5000L, "", false); 119 validateDefaultState(); 120 DataModel.getDataModel().removeTimer(timer); 121 }; 122 InstrumentationRegistry.getInstrumentation().runOnMainSync(addTimerRunnable); 123 } 124 125 @Test validateDefaultState_NoTimersExist()126 public void validateDefaultState_NoTimersExist() { 127 Runnable runnable = () -> { 128 validateDefaultState(); 129 }; 130 InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable); 131 } 132 type0InDefaultState()133 private void type0InDefaultState() { 134 performClick(R.id.timer_setup_digit_0); 135 assertIsReset(); 136 } 137 138 @Test type0InDefaultState_TimersExist()139 public void type0InDefaultState_TimersExist() { 140 Runnable addTimerRunnable = () -> { 141 Timer timer = DataModel.getDataModel().addTimer(5000L, "", false); 142 type0InDefaultState(); 143 DataModel.getDataModel().removeTimer(timer); 144 }; 145 InstrumentationRegistry.getInstrumentation().runOnMainSync(addTimerRunnable); 146 } 147 148 @Test type0InDefaultState_NoTimersExist()149 public void type0InDefaultState_NoTimersExist() { 150 Runnable runnable = () -> { 151 type0InDefaultState(); 152 }; 153 InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable); 154 } 155 fillDisplayThenDeleteAll()156 private void fillDisplayThenDeleteAll() { 157 assertIsReset(); 158 // Fill the display. 159 performClick(R.id.timer_setup_digit_1); 160 assertHasValue(0, 0, 1); 161 performClick(R.id.timer_setup_digit_2); 162 assertHasValue(0, 0, 12); 163 performClick(R.id.timer_setup_digit_3); 164 assertHasValue(0, 1, 23); 165 performClick(R.id.timer_setup_digit_4); 166 assertHasValue(0, 12, 34); 167 performClick(R.id.timer_setup_digit_5); 168 assertHasValue(1, 23, 45); 169 performClick(R.id.timer_setup_digit_6); 170 assertHasValue(12, 34, 56); 171 172 // Typing another character is ignored. 173 performClick(R.id.timer_setup_digit_7); 174 assertHasValue(12, 34, 56); 175 performClick(R.id.timer_setup_digit_8); 176 assertHasValue(12, 34, 56); 177 178 // Delete everything in the display. 179 performClick(R.id.timer_setup_delete); 180 assertHasValue(1, 23, 45); 181 performClick(R.id.timer_setup_delete); 182 assertHasValue(0, 12, 34); 183 performClick(R.id.timer_setup_delete); 184 assertHasValue(0, 1, 23); 185 performClick(R.id.timer_setup_delete); 186 assertHasValue(0, 0, 12); 187 performClick(R.id.timer_setup_delete); 188 assertHasValue(0, 0, 1); 189 performClick(R.id.timer_setup_delete); 190 assertIsReset(); 191 } 192 193 @Test fillDisplayThenDeleteAll_TimersExist()194 public void fillDisplayThenDeleteAll_TimersExist() { 195 Runnable addTimerRunnable = () -> { 196 Timer timer = DataModel.getDataModel().addTimer(5000L, "", false); 197 fillDisplayThenDeleteAll(); 198 DataModel.getDataModel().removeTimer(timer); 199 }; 200 InstrumentationRegistry.getInstrumentation().runOnMainSync(addTimerRunnable); 201 } 202 203 @Test fillDisplayThenDeleteAll_NoTimersExist()204 public void fillDisplayThenDeleteAll_NoTimersExist() { 205 Runnable runnable = () -> { 206 fillDisplayThenDeleteAll(); 207 }; 208 InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable); 209 } 210 fillDisplayWith9s()211 private void fillDisplayWith9s() { 212 performClick(R.id.timer_setup_digit_9); 213 performClick(R.id.timer_setup_digit_9); 214 performClick(R.id.timer_setup_digit_9); 215 performClick(R.id.timer_setup_digit_9); 216 performClick(R.id.timer_setup_digit_9); 217 performClick(R.id.timer_setup_digit_9); 218 assertHasValue(99, 99, 99); 219 } 220 221 @Test fillDisplayWith9s_TimersExist()222 public void fillDisplayWith9s_TimersExist() { 223 Runnable addTimerRunnable = () -> { 224 Timer timer = DataModel.getDataModel().addTimer(5000L, "", false); 225 fillDisplayWith9s(); 226 DataModel.getDataModel().removeTimer(timer); 227 }; 228 InstrumentationRegistry.getInstrumentation().runOnMainSync(addTimerRunnable); 229 } 230 231 @Test fillDisplayWith9s_NoTimersExist()232 public void fillDisplayWith9s_NoTimersExist() { 233 Runnable runnable = () -> { 234 fillDisplayWith9s(); 235 }; 236 InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable); 237 } 238 assertIsReset()239 private void assertIsReset() { 240 assertStateEquals(0, 0, 0); 241 assertFalse(timerSetupView.hasValidInput()); 242 assertEquals(0, timerSetupView.getTimeInMillis()); 243 244 assertTrue(TextUtils.equals("00h 00m 00s", timeView.getText())); 245 assertTrue(TextUtils.equals("0 hours, 0 minutes, 0 seconds", 246 timeView.getContentDescription())); 247 248 assertFalse(deleteView.isEnabled()); 249 assertTrue(TextUtils.equals("Delete", deleteView.getContentDescription())); 250 251 final View fab = fabContainer.getFab(); 252 final TextView leftButton = fabContainer.getLeftButton(); 253 final TextView rightButton = fabContainer.getRightButton(); 254 255 if (DataModel.getDataModel().getTimers().isEmpty()) { 256 assertEquals(INVISIBLE, leftButton.getVisibility()); 257 } else { 258 assertEquals(VISIBLE, leftButton.getVisibility()); 259 assertTrue(TextUtils.equals("Cancel", leftButton.getText())); 260 } 261 262 assertNull(fab.getContentDescription()); 263 assertEquals(INVISIBLE, fab.getVisibility()); 264 assertEquals(INVISIBLE, rightButton.getVisibility()); 265 } 266 assertHasValue(int hours, int minutes, int seconds)267 private void assertHasValue(int hours, int minutes, int seconds) { 268 final long time = 269 hours * HOUR_IN_MILLIS + minutes * MINUTE_IN_MILLIS + seconds * SECOND_IN_MILLIS; 270 assertStateEquals(hours, minutes, seconds); 271 assertTrue(timerSetupView.hasValidInput()); 272 assertEquals(time, timerSetupView.getTimeInMillis()); 273 274 final String timeString = 275 String.format(Locale.US, "%02dh %02dm %02ds", hours, minutes, seconds); 276 assertTrue(TextUtils.equals(timeString, timeView.getText())); 277 278 assertTrue(deleteView.isEnabled()); 279 assertTrue(TextUtils.equals("Delete " + seconds % 10, deleteView.getContentDescription())); 280 281 final View fab = fabContainer.getFab(); 282 final TextView leftButton = fabContainer.getLeftButton(); 283 final TextView rightButton = fabContainer.getRightButton(); 284 285 if (DataModel.getDataModel().getTimers().isEmpty()) { 286 assertEquals(INVISIBLE, leftButton.getVisibility()); 287 } else { 288 assertEquals(VISIBLE, leftButton.getVisibility()); 289 assertTrue(TextUtils.equals("Cancel", leftButton.getText())); 290 } 291 292 assertEquals(VISIBLE, fab.getVisibility()); 293 assertTrue(TextUtils.equals("Start", fab.getContentDescription())); 294 assertEquals(INVISIBLE, rightButton.getVisibility()); 295 } 296 assertStateEquals(int hours, int minutes, int seconds)297 private void assertStateEquals(int hours, int minutes, int seconds) { 298 final int[] expected = { 299 seconds % 10, seconds / 10, minutes % 10, minutes / 10, hours % 10, hours / 10 300 }; 301 final int[] actual = (int[]) timerSetupView.getState(); 302 assertArrayEquals(expected, actual); 303 } 304 performClick(@dRes int id)305 private void performClick(@IdRes int id) { 306 final View view = timerSetupView.findViewById(id); 307 assertNotNull(view); 308 assertTrue(view.performClick()); 309 } 310 } 311