// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.document;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/** Tests for launching Chrome. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class LauncherActivityTest {
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
private Context mContext;
private static final long DEVICE_STARTUP_TIMEOUT_MS = 15000L;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
}
@Test
@SmallTest
public void testLaunchWithUrlNoScheme() {
// Prepare intent
final String intentUrl = "www.google.com";
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(intentUrl));
intent.setClassName(mContext.getPackageName(), ChromeLauncherActivity.class.getName());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final Activity startedActivity = tryLaunchingChrome(intent);
final Intent activityIntent = startedActivity.getIntent();
Assert.assertEquals(intentUrl, activityIntent.getDataString());
}
@Test
@SmallTest
public void testDoesNotCrashWithBadParcel() {
// Prepare bad intent
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
final Parcel parcel = Parcel.obtain();
// Force unparcelling within ChromeLauncherActivity. Writing and reading from a parcel will
// simulate being parcelled by another application, and thus cause unmarshalling when
// Chrome tries reading an extra the next time.
intent.writeToParcel(parcel, 0);
intent.readFromParcel(parcel);
intent.setClassName(mContext.getPackageName(), ChromeLauncherActivity.class.getName());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("BadExtra", new InvalidParcelable());
final Activity startedActivity = tryLaunchingChrome(intent);
final Intent activityIntent = startedActivity.getIntent();
Assert.assertEquals("Data was not preserved", intent.getData(), activityIntent.getData());
Assert.assertEquals(
"Action was not preserved", intent.getAction(), activityIntent.getAction());
}
@Test
@SmallTest
public void testDoesNotCrashWithNoUriInViewIntent() {
// Prepare intent
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setClassName(mContext.getPackageName(), ChromeLauncherActivity.class.getName());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Could crash after the activity is created, wait for the tab to stop loading.
final ChromeActivity activity = (ChromeActivity) tryLaunchingChrome(intent);
CriteriaHelper.pollUiThread(
() -> {
Tab tab = activity.getActivityTab();
Criteria.checkThat(tab, Matchers.notNullValue());
Criteria.checkThat(tab.isLoading(), Matchers.is(false));
},
DEVICE_STARTUP_TIMEOUT_MS,
CriteriaHelper.DEFAULT_POLLING_INTERVAL);
}
private Activity tryLaunchingChrome(final Intent intent) {
mContext.startActivity(intent);
// Check that ChromeLauncher Activity successfully launched
CriteriaHelper.pollInstrumentationThread(
() -> {
Criteria.checkThat(
ApplicationStatus.getStateForApplication(),
Matchers.is(ApplicationState.HAS_RUNNING_ACTIVITIES));
});
// Check that Chrome proper was successfully launched as a follow-up
final AtomicReference<Activity> launchedActivity = new AtomicReference<>();
CriteriaHelper.pollInstrumentationThread(
() -> {
final List<Activity> activities = ApplicationStatus.getRunningActivities();
Criteria.checkThat(activities.size(), Matchers.is(1));
launchedActivity.set(activities.get(0));
Criteria.checkThat(
launchedActivity.get(), Matchers.instanceOf(ChromeActivity.class));
},
DEVICE_STARTUP_TIMEOUT_MS,
CriteriaHelper.DEFAULT_POLLING_INTERVAL);
return launchedActivity.get();
}
/**
* This Parcelable does not adhere to the form standards of a well formed Parcelable and will
* thus cause a BadParcelableException. The lint suppression is needed since it detects that
* this will throw a BadParcelableException.
*/
@SuppressLint("ParcelCreator")
@SuppressWarnings("ParcelableCreator")
private static class InvalidParcelable implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int params) {}
@Override
public int describeContents() {
return 0;
}
}
}