1 /* 2 * Copyright (C) 2022 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 android.service.games; 18 19 import static androidx.test.espresso.Espresso.onView; 20 import static androidx.test.espresso.action.ViewActions.click; 21 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; 22 import static androidx.test.espresso.assertion.ViewAssertions.matches; 23 import static androidx.test.espresso.matcher.ViewMatchers.isClickable; 24 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; 25 import static androidx.test.espresso.matcher.ViewMatchers.withText; 26 import static androidx.test.ext.truth.content.IntentSubject.assertThat; 27 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 28 29 import static com.google.common.truth.Truth.assertThat; 30 31 import static org.hamcrest.Matchers.allOf; 32 33 import android.app.Activity; 34 import android.app.ActivityManager; 35 import android.content.Intent; 36 import android.os.Bundle; 37 import android.os.RemoteException; 38 import android.platform.test.annotations.Presubmit; 39 import android.testing.AndroidTestingRunner; 40 import android.widget.Button; 41 import android.widget.LinearLayout; 42 import android.widget.TextView; 43 44 import androidx.test.espresso.NoActivityResumedException; 45 import androidx.test.filters.FlakyTest; 46 import androidx.test.filters.SmallTest; 47 48 import com.android.internal.infra.AndroidFuture; 49 50 import com.google.common.util.concurrent.Uninterruptibles; 51 52 import org.junit.After; 53 import org.junit.Before; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 57 import java.time.Duration; 58 import java.util.concurrent.TimeUnit; 59 60 /** 61 * Unit tests for the {@link GameSessionTrampolineActivity}. 62 */ 63 @RunWith(AndroidTestingRunner.class) 64 @SmallTest 65 @Presubmit 66 public class GameSessionTrampolineActivityTest { 67 68 private static final Duration TEST_ACTIVITY_OPEN_DURATION = Duration.ofSeconds(5); 69 70 @Before setUp()71 public void setUp() { 72 setAlwaysFinishActivities(false); 73 } 74 75 @After tearDown()76 public void tearDown() { 77 setAlwaysFinishActivities(false); 78 } 79 80 @Test launch_launchesTargetActivity()81 public void launch_launchesTargetActivity() { 82 AndroidFuture<GameSessionActivityResult> unusedResultFuture = 83 startTestActivityViaGameSessionTrampolineActivity(); 84 85 TestActivityPage.assertPageIsLaunched(); 86 } 87 88 @Test 89 @FlakyTest(bugId = 245615459) launch_targetActivityFinishesSuccessfully_futureCompletedWithSameResults()90 public void launch_targetActivityFinishesSuccessfully_futureCompletedWithSameResults() { 91 AndroidFuture<GameSessionActivityResult> resultFuture = 92 startTestActivityViaGameSessionTrampolineActivity(); 93 94 TestActivityPage.assertPageIsLaunched(); 95 TestActivityPage.clickFinish(); 96 97 GameSessionActivityResult expectedResult = 98 new GameSessionActivityResult(Activity.RESULT_OK, TestActivity.RESULT_INTENT); 99 100 assertEquals(resultFuture, expectedResult); 101 102 TestActivityPage.assertPageIsNotLaunched(); 103 } 104 105 @Test 106 @FlakyTest(bugId = 245616660) launch_trampolineActivityProcessDeath_futureCompletedWithSameResults()107 public void launch_trampolineActivityProcessDeath_futureCompletedWithSameResults() { 108 setAlwaysFinishActivities(true); 109 110 AndroidFuture<GameSessionActivityResult> resultFuture = 111 startTestActivityViaGameSessionTrampolineActivity(); 112 113 TestActivityPage.assertPageIsLaunched(); 114 TestActivityPage.clickFinish(); 115 116 GameSessionActivityResult expectedResult = 117 new GameSessionActivityResult(Activity.RESULT_OK, TestActivity.RESULT_INTENT); 118 119 assertEquals(resultFuture, expectedResult); 120 121 TestActivityPage.assertPageIsNotLaunched(); 122 } 123 assertEquals( AndroidFuture<GameSessionActivityResult> actualFuture, GameSessionActivityResult expected)124 private static void assertEquals( 125 AndroidFuture<GameSessionActivityResult> actualFuture, 126 GameSessionActivityResult expected) { 127 try { 128 assertEquals(actualFuture.get(20, TimeUnit.SECONDS), expected); 129 } catch (Exception ex) { 130 throw new IllegalStateException(ex); 131 } 132 } 133 assertEquals( GameSessionActivityResult actual, GameSessionActivityResult expected)134 private static void assertEquals( 135 GameSessionActivityResult actual, 136 GameSessionActivityResult expected) { 137 assertThat(actual.getResultCode()).isEqualTo(expected.getResultCode()); 138 assertThat(actual.getData()).filtersEquallyTo(actual.getData()); 139 } 140 setAlwaysFinishActivities(boolean isEnabled)141 private static void setAlwaysFinishActivities(boolean isEnabled) { 142 try { 143 ActivityManager.getService().setAlwaysFinish(isEnabled); 144 } catch (RemoteException ex) { 145 throw new IllegalStateException(ex); 146 } 147 } 148 149 private static AndroidFuture<GameSessionActivityResult> startTestActivityViaGameSessionTrampolineActivity()150 startTestActivityViaGameSessionTrampolineActivity() { 151 Intent testActivityIntent = new Intent(); 152 testActivityIntent.setClass(getInstrumentation().getTargetContext(), TestActivity.class); 153 sleep(TEST_ACTIVITY_OPEN_DURATION); 154 155 return startGameSessionTrampolineActivity(testActivityIntent); 156 } 157 sleep(Duration sleepDuration)158 private static void sleep(Duration sleepDuration) { 159 Uninterruptibles.sleepUninterruptibly(sleepDuration.toMillis(), TimeUnit.MILLISECONDS); 160 } 161 startGameSessionTrampolineActivity( Intent targetIntent)162 private static AndroidFuture<GameSessionActivityResult> startGameSessionTrampolineActivity( 163 Intent targetIntent) { 164 AndroidFuture<GameSessionActivityResult> resultFuture = new AndroidFuture<>(); 165 Intent trampolineActivityIntent = GameSessionTrampolineActivity.createIntent(targetIntent, 166 null, resultFuture); 167 trampolineActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 168 getInstrumentation().getTargetContext().startActivity(trampolineActivityIntent); 169 getInstrumentation().waitForIdleSync(); 170 171 return resultFuture; 172 } 173 174 175 private static class TestActivityPage { TestActivityPage()176 private TestActivityPage() {} 177 assertPageIsLaunched()178 public static void assertPageIsLaunched() { 179 onView(withText(TestActivity.PAGE_TITLE_TEXT)).check(matches(isDisplayed())); 180 } 181 assertPageIsNotLaunched()182 public static void assertPageIsNotLaunched() { 183 try { 184 onView(withText(TestActivity.PAGE_TITLE_TEXT)).check(doesNotExist()); 185 } catch (NoActivityResumedException ex) { 186 // Do nothing 187 } 188 } 189 clickFinish()190 public static void clickFinish() { 191 onView(allOf(withText(TestActivity.FINISH_BUTTON_TEXT), isClickable())).perform( 192 click()); 193 getInstrumentation().waitForIdleSync(); 194 } 195 } 196 197 public static class TestActivity extends Activity { 198 private static final String PAGE_TITLE_TEXT = "GameSessionTestActivity"; 199 private static final String FINISH_BUTTON_TEXT = "Finish Test Activity"; 200 private static final Intent RESULT_INTENT = new Intent("com.test.action.VIEW"); 201 202 @Override onCreate(Bundle savedInstanceState)203 protected void onCreate(Bundle savedInstanceState) { 204 super.onCreate(savedInstanceState); 205 206 LinearLayout contentLayout = new LinearLayout(this); 207 contentLayout.setOrientation(LinearLayout.VERTICAL); 208 209 TextView titleTextView = new TextView(this); 210 titleTextView.setText(PAGE_TITLE_TEXT); 211 contentLayout.addView(titleTextView); 212 213 Button finishActivityButton = new Button(this); 214 finishActivityButton.setText(FINISH_BUTTON_TEXT); 215 finishActivityButton.setOnClickListener((unused) -> { 216 setResult(Activity.RESULT_OK, RESULT_INTENT); 217 finish(); 218 }); 219 220 221 contentLayout.addView(finishActivityButton); 222 setContentView(contentLayout); 223 } 224 } 225 } 226