1 /* 2 * Copyright (C) 2023 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.car.carlauncher; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import android.app.Activity; 22 import android.app.Instrumentation; 23 import android.car.app.RemoteCarTaskView; 24 import android.car.test.mocks.AbstractExtendedMockitoTestCase; 25 import android.content.Context; 26 import android.content.Intent; 27 28 import androidx.core.app.ActivityCompat; 29 import androidx.test.ext.junit.rules.ActivityScenarioRule; 30 import androidx.test.ext.junit.runners.AndroidJUnit4; 31 import androidx.test.platform.app.InstrumentationRegistry; 32 33 import org.junit.After; 34 import org.junit.Before; 35 import org.junit.Rule; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 import org.mockito.Mock; 39 40 import java.util.concurrent.CountDownLatch; 41 import java.util.concurrent.TimeUnit; 42 43 @RunWith(AndroidJUnit4.class) 44 public final class CarLauncherViewModelTest extends AbstractExtendedMockitoTestCase { 45 private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); 46 private final Context mContext = mInstrumentation.getContext(); 47 48 @Rule 49 public final ActivityScenarioRule<TestActivity> mActivityRule = 50 new ActivityScenarioRule<>(TestActivity.class); 51 52 @Mock 53 private Intent mIntent; 54 55 private RemoteCarTaskView mRemoteCarTaskView; 56 private TestActivity mActivity; 57 58 59 @Before setUp()60 public void setUp() { 61 mActivityRule.getScenario().onActivity(activity -> mActivity = activity); 62 } 63 64 @After tearDown()65 public void tearDown() throws InterruptedException { 66 mActivityRule.getScenario().close(); 67 mActivity.finishCompletely(); 68 mRemoteCarTaskView = null; 69 } 70 71 @Test testOnConfigChange_sameRemoteCarTaskView()72 public void testOnConfigChange_sameRemoteCarTaskView() { 73 // Arrange 74 createCarLauncherViewModel(); 75 RemoteCarTaskView oldRemoteCarTaskView = mRemoteCarTaskView; 76 77 // Act 78 triggerActivityRecreation(); 79 80 // Assert 81 assertThat(oldRemoteCarTaskView).isSameInstanceAs(mRemoteCarTaskView); 82 } 83 84 @Test testViewModelOnCleared_clearsRemoteCarTaskView()85 public void testViewModelOnCleared_clearsRemoteCarTaskView() { 86 // Arrange 87 CarLauncherViewModel carLauncherViewModel = createCarLauncherViewModel(); 88 89 // Act 90 runOnMain(carLauncherViewModel::onCleared); 91 mInstrumentation.waitForIdleSync(); 92 93 // Assert 94 assertThat(mRemoteCarTaskView).isNull(); 95 } 96 createCarLauncherViewModel()97 private CarLauncherViewModel createCarLauncherViewModel() { 98 CarLauncherViewModel carLauncherViewModel = new CarLauncherViewModel(mActivity, mIntent); 99 runOnMain(() -> carLauncherViewModel.getRemoteCarTaskView().observeForever( 100 remoteCarTaskView -> mRemoteCarTaskView = remoteCarTaskView)); 101 mInstrumentation.waitForIdleSync(); 102 return carLauncherViewModel; 103 } 104 triggerActivityRecreation()105 private void triggerActivityRecreation() { 106 // Causes activity recreation with a new instance resulting in the same flow as 107 // activity being recreated due to a configuration change. 108 runOnMain(() -> ActivityCompat.recreate(mActivity)); 109 mInstrumentation.waitForIdleSync(); 110 } 111 runOnMain(Runnable runnable)112 private void runOnMain(Runnable runnable) { 113 mContext.getMainExecutor().execute(runnable); 114 } 115 116 117 public static class TestActivity extends Activity { 118 private static final int FINISH_TIMEOUT_MS = 1000; 119 private final CountDownLatch mDestroyed = new CountDownLatch(1); 120 121 @Override onDestroy()122 protected void onDestroy() { 123 super.onDestroy(); 124 mDestroyed.countDown(); 125 } 126 finishCompletely()127 void finishCompletely() throws InterruptedException { 128 finish(); 129 mDestroyed.await(FINISH_TIMEOUT_MS, TimeUnit.MILLISECONDS); 130 } 131 } 132 } 133