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