blob: 3d1559df49fb4bc00793a4f001300a397f3d9661 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// 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.customtabs.dynamicmodule;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.customtabs.CustomTabsCallback;
import android.support.test.InstrumentationRegistry;
import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.EXTRA_HIDE_CCT_HEADER_ON_MODULE_MANAGED_URLS;
import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.EXTRA_MODULE_CLASS_NAME;
import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.EXTRA_MODULE_MANAGED_HOST_LIST;
import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.EXTRA_MODULE_MANAGED_URLS_REGEX;
import static org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider.EXTRA_MODULE_PACKAGE_NAME;
import static org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleNavigationEventObserver.PENDING_URL_KEY;
import static org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleNavigationEventObserver.URL_KEY;
import org.junit.Assert;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.chrome.browser.AppHooksModule;
import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
import java.util.ArrayList;
import java.util.concurrent.TimeoutException;
/**
* Utility class that contains fake CCT dynamic module classes and convenience calls
* related with CCT dynamic module testing.
*/
public class CustomTabsDynamicModuleTestUtils {
/* package */ final static String FAKE_MODULE_PACKAGE_NAME =
InstrumentationRegistry.getInstrumentation().getContext().getPackageName();
/* package */ final static String FAKE_MODULE_CLASS_NAME = FakeCCTDynamicModule.class.getName();
/* package */ final static ComponentName FAKE_MODULE_COMPONENT_NAME = new ComponentName(
FAKE_MODULE_PACKAGE_NAME, FAKE_MODULE_CLASS_NAME);
private static int sModuleVersion = 1;
public static void setModuleVersion(int version) {
sModuleVersion = version;
}
/**
* To load a fake module in tests we need to bypass a check if package name of module
* is Google-signed. This class overrides this check for testing.
*/
/* package */ static class AppHooksModuleForTest extends AppHooksModule {
@Override
public ExternalAuthUtils provideExternalAuthUtils() {
return new ExternalAuthUtils() {
@Override
public boolean isGoogleSigned(String packageName) {
return true;
}
};
}
}
/**
* This class is used to test CCT module loader.
*/
public static class FakeCCTDynamicModule extends BaseModuleEntryPoint {
@Override
public void init(IModuleHost moduleHost) {
}
@Override
public int getModuleVersion() {
return sModuleVersion;
}
@Override
public int getMinimumHostVersion() {
return 0;
}
@Override
public IActivityDelegate createActivityDelegate(IActivityHost activityHost) {
return new FakeCCTActivityDelegate();
}
@Override
public void onDestroy() {
}
}
/**
* Fake class to test CCT dynamic module.
*/
public static class FakeCCTActivityDelegate extends BaseActivityDelegate {
private final CallbackHelper mOnNavigationStarted = new CallbackHelper();
private final CallbackHelper mOnNavigationFinished = new CallbackHelper();
public FakeCCTActivityDelegate() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
}
@Override
public void onResume() {
}
@Override
public void onPostCreate(Bundle savedInstanceState) {
}
@Override
public void onStart() {
}
@Override
public void onStop() {
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
}
@Override
public void onSaveInstanceState(Bundle outState) {
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
}
@Override
public void onPause() {
}
@Override
public void onDestroy() {
}
@Override
public boolean onBackPressed() {
return false;
}
@Override
public void onBackPressedAsync(IObjectWrapper notHandledRunnable) {
}
@Override
public void onMessageChannelReady() {
}
@Override
public void onPostMessage(String message) {
}
@Override
public void onNavigationEvent(int navigationEvent, Bundle extras) {
// Introduced in API version 4.
if (sModuleVersion < 4) {
Assert.fail("onNavigationEvent must not be used if module version less than 4");
}
if (navigationEvent == CustomTabsCallback.NAVIGATION_STARTED) {
Assert.assertNotNull(extras.get(PENDING_URL_KEY));
mOnNavigationStarted.notifyCalled();
} else if (navigationEvent == CustomTabsCallback.NAVIGATION_FINISHED) {
Assert.assertNotNull(extras.get(URL_KEY));
mOnNavigationFinished.notifyCalled();
}
}
/**
* Waits for expected number of navigation events happen.
*/
/* package */ void waitForNavigationEvent(int navigationEvent, int currentCallCount,
int numberOfCallsToWaitFor) throws TimeoutException, InterruptedException {
if (sModuleVersion < 4) return;
if (navigationEvent == CustomTabsCallback.NAVIGATION_STARTED) {
mOnNavigationStarted.waitForCallback(currentCallCount, numberOfCallsToWaitFor);
} else if (navigationEvent == CustomTabsCallback.NAVIGATION_FINISHED) {
mOnNavigationFinished.waitForCallback(currentCallCount, numberOfCallsToWaitFor);
}
}
}
/**
* Class to build an {@link Intent} to start a CCT with a dynamic module.
*
* By default the intent contains extras to load {@link FakeCCTDynamicModule}.
*/
/* package */ static class IntentBuilder {
private final Intent mIntent;
public Intent build() {
return mIntent;
}
IntentBuilder(String url) {
mIntent = CustomTabsTestUtils.createMinimalCustomTabIntent(
InstrumentationRegistry.getTargetContext(), url);
setModuleComponentName(FAKE_MODULE_COMPONENT_NAME);
}
IntentBuilder setModuleFailToLoadComponentName() {
setModulePackageName(FAKE_MODULE_PACKAGE_NAME);
setModuleClassName("ClassName");
return this;
}
IntentBuilder setModuleComponentName(ComponentName componentName) {
setModulePackageName(componentName.getPackageName());
setModuleClassName(componentName.getClassName());
return this;
}
IntentBuilder setModulePackageName(@Nullable String packageName) {
if (packageName == null) mIntent.removeExtra(EXTRA_MODULE_PACKAGE_NAME);
mIntent.putExtra(EXTRA_MODULE_PACKAGE_NAME, packageName);
return this;
}
IntentBuilder setModuleClassName(@Nullable String className) {
if (className == null) mIntent.removeExtra(EXTRA_MODULE_CLASS_NAME);
mIntent.putExtra(EXTRA_MODULE_CLASS_NAME, className);
return this;
}
IntentBuilder setModuleManagedUrlRegex(String urlRegex) {
mIntent.putExtra(EXTRA_MODULE_MANAGED_URLS_REGEX, urlRegex);
return this;
}
IntentBuilder setModuleHostList(ArrayList<String> moduleHostList) {
mIntent.putStringArrayListExtra(EXTRA_MODULE_MANAGED_HOST_LIST, moduleHostList);
return this;
}
IntentBuilder setHideCCTHeader(boolean isEnabled) {
mIntent.putExtra(EXTRA_HIDE_CCT_HEADER_ON_MODULE_MANAGED_URLS, isEnabled);
return this;
}
}
}