blob: cac5944a40f430ad91ccde0de75fedef789233ea [file] [log] [blame]
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.core.client.impl;
import com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadTerminatedHandler;
import com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy;
import com.google.gwt.core.client.impl.AsyncFragmentLoader.Logger;
import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
/**
* Tests that the fragment loader requests the right fragments and logs the
* correct lightweight metrics under a variety of request patterns.
*/
public class AsyncFragmentLoaderTest extends TestCase {
private static class MockErrorHandler implements LoadTerminatedHandler {
private boolean wasCalled = false;
public boolean getWasCalled() {
return wasCalled;
}
public void loadTerminated(Throwable reason) {
wasCalled = true;
}
}
private static class MockLoadStrategy implements LoadingStrategy {
public final Map<Integer, LoadTerminatedHandler> errorHandlers = new HashMap<Integer, LoadTerminatedHandler>();
private List<Integer> loadRequests = new LinkedList<Integer>();
public void assertFragmentsRequested(int... expectedAry) {
List<Integer> actual = new ArrayList<Integer>(loadRequests);
loadRequests.clear();
List<Integer> expected = toList(expectedAry);
if (!sameContents(actual, expected)) {
fail("Expected= " + commaSeparated(expected) + "; actual="
+ commaSeparated(actual));
}
}
public void startLoadingFragment(int fragment,
LoadTerminatedHandler loadErrorHandler) {
errorHandlers.put(fragment, loadErrorHandler);
loadRequests.add(fragment);
}
private String commaSeparated(List<Integer> ary) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Integer x : ary) {
if (first) {
first = false;
} else {
sb.append(",");
}
sb.append(x);
}
return sb.toString();
}
private boolean sameContents(List<Integer> actual, List<Integer> expected) {
if (actual.size() != expected.size()) {
return false;
}
for (int i = 0; i < actual.size(); i++) {
if (!actual.get(i).equals(expected.get(i))) {
return false;
}
}
return true;
}
private List<Integer> toList(int[] ary) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < ary.length; i++) {
list.add(ary[i]);
}
return list;
}
}
private static class MockProgressEvent {
public final String eventGroup;
public final int fragment;
public final String type;
public MockProgressEvent(String eventGroup, String type, int fragment) {
this.eventGroup = eventGroup;
this.type = type;
this.fragment = fragment;
}
}
private static class MockProgressLogger implements Logger {
private Queue<MockProgressEvent> events = new LinkedList<MockProgressEvent>();
public void assertEvent(String eventGroup, String type, int fragment) {
MockProgressEvent event = events.remove();
assertEquals(eventGroup, event.eventGroup);
assertEquals(type, event.type);
assertEquals(fragment, event.fragment);
}
public void assertNoEvents() {
assertTrue("Expected no more progress events, but there are "
+ events.size(), events.size() == 0);
}
public void logEventProgress(String eventGroup, String type,
int fragment, int size) {
events.add(new MockProgressEvent(eventGroup, type, fragment));
}
}
private static final String BEGIN = "begin";
private static final String END = "end";
private static final String LEFTOVERS_DOWNLOAD = "leftoversDownload";
private static final LoadTerminatedHandler NULL_ERROR_HANDLER = new LoadTerminatedHandler() {
public void loadTerminated(Throwable reason) {
}
};
private static Throwable makeLoadFailedException() {
return new RuntimeException("Load Failed");
}
public void testBasics() {
MockLoadStrategy reqs = new MockLoadStrategy();
MockProgressLogger progress = new MockProgressLogger();
int numEntries = 5;
AsyncFragmentLoader loader = new AsyncFragmentLoader(numEntries,
new int[] {}, reqs, progress);
loader.inject(1, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(numEntries);
progress.assertEvent(LEFTOVERS_DOWNLOAD, BEGIN, numEntries);
loader.leftoversFragmentHasLoaded();
reqs.assertFragmentsRequested(1);
progress.assertEvent(LEFTOVERS_DOWNLOAD, END, numEntries);
progress.assertEvent("download1", BEGIN, 1);
loader.fragmentHasLoaded(1);
progress.assertEvent("download1", END, 1);
loader.inject(2, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(2);
progress.assertEvent("download2", BEGIN, 2);
loader.fragmentHasLoaded(2);
progress.assertEvent("download2", END, 2);
progress.assertNoEvents();
}
/**
* Check the behavior when there are download failures.
*/
public void testDownloadFailures() {
MockLoadStrategy reqs = new MockLoadStrategy();
MockProgressLogger progress = new MockProgressLogger();
int numEntries = 10;
AsyncFragmentLoader loader = new AsyncFragmentLoader(numEntries, new int[] {
1, 2, 3}, reqs, progress);
// request fragment 1
MockErrorHandler error1try1 = new MockErrorHandler();
loader.inject(1, error1try1);
reqs.assertFragmentsRequested(1);
progress.assertEvent("download1", BEGIN, 1);
// fragment 1 fails
loadFailed(reqs, 1);
assertTrue(error1try1.getWasCalled());
// try again on fragment 1
MockErrorHandler error1try2 = new MockErrorHandler();
loader.inject(1, error1try2);
reqs.assertFragmentsRequested(1);
progress.assertEvent("download1", BEGIN, 1);
// this time fragment 1 succeeds
loader.fragmentHasLoaded(1);
reqs.assertFragmentsRequested();
assertFalse(error1try2.getWasCalled());
progress.assertEvent("download1", END, 1);
// request a later initial fragment (3), and see what happens if an
// intermediate download fails
MockErrorHandler error3try1 = new MockErrorHandler();
loader.inject(3, error3try1);
reqs.assertFragmentsRequested(2);
progress.assertEvent("download2", BEGIN, 2);
loadFailed(reqs, 2);
assertTrue(error3try1.wasCalled);
// request both 3 and 5, and see what happens if
// the leftovers download fails
MockErrorHandler error3try2 = new MockErrorHandler();
MockErrorHandler error5try1 = new MockErrorHandler();
loader.inject(3, error3try2);
loader.inject(5, error5try1);
reqs.assertFragmentsRequested(2);
progress.assertEvent("download2", BEGIN, 2);
loader.fragmentHasLoaded(2);
reqs.assertFragmentsRequested(3);
progress.assertEvent("download2", END, 2);
progress.assertEvent("download3", BEGIN, 3);
loader.fragmentHasLoaded(3);
reqs.assertFragmentsRequested(numEntries);
progress.assertEvent("download3", END, 3);
progress.assertEvent(LEFTOVERS_DOWNLOAD, BEGIN, numEntries);
loadFailed(reqs, numEntries); // leftovers fails!
assertFalse(error3try2.getWasCalled()); // 3 should have succeeded
assertTrue(error5try1.getWasCalled()); // 5 failed
// now try 5 again, and have everything succeed
MockErrorHandler error5try2 = new MockErrorHandler();
loader.inject(5, error5try2);
reqs.assertFragmentsRequested(numEntries);
progress.assertEvent(LEFTOVERS_DOWNLOAD, BEGIN, numEntries);
loader.leftoversFragmentHasLoaded();
reqs.assertFragmentsRequested(5);
progress.assertEvent(LEFTOVERS_DOWNLOAD, END, numEntries);
progress.assertEvent("download5", BEGIN, 5);
loader.fragmentHasLoaded(5);
reqs.assertFragmentsRequested();
assertFalse(error5try2.getWasCalled());
progress.assertEvent("download5", END, 5);
// try 6 but have it fail
MockErrorHandler error6try1 = new MockErrorHandler();
loader.inject(6, error6try1);
reqs.assertFragmentsRequested(6);
progress.assertEvent("download6", BEGIN, 6);
loadFailed(reqs, 6);
assertTrue(error6try1.getWasCalled());
// try 7 and have it succeed
loader.inject(7, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(7);
progress.assertEvent("download7", BEGIN, 7);
loader.fragmentHasLoaded(7);
reqs.assertFragmentsRequested();
progress.assertEvent("download7", END, 7);
// try 6 again and have it succeed this time
MockErrorHandler error6try2 = new MockErrorHandler();
loader.inject(6, error6try2);
reqs.assertFragmentsRequested(6);
progress.assertEvent("download6", BEGIN, 6);
loader.fragmentHasLoaded(6);
reqs.assertFragmentsRequested();
assertFalse(error6try2.getWasCalled());
progress.assertEvent("download6", END, 6);
// a finish event should do nothing if the fragment has already succeeded
progress.assertNoEvents();
loadFailed(reqs, 6);
assertFalse(error6try2.getWasCalled());
progress.assertNoEvents();
}
public void testExclusivesLoadSequentially1() {
MockLoadStrategy reqs = new MockLoadStrategy();
MockProgressLogger progress = new MockProgressLogger();
int numEntries = 6;
AsyncFragmentLoader loader = new AsyncFragmentLoader(numEntries,
new int[] {}, reqs, progress);
// Load fragment 1
loader.inject(1, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(numEntries); // leftovers
progress.assertEvent("leftoversDownload", BEGIN, numEntries);
loader.fragmentHasLoaded(numEntries);
reqs.assertFragmentsRequested(1);
progress.assertEvent("leftoversDownload", END, numEntries);
progress.assertEvent("download1", BEGIN, 1);
loader.fragmentHasLoaded(1);
progress.assertEvent("download1", END, 1);
progress.assertNoEvents();
// Request 2 and 3 immediately
loader.inject(2, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(2);
progress.assertEvent("download2", BEGIN, 2);
loader.inject(3, NULL_ERROR_HANDLER);
progress.assertNoEvents(); // waiting on 2 to finish
// 2 loads, 3 should be requested
loader.fragmentHasLoaded(2);
reqs.assertFragmentsRequested(3);
progress.assertEvent("download2", END, 2);
progress.assertEvent("download3", BEGIN, 3);
progress.assertNoEvents();
// 3 loads
loader.fragmentHasLoaded(3);
progress.assertEvent("download3", END, 3);
progress.assertNoEvents();
}
public void testExclusivesLoadSequentially2() {
MockLoadStrategy reqs = new MockLoadStrategy();
MockProgressLogger progress = new MockProgressLogger();
int numEntries = 6;
AsyncFragmentLoader loader = new AsyncFragmentLoader(numEntries,
new int[] {}, reqs, progress);
// Request 1
loader.inject(1, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(numEntries);
progress.assertEvent("leftoversDownload", BEGIN, numEntries);
// Request 2, resulting in two fragments being queued behind the leftovers
// download
loader.inject(2, NULL_ERROR_HANDLER);
progress.assertNoEvents();
// Leftovers arrives, but only fragment 1 should initially be requested
loader.leftoversFragmentHasLoaded();
reqs.assertFragmentsRequested(1);
progress.assertEvent("leftoversDownload", END, numEntries);
progress.assertEvent("download1", BEGIN, 1);
progress.assertNoEvents();
// fragment 1 arrives, 2 requested
loader.fragmentHasLoaded(1);
reqs.assertFragmentsRequested(2);
progress.assertEvent("download1", END, 1);
progress.assertEvent("download2", BEGIN, 2);
// fragment 2 arrives, all done
loader.fragmentHasLoaded(2);
progress.assertEvent("download2", END, 2);
progress.assertNoEvents();
}
/**
* If only the first part of the initial load sequence is requested, then
* don't request more.
*/
public void testLoadingPartOfInitialSequence() {
MockLoadStrategy reqs = new MockLoadStrategy();
MockProgressLogger progress = new MockProgressLogger();
int numEntries = 6;
AsyncFragmentLoader loader = new AsyncFragmentLoader(numEntries, new int[] {
1, 2, 3}, reqs, progress);
loader.inject(1, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(1);
progress.assertEvent("download1", BEGIN, 1);
loader.fragmentHasLoaded(1);
reqs.assertFragmentsRequested(); // should stop
progress.assertEvent("download1", END, 1);
loader.inject(2, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(2);
progress.assertEvent("download2", BEGIN, 2);
loader.fragmentHasLoaded(2);
reqs.assertFragmentsRequested(); // again, should stop
progress.assertEvent("download2", END, 2);
loader.inject(3, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(3);
progress.assertEvent("download3", BEGIN, 3);
loader.fragmentHasLoaded(3);
reqs.assertFragmentsRequested();
progress.assertEvent("download3", END, 3);
progress.assertNoEvents();
// check that exclusives now load
loader.inject(5, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(numEntries);
progress.assertEvent(LEFTOVERS_DOWNLOAD, BEGIN, numEntries);
loader.fragmentHasLoaded(numEntries);
reqs.assertFragmentsRequested(5);
progress.assertEvent(LEFTOVERS_DOWNLOAD, END, numEntries);
progress.assertEvent("download5", BEGIN, 5);
loader.fragmentHasLoaded(5);
reqs.assertFragmentsRequested();
progress.assertEvent("download5", END, 5);
progress.assertNoEvents();
}
/**
* This test catches a case in an earlier version of AsyncFragmentLoader where
* AsyncFragmentLoader.waitingForInitialFragments could exhaust its available
* space.
*/
public void testOverflowInWaitingForInitialFragments() {
MockLoadStrategy reqs = new MockLoadStrategy();
MockProgressLogger progress = new MockProgressLogger();
int numEntries = 6;
AsyncFragmentLoader loader = new AsyncFragmentLoader(numEntries, new int[] {
1, 2, 3}, reqs, progress);
/*
* Repeatedly queue up extra downloads waiting on an initial and then fail.
*/
for (int i = 0; i < 10; i++) {
MockErrorHandler error = new MockErrorHandler();
loader.inject(4, error);
reqs.assertFragmentsRequested(1);
progress.assertEvent("download1", BEGIN, 1);
loadFailed(reqs, 1);
assertTrue(error.getWasCalled());
progress.assertNoEvents();
}
}
public void testPrefetch() {
MockLoadStrategy reqs = new MockLoadStrategy();
MockProgressLogger progress = new MockProgressLogger();
int numEntries = 20;
AsyncFragmentLoader loader = new AsyncFragmentLoader(numEntries, new int[] {
1, 2, 3}, reqs, progress);
loader.startPrefetching();
// request a prefetch of something in the initial load sequence
loader.setPrefetchQueue(2);
reqs.assertFragmentsRequested(1);
progress.assertEvent("download1", BEGIN, 1);
loader.fragmentHasLoaded(1);
reqs.assertFragmentsRequested(2);
progress.assertEvent("download1", END, 1);
progress.assertEvent("download2", BEGIN, 2);
loader.fragmentHasLoaded(2);
reqs.assertFragmentsRequested();
progress.assertEvent("download2", END, 2);
progress.assertNoEvents();
// request a prefetch of an exclusive
loader.setPrefetchQueue(4);
reqs.assertFragmentsRequested(3);
progress.assertEvent("download3", BEGIN, 3);
loader.fragmentHasLoaded(3);
reqs.assertFragmentsRequested(numEntries);
progress.assertEvent("download3", END, 3);
progress.assertEvent("leftoversDownload", BEGIN, numEntries);
loader.leftoversFragmentHasLoaded();
reqs.assertFragmentsRequested(4);
progress.assertEvent("leftoversDownload", END, numEntries);
progress.assertEvent("download4", BEGIN, 4);
loader.fragmentHasLoaded(4);
reqs.assertFragmentsRequested();
progress.assertEvent("download4", END, 4);
progress.assertNoEvents();
// request a prefetch, but check that an inject call takes priority
loader.setPrefetchQueue(5, 6);
reqs.assertFragmentsRequested(5);
progress.assertEvent("download5", BEGIN, 5);
loader.inject(7, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested();
progress.assertNoEvents();
loader.fragmentHasLoaded(5);
reqs.assertFragmentsRequested(7);
progress.assertEvent("download5", END, 5);
progress.assertEvent("download7", BEGIN, 7);
loader.fragmentHasLoaded(7);
reqs.assertFragmentsRequested(6);
progress.assertEvent("download7", END, 7);
progress.assertEvent("download6", BEGIN, 6);
loader.fragmentHasLoaded(6);
reqs.assertFragmentsRequested();
progress.assertEvent("download6", END, 6);
progress.assertNoEvents();
// request prefetches, then request different prefetches
loader.setPrefetchQueue(8, 9);
reqs.assertFragmentsRequested(8);
progress.assertEvent("download8", BEGIN, 8);
loader.setPrefetchQueue(10);
reqs.assertFragmentsRequested();
progress.assertNoEvents();
loader.fragmentHasLoaded(8);
reqs.assertFragmentsRequested(10);
progress.assertEvent("download8", END, 8);
progress.assertEvent("download10", BEGIN, 10);
loader.fragmentHasLoaded(10);
reqs.assertFragmentsRequested();
progress.assertEvent("download10", END, 10);
progress.assertNoEvents();
// request prefetches that have already been loaded
loader.setPrefetchQueue(1, 3, 7, 10);
reqs.assertFragmentsRequested();
progress.assertNoEvents();
}
/**
* Prefetch initial split points out of order.
*/
public void testPrefetchInitialsOutOfOrder() {
MockLoadStrategy reqs = new MockLoadStrategy();
MockProgressLogger progress = new MockProgressLogger();
int numEntries = 20;
AsyncFragmentLoader loader = new AsyncFragmentLoader(numEntries, new int[] {
1, 2, 3}, reqs, progress);
loader.startPrefetching();
// request a prefetch of something in the initial load sequence
loader.setPrefetchQueue(3, 2, 1);
reqs.assertFragmentsRequested(1);
progress.assertEvent("download1", BEGIN, 1);
loader.fragmentHasLoaded(1);
reqs.assertFragmentsRequested(2);
progress.assertEvent("download1", END, 1);
progress.assertEvent("download2", BEGIN, 2);
loader.fragmentHasLoaded(2);
reqs.assertFragmentsRequested(3);
progress.assertEvent("download2", END, 2);
progress.assertEvent("download3", BEGIN, 3);
loader.fragmentHasLoaded(3);
progress.assertEvent("download3", END, 3);
reqs.assertFragmentsRequested();
progress.assertNoEvents();
// check that the loader is in a sane state by downloading an exclusive
loader.inject(5, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(numEntries);
progress.assertEvent("leftoversDownload", BEGIN, numEntries);
loader.leftoversFragmentHasLoaded();
reqs.assertFragmentsRequested(5);
progress.assertEvent("leftoversDownload", END, numEntries);
progress.assertEvent("download5", BEGIN, 5);
loader.fragmentHasLoaded(5);
reqs.assertFragmentsRequested();
progress.assertEvent("download5", END, 5);
progress.assertNoEvents();
}
/**
* Test that no prefetching happens if prefetching is turned off.
*/
public void testPrefetchingDisabled() {
MockLoadStrategy reqs = new MockLoadStrategy();
MockProgressLogger progress = new MockProgressLogger();
int numEntries = 20;
AsyncFragmentLoader loader = new AsyncFragmentLoader(numEntries,
new int[] {}, reqs, progress);
loader.stopPrefetching();
// Prefetch 1, but leave prefetching off
loader.setPrefetchQueue(1);
reqs.assertFragmentsRequested();
progress.assertNoEvents();
// Inject 2, which should lead to leftovers and 2 loading
loader.inject(2, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(numEntries);
progress.assertEvent("leftoversDownload", BEGIN, numEntries);
loader.leftoversFragmentHasLoaded();
reqs.assertFragmentsRequested(2);
progress.assertEvent("leftoversDownload", END, numEntries);
progress.assertEvent("download2", BEGIN, 2);
loader.fragmentHasLoaded(2);
reqs.assertFragmentsRequested();
progress.assertEvent("download2", END, 2);
progress.assertNoEvents();
// Enable prefetching; now 1 should load
loader.startPrefetching();
reqs.assertFragmentsRequested(1);
progress.assertEvent("download1", BEGIN, 1);
loader.fragmentHasLoaded(1);
reqs.assertFragmentsRequested();
progress.assertEvent("download1", END, 1);
progress.assertNoEvents();
}
/**
* Test prefetching an item and then injecting it while the prefetch is in
* progress.
*/
public void testPrefetchThenInjectOfSame() {
MockLoadStrategy reqs = new MockLoadStrategy();
MockProgressLogger progress = new MockProgressLogger();
int numEntries = 20;
AsyncFragmentLoader loader = new AsyncFragmentLoader(numEntries,
new int[] {}, reqs, progress);
loader.startPrefetching();
// Load the leftovers and one fragment
loader.inject(1, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(numEntries);
progress.assertEvent("leftoversDownload", BEGIN, numEntries);
loader.leftoversFragmentHasLoaded();
reqs.assertFragmentsRequested(1);
progress.assertEvent("leftoversDownload", END, numEntries);
progress.assertEvent("download1", BEGIN, 1);
loader.fragmentHasLoaded(1);
reqs.assertFragmentsRequested();
progress.assertEvent("download1", END, 1);
progress.assertNoEvents();
// Start prefetching a fragment
loader.setPrefetchQueue(2);
reqs.assertFragmentsRequested(2);
progress.assertEvent("download2", BEGIN, 2);
// Inject the same fragment and another one
loader.inject(2, NULL_ERROR_HANDLER);
loader.inject(3, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested();
progress.assertNoEvents();
// Finish the fragment loads
loader.fragmentHasLoaded(2);
reqs.assertFragmentsRequested(3);
progress.assertEvent("download2", END, 2);
progress.assertEvent("download3", BEGIN, 3);
loader.fragmentHasLoaded(3);
reqs.assertFragmentsRequested();
progress.assertEvent("download3", END, 3);
progress.assertNoEvents();
}
/**
* A thorough exercise of loading with an initial load sequence specified.
*/
public void testWithInitialLoadSequence() {
MockLoadStrategy reqs = new MockLoadStrategy();
MockProgressLogger progress = new MockProgressLogger();
int numEntries = 6;
AsyncFragmentLoader loader = new AsyncFragmentLoader(numEntries, new int[] {
1, 2, 3}, reqs, progress);
loader.inject(1, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(1);
progress.assertEvent("download1", BEGIN, 1);
loader.inject(3, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(); // still waiting on fragment 1
progress.assertNoEvents();
loader.inject(5, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(); // still waiting on fragment 1
progress.assertNoEvents();
// say that 1 loads, which should trigger a chain of backlogged downloads
loader.fragmentHasLoaded(1);
reqs.assertFragmentsRequested(2); // next initial
progress.assertEvent("download1", END, 1);
progress.assertEvent("download2", BEGIN, 2);
loader.fragmentHasLoaded(2);
reqs.assertFragmentsRequested(3); // next initial
progress.assertEvent("download2", END, 2);
progress.assertEvent("download3", BEGIN, 3);
loader.fragmentHasLoaded(3);
reqs.assertFragmentsRequested(numEntries); // leftovers
progress.assertEvent("download3", END, 3);
progress.assertEvent("leftoversDownload", BEGIN, numEntries);
loader.leftoversFragmentHasLoaded();
reqs.assertFragmentsRequested(5);
progress.assertEvent("leftoversDownload", END, numEntries);
progress.assertEvent("download5", BEGIN, 5);
loader.fragmentHasLoaded(5);
reqs.assertFragmentsRequested(); // quiescent
progress.assertEvent("download5", END, 5);
progress.assertNoEvents();
// check that new exclusive fragment requests work
loader.inject(4, NULL_ERROR_HANDLER);
reqs.assertFragmentsRequested(4);
progress.assertEvent("download4", BEGIN, 4);
loader.fragmentHasLoaded(4);
reqs.assertFragmentsRequested();
progress.assertEvent("download4", END, 4);
progress.assertNoEvents();
}
private void loadFailed(MockLoadStrategy reqs, int fragment) {
reqs.errorHandlers.get(fragment).loadTerminated(makeLoadFailedException());
}
}