Snap for 8426163 from 0cafcaaffd9b9bd68dcafb4528dd3bcf8dfef1fc to mainline-tzdata2-release
Change-Id: I26c58f882e7a7a05cea58e31b6797517f280e27c
diff --git a/Android.bp b/Android.bp
index 6eeca34..83bdc35 100644
--- a/Android.bp
+++ b/Android.bp
@@ -23,7 +23,7 @@
srcs: ["src/**/*.java"],
sdk_version: "system_current",
min_sdk_version: "29",
- target_sdk_version: "30", // Keep in sync with CaptivePortalLoginTests
+ target_sdk_version: "30",
static_libs: [
"androidx.annotation_annotation",
"androidx.legacy_legacy-support-v4",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0ac691c..0b0c191 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -18,8 +18,8 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.captiveportallogin"
- android:versionCode="319999900"
- android:versionName="s_aml_319999900">
+ android:versionCode="309999900"
+ android:versionName="r_aml_309999900">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -49,7 +49,6 @@
android:name="com.android.captiveportallogin.CaptivePortalLoginActivity"
android:label="@string/action_bar_label"
android:theme="@style/AppTheme"
- android:exported="true"
android:configChanges="keyboardHidden|orientation|screenSize" >
<intent-filter>
<action android:name="android.net.conn.CAPTIVE_PORTAL"/>
diff --git a/TEST_MAPPING b/TEST_MAPPING
index cd9448b..c2cd949 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,10 +1,10 @@
{
- "presubmit": [
+ "postsubmit": [
{
"name": "CaptivePortalLoginTests"
}
],
- "mainline-presubmit": [
+ "mainline-postsubmit": [
{
"name": "CaptivePortalLoginTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
}
diff --git a/src/com/android/captiveportallogin/DownloadService.java b/src/com/android/captiveportallogin/DownloadService.java
index e9569db..da8eb2c 100644
--- a/src/com/android/captiveportallogin/DownloadService.java
+++ b/src/com/android/captiveportallogin/DownloadService.java
@@ -430,7 +430,7 @@
.setIdentifier(String.valueOf(taskId));
final PendingIntent pendingIntent = PendingIntent.getActivity(
- this, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE);
+ this, 0 /* requestCode */, intent, 0 /* flags */);
return new Notification.Builder(this, CHANNEL_DOWNLOADS)
.setContentTitle(getResources().getString(R.string.download_completed))
.setContentText(displayName)
diff --git a/tests/Android.bp b/tests/Android.bp
index d6f2a95..78b10c3 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -20,13 +20,10 @@
android_test {
name: "CaptivePortalLoginTests",
- defaults: ["framework-connectivity-test-defaults"],
srcs: ["src/**/*.java", "src/**/*.kt"],
platform_apis: true,
min_sdk_version: "29",
- target_sdk_version: "30", // Keep in sync with CaptivePortalLogin
- test_suites: ["general-tests"],
- test_mainline_modules: ["CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex"],
+ test_suites: ["device-tests"],
static_libs: [
"androidx.test.rules",
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index b6f157f..a263944 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -18,6 +18,8 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.captiveportallogin.tests">
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<application android:debuggable="true">
diff --git a/tests/src/com/android/captiveportallogin/CaptivePortalLoginActivityTest.java b/tests/src/com/android/captiveportallogin/CaptivePortalLoginActivityTest.java
index 4bf622e..3cd349a 100644
--- a/tests/src/com/android/captiveportallogin/CaptivePortalLoginActivityTest.java
+++ b/tests/src/com/android/captiveportallogin/CaptivePortalLoginActivityTest.java
@@ -27,7 +27,6 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
-import static androidx.lifecycle.Lifecycle.State.DESTROYED;
import static androidx.test.espresso.intent.Intents.intending;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
import static androidx.test.espresso.intent.matcher.IntentMatchers.isInternal;
@@ -39,12 +38,12 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
-import static com.android.testutils.TestPermissionUtil.runAsShell;
import static junit.framework.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.any;
@@ -79,6 +78,7 @@
import androidx.test.core.app.ActivityScenario;
import androidx.test.espresso.intent.Intents;
+import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.espresso.web.webdriver.Locator;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
@@ -88,6 +88,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
@@ -103,8 +104,6 @@
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BooleanSupplier;
import fi.iki.elonen.NanoHTTPD;
@@ -122,8 +121,7 @@
InetAddresses.parseNumericAddress("2001:db8::8"), 64);
private static final String TEST_USERAGENT = "Test/42.0 Unit-test";
private static final String TEST_FRIENDLY_NAME = "Network friendly name";
- private static final String TEST_PORTAL_HOSTNAME = "localhost";
- private ActivityScenario<InstrumentedCaptivePortalLoginActivity> mActivityScenario;
+ private InstrumentedCaptivePortalLoginActivity mActivity;
private MockitoSession mSession;
private Network mNetwork = new Network(TEST_NETID);
private TestNetworkTracker mTestNetworkTracker;
@@ -135,9 +133,6 @@
public static class InstrumentedCaptivePortalLoginActivity extends CaptivePortalLoginActivity {
private final ConditionVariable mDestroyedCv = new ConditionVariable(false);
private final CompletableFuture<Intent> mForegroundServiceStart = new CompletableFuture<>();
- // Workaround for https://github.com/android/android-test/issues/1119
- private final CompletableFuture<Intent> mOpenInBrowserIntent =
- new CompletableFuture<>();
@Override
public Object getSystemService(String name) {
switch (name) {
@@ -166,15 +161,8 @@
mDestroyedCv.open();
}
- @Override
- public void startActivity(Intent intent) {
- if (Intent.ACTION_VIEW.equals(intent.getAction())
- && intent.getData() != null
- && intent.getData().getAuthority().startsWith(TEST_PORTAL_HOSTNAME)) {
- mOpenInBrowserIntent.complete(intent);
- return;
- }
- super.startActivity(intent);
+ void waitForDestroy(long timeoutMs) {
+ assertTrue("Activity not destroyed within timeout", mDestroyedCv.block(timeoutMs));
}
}
@@ -229,6 +217,11 @@
};
}
+ @Rule
+ public final IntentsTestRule mActivityRule =
+ new IntentsTestRule<>(InstrumentedCaptivePortalLoginActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
+
@Before
public void setUp() throws Exception {
final Context context = getInstrumentation().getContext();
@@ -277,68 +270,51 @@
@After
public void tearDown() throws Exception {
- if (mActivityScenario != null) {
- // Note this may sometimes block for 45 seconds until
- // https://github.com/android/android-test/issues/676 is fixed
- mActivityScenario.close();
- Intents.release();
- }
+ mActivityRule.finishActivity();
+ if (mActivity != null) mActivity.waitForDestroy(TEST_TIMEOUT_MS);
getInstrumentation().getContext().getSystemService(ConnectivityManager.class)
.bindProcessToNetwork(null);
- if (mTestNetworkTracker != null) {
- runAsShell(MANAGE_TEST_NETWORKS, mTestNetworkTracker::teardown);
- }
+ if (mTestNetworkTracker != null) mTestNetworkTracker.teardown();
// finish mocking after the activity has terminated to avoid races on teardown.
mSession.finishMocking();
}
private void initActivity(String url) {
- final Context ctx = getInstrumentation().getContext();
- mActivityScenario = ActivityScenario.launch(
- new Intent(ctx, InstrumentedCaptivePortalLoginActivity.class)
- .setAction(ACTION_CAPTIVE_PORTAL_SIGN_IN)
- .putExtra(EXTRA_CAPTIVE_PORTAL_URL, url)
- .putExtra(EXTRA_NETWORK, mNetwork)
- .putExtra(EXTRA_CAPTIVE_PORTAL_USER_AGENT, TEST_USERAGENT)
- .putExtra(EXTRA_CAPTIVE_PORTAL, new MockCaptivePortal()));
- mActivityScenario.onActivity(activity -> {
- ctx.getSystemService(KeyguardManager.class).requestDismissKeyguard(activity, null);
- // Dismiss dialogs or notification shade, so the test can interact with the activity.
- activity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
- });
+ // onCreate will be triggered in launchActivity(). Handle mock objects after
+ // launchActivity() if any new mock objects. Activity launching flow will be
+ // 1. launchActivity()
+ // 2. onCreate()
+ // 3. end of launchActivity()
+ mActivity = (InstrumentedCaptivePortalLoginActivity) mActivityRule.launchActivity(
+ new Intent(ACTION_CAPTIVE_PORTAL_SIGN_IN)
+ .putExtra(EXTRA_CAPTIVE_PORTAL_URL, url)
+ .putExtra(EXTRA_NETWORK, mNetwork)
+ .putExtra(EXTRA_CAPTIVE_PORTAL_USER_AGENT, TEST_USERAGENT)
+ .putExtra(EXTRA_CAPTIVE_PORTAL, new MockCaptivePortal())
+ );
+ // Verify activity created successfully.
+ assertNotNull(mActivity);
+ getInstrumentation().getContext().getSystemService(KeyguardManager.class)
+ .requestDismissKeyguard(mActivity, null);
+ // Dismiss dialogs or notification shade, so that the test can interact with the activity.
+ mActivity.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
getInstrumentation().waitForIdleSync();
-
- // Initialize intent capturing after launching the activity to avoid capturing extra intents
- Intents.init();
}
@Test
public void testonCreateWithNullCaptivePortal() throws Exception {
- final Context ctx = getInstrumentation().getContext();
- final Intent intent = new Intent(ctx, InstrumentedCaptivePortalLoginActivity.class)
- .setAction(ACTION_CAPTIVE_PORTAL_SIGN_IN)
- .putExtra(EXTRA_CAPTIVE_PORTAL_URL, TEST_URL)
- .putExtra(EXTRA_NETWORK, mNetwork)
- .putExtra(EXTRA_CAPTIVE_PORTAL_USER_AGENT, TEST_USERAGENT)
- .putExtra(EXTRA_CAPTIVE_PORTAL, (Bundle) null);
- try (ActivityScenario<InstrumentedCaptivePortalLoginActivity> scenario =
- ActivityScenario.launch(intent)) {
- getInstrumentation().waitForIdleSync();
- // Verify that activity calls finish() immediately in its onCreate
- assertEquals(DESTROYED, scenario.getState());
- }
+ mActivity = (InstrumentedCaptivePortalLoginActivity) mActivityRule.launchActivity(
+ new Intent(ACTION_CAPTIVE_PORTAL_SIGN_IN)
+ .putExtra(EXTRA_CAPTIVE_PORTAL_URL, TEST_URL)
+ .putExtra(EXTRA_NETWORK, mNetwork)
+ .putExtra(EXTRA_CAPTIVE_PORTAL_USER_AGENT, TEST_USERAGENT)
+ .putExtra(EXTRA_CAPTIVE_PORTAL, (Bundle) null));
+ // Verify that activity is still created but waiting for closing.
+ assertNotNull(mActivity);
}
- /**
- * Get the activity MockCaptivePortal.
- *
- * The activity may use a different MockCaptivePortal instance after being recreated, so the
- * MockCaptivePortal should not be kept across possible activity recreation.
- */
private MockCaptivePortal getCaptivePortal() {
- final AtomicReference<MockCaptivePortal> portalRef = new AtomicReference<>();
- mActivityScenario.onActivity(a -> portalRef.set((MockCaptivePortal) a.mCaptivePortal));
- return portalRef.get();
+ return (MockCaptivePortal) mActivity.mCaptivePortal;
}
private void configNonVpnNetwork() {
@@ -378,21 +354,19 @@
initActivity(TEST_URL);
// Test non-vpn case.
configNonVpnNetwork();
- mActivityScenario.onActivity(activity -> assertFalse(activity.hasVpnNetwork()));
-
+ assertFalse(mActivity.hasVpnNetwork());
// Test vpn case.
configVpnNetwork();
- mActivityScenario.onActivity(activity -> assertTrue(activity.hasVpnNetwork()));
+ assertTrue(mActivity.hasVpnNetwork());
}
@Test
public void testIsAlwaysOnVpnEnabled() throws Exception {
initActivity(TEST_URL);
doReturn(false).when(sMockDevicePolicyManager).isAlwaysOnVpnLockdownEnabled(any());
- mActivityScenario.onActivity(activity -> assertFalse(activity.isAlwaysOnVpnEnabled()));
-
+ assertFalse(mActivity.isAlwaysOnVpnEnabled());
doReturn(true).when(sMockDevicePolicyManager).isAlwaysOnVpnLockdownEnabled(any());
- mActivityScenario.onActivity(activity -> assertTrue(activity.isAlwaysOnVpnEnabled()));
+ assertTrue(mActivity.isAlwaysOnVpnEnabled());
}
private void runVpnMsgOrLinkToBrowser(boolean useVpnMatcher) {
@@ -401,21 +375,18 @@
configNonVpnNetwork();
doReturn(false).when(sMockDevicePolicyManager).isAlwaysOnVpnLockdownEnabled(any());
final String linkMatcher = ".*<a[^>]+href.*";
- mActivityScenario.onActivity(act ->
- assertTrue(act.getWebViewClient().getVpnMsgOrLinkToBrowser().matches(linkMatcher)));
+ assertTrue(mActivity.getWebViewClient().getVpnMsgOrLinkToBrowser().matches(linkMatcher));
// Test has vpn case.
configVpnNetwork();
final String vpnMatcher = ".*<div.*vpnwarning.*";
- mActivityScenario.onActivity(act ->
- assertTrue(act.getWebViewClient().getVpnMsgOrLinkToBrowser().matches(vpnMatcher)));
+ assertTrue(mActivity.getWebViewClient().getVpnMsgOrLinkToBrowser().matches(vpnMatcher));
// Test always-on vpn case.
configNonVpnNetwork();
doReturn(true).when(sMockDevicePolicyManager).isAlwaysOnVpnLockdownEnabled(any());
- mActivityScenario.onActivity(act ->
- assertTrue(act.getWebViewClient().getVpnMsgOrLinkToBrowser().matches(
- (useVpnMatcher ? vpnMatcher : linkMatcher))));
+ assertTrue(mActivity.getWebViewClient().getVpnMsgOrLinkToBrowser().matches(
+ (useVpnMatcher ? vpnMatcher : linkMatcher)));
}
@Test @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.Q)
@@ -436,20 +407,22 @@
}
private void notifyCapabilitiesChanged(final NetworkCapabilities nc) {
- mActivityScenario.onActivity(a -> a.handleCapabilitiesChanged(mNetwork, nc));
+ mActivity.handleCapabilitiesChanged(mNetwork, nc);
getInstrumentation().waitForIdleSync();
}
- private void notifyValidatedChangedAndDismissed(final NetworkCapabilities nc) {
- // Get the MockCaptivePortal before the activity destroys itself
+ private void verifyDismissed() {
final MockCaptivePortal cp = getCaptivePortal();
- notifyCapabilitiesChanged(nc);
-
assertEquals(cp.mDismissTimes, 1);
assertEquals(cp.mIgnoreTimes, 0);
assertEquals(cp.mUseTimes, 0);
}
+ private void notifyValidatedChangedAndDismissed(final NetworkCapabilities nc) {
+ notifyCapabilitiesChanged(nc);
+ verifyDismissed();
+ }
+
private void verifyNotDone() {
final MockCaptivePortal cp = getCaptivePortal();
assertEquals(cp.mDismissTimes, 0);
@@ -462,6 +435,13 @@
verifyNotDone();
}
+ private void verifyUseAsIs() {
+ final MockCaptivePortal cp = getCaptivePortal();
+ assertEquals(cp.mDismissTimes, 0);
+ assertEquals(cp.mIgnoreTimes, 0);
+ assertEquals(cp.mUseTimes, 1);
+ }
+
private void setDismissPortalInValidatedNetwork(final boolean enable) {
// Feature is enabled if the package version greater than configuration. Instead of reading
// the package version, use Long.MAX_VALUE to replace disable configuration and 1 for
@@ -542,28 +522,17 @@
final HttpServer server = runCustomSchemeTest("mailto:test@example.com");
assertEquals(0, Intents.getIntents().size());
- // Mockito intents cannot be used for an intent sent in onDestroy, due to
- // https://github.com/android/android-test/issues/1119
- final CompletableFuture<Intent> viewIntent = new CompletableFuture<>();
- mActivityScenario.onActivity(a -> a.mOpenInBrowserIntent.thenAccept(viewIntent::complete));
-
- final MockCaptivePortal cp = getCaptivePortal();
onWebView().withElement(findElement(Locator.ID, "continue_link"))
.perform(webClick());
- try {
- viewIntent.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (TimeoutException e) {
- throw new AssertionError(
- "Open in browser intent not received after " + TEST_TIMEOUT_MS + "ms", e);
- }
+ // The intent is sent in onDestroy(); there is no way to wait for that event, so poll
+ // until the intent is found.
+ assertTrue(isEventually(() -> Intents.getIntents().size() == 1, TEST_TIMEOUT_MS));
+ verifyUseAsIs();
+ final Intent sentIntent = Intents.getIntents().get(0);
+ assertEquals(Intent.ACTION_VIEW, sentIntent.getAction());
+ assertEquals(Uri.parse(server.makeUrl(TEST_URL_QUERY)), sentIntent.getData());
- getInstrumentation().waitForIdleSync();
- assertEquals(DESTROYED, mActivityScenario.getState());
-
- assertEquals(cp.mDismissTimes, 0);
- assertEquals(cp.mIgnoreTimes, 0);
- assertEquals(cp.mUseTimes, 1);
server.stop();
}
@@ -585,6 +554,7 @@
initActivity(server.makeUrl(TEST_URL_QUERY));
// Create a mock file to be returned when mocking the file chooser
+ final Context ctx = mActivity.getApplicationContext();
final Intent mockFileResponse = new Intent();
final Uri mockFile = Uri.parse("content://mockdata");
mockFileResponse.setData(mockFile);
@@ -608,10 +578,8 @@
assertEquals(filename, fileIntent.getStringExtra(Intent.EXTRA_TITLE));
// The download intent should be fired after the create file result is received
- final CompletableFuture<Intent> dlIntentFuture = new CompletableFuture<>();
- mActivityScenario.onActivity(a ->
- a.mForegroundServiceStart.thenAccept(dlIntentFuture::complete));
- final Intent dlIntent = dlIntentFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ final Intent dlIntent = mActivity.mForegroundServiceStart.get(
+ TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(DownloadService.class.getName(), dlIntent.getComponent().getClassName());
assertEquals(mNetwork, dlIntent.getParcelableExtra(DownloadService.ARG_NETWORK));
assertEquals(TEST_USERAGENT, dlIntent.getStringExtra(DownloadService.ARG_USERAGENT));
@@ -646,30 +614,26 @@
initActivity("https://tc.example.com/");
// Verify that the correct venue friendly name is used
- mActivityScenario.onActivity(activity ->
- assertEquals(getInstrumentation().getContext().getString(R.string.action_bar_title,
- TEST_FRIENDLY_NAME), activity.getActionBar().getTitle()));
+ assertEquals(getInstrumentation().getContext().getString(R.string.action_bar_title,
+ TEST_FRIENDLY_NAME), mActivity.getActionBar().getTitle());
}
@Test @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.Q)
public void testWifiSsid_Q() throws Exception {
configNonVpnNetwork();
initActivity("https://portal.example.com/");
- mActivityScenario.onActivity(activity ->
- assertEquals(activity.getActionBar().getTitle(),
- getInstrumentation().getContext().getString(R.string.action_bar_title,
- TEST_WIFIINFO_SSID)));
-
+ assertEquals(mActivity.getActionBar().getTitle(),
+ getInstrumentation().getContext().getString(R.string.action_bar_title,
+ TEST_WIFIINFO_SSID));
}
@Test @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
public void testWifiSsid() throws Exception {
configNonVpnNetwork();
initActivity("https://portal.example.com/");
- mActivityScenario.onActivity(activity ->
- assertEquals(activity.getActionBar().getTitle(),
- getInstrumentation().getContext().getString(R.string.action_bar_title,
- TEST_NC_SSID)));
+ assertEquals(mActivity.getActionBar().getTitle(),
+ getInstrumentation().getContext().getString(R.string.action_bar_title,
+ TEST_NC_SSID));
}
/**
@@ -728,7 +692,7 @@
private HttpServer(ServerSocket socket) {
// 0 as port for picking a port automatically
- super(TEST_PORTAL_HOSTNAME, 0);
+ super("localhost", 0);
mSocket = socket;
}
@@ -740,7 +704,7 @@
private String makeUrl(String query) {
return new Uri.Builder()
.scheme("http")
- .encodedAuthority(TEST_PORTAL_HOSTNAME + ":" + mSocket.getLocalPort())
+ .encodedAuthority("localhost:" + mSocket.getLocalPort())
// Explicitly specify an empty path to match the format of URLs returned by
// WebView (for example in onDownloadStart)
.path("/")
diff --git a/tests/src/com/android/captiveportallogin/DownloadServiceTest.kt b/tests/src/com/android/captiveportallogin/DownloadServiceTest.kt
index aba864a..7139e7c 100644
--- a/tests/src/com/android/captiveportallogin/DownloadServiceTest.kt
+++ b/tests/src/com/android/captiveportallogin/DownloadServiceTest.kt
@@ -54,6 +54,7 @@
import java.net.URL
import java.net.URLConnection
import java.nio.charset.StandardCharsets
+import java.text.NumberFormat
import java.util.concurrent.SynchronousQueue
import java.util.concurrent.TimeUnit.MILLISECONDS
import kotlin.math.min
@@ -287,6 +288,10 @@
assertTrue(TEST_FILESIZE / 100 > 0)
inputStream1.setAvailable(TEST_FILESIZE / 100)
+ // 1% progress should be shown in the notification
+ val progressText = NumberFormat.getPercentInstance().format(.01f)
+ findNotification(UiSelector().textContains(progressText))
+
// Setup the connection for the next download with indeterminate progress
val inputStream2 = TestInputStream()
doReturn(inputStream2).`when`(connection).inputStream