blob: a4e65642de8509bf32b5d7915ed66d36d8a6d827 [file] [log] [blame]
// Copyright 2017 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.init;
import android.support.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils;
import org.chromium.base.task.PostTask;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.content_public.browser.UiThreadTaskTraits;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* Tests for {@link ChainedTasks}.
*/
@RunWith(BaseJUnit4ClassRunner.class)
public class ChainedTasksTest {
private static final long TIMEOUT_MS = 1000;
private static final class TestRunnable implements Runnable {
private final List<String> mLogMessages;
private final String mMessage;
public TestRunnable(List<String> logMessages, String message) {
mLogMessages = logMessages;
mMessage = message;
}
@Override
public void run() {
mLogMessages.add(mMessage);
}
}
@Test
@SmallTest
public void testCoalescedTasks() {
final List<String> expectedMessages =
Arrays.asList(new String[] {"First", "Second", "Third"});
final List<String> messages = new ArrayList<>();
final ChainedTasks tasks = new ChainedTasks();
for (String message : expectedMessages) tasks.add(new TestRunnable(messages, message));
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
tasks.start(true);
Assert.assertEquals(expectedMessages, messages);
}
});
}
@Test
@SmallTest
public void testCoalescedTasksDontBlockNonUiThread() throws Exception {
final Semaphore waitForIt = new Semaphore(0);
final Semaphore finished = new Semaphore(0);
final ChainedTasks tasks = new ChainedTasks();
tasks.add(new Runnable() {
@Override
public void run() {
try {
waitForIt.acquire();
} catch (InterruptedException e) {
throw new RuntimeException();
}
}
});
List<String> expectedMessages = Arrays.asList(new String[] {"First", "Second", "Third"});
final List<String> messages = new ArrayList<>();
for (String message : expectedMessages) tasks.add(new TestRunnable(messages, message));
tasks.add(new Runnable() {
@Override
public void run() {
finished.release();
}
});
tasks.start(true);
// If start() were blocking, then this would be a deadlock, as the first task acquires a
// semaphore that we are releasing later on the same thread.
waitForIt.release();
Assert.assertTrue(finished.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
Assert.assertEquals(expectedMessages, messages);
}
@Test
@SmallTest
public void testAsyncTasks() throws Exception {
List<String> expectedMessages = Arrays.asList(new String[] {"First", "Second", "Third"});
final List<String> messages = new ArrayList<>();
final ChainedTasks tasks = new ChainedTasks();
final Semaphore finished = new Semaphore(0);
for (String message : expectedMessages) tasks.add(new TestRunnable(messages, message));
tasks.add(new Runnable() {
@Override
public void run() {
finished.release();
}
});
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
tasks.start(false);
Assert.assertTrue("No task should run synchronously", messages.isEmpty());
}
});
Assert.assertTrue(finished.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
Assert.assertEquals(expectedMessages, messages);
}
@Test
@SmallTest
public void testAsyncTasksAreChained() throws Exception {
List<String> expectedMessages =
Arrays.asList(new String[] {"First", "Second", "High Priority", "Third"});
final List<String> messages = new ArrayList<>();
final ChainedTasks tasks = new ChainedTasks();
final Semaphore secondTaskFinished = new Semaphore(0);
final Semaphore waitForHighPriorityTask = new Semaphore(0);
final Semaphore finished = new Semaphore(0);
// Posts 2 tasks, waits for a high priority task to be posted from another thread, and
// carries on.
tasks.add(new TestRunnable(messages, "First"));
tasks.add(new TestRunnable(messages, "Second"));
tasks.add(new Runnable() {
@Override
public void run() {
try {
secondTaskFinished.release();
waitForHighPriorityTask.acquire();
} catch (InterruptedException e) {
Assert.fail();
}
}
});
tasks.add(new TestRunnable(messages, "Third"));
tasks.add(new Runnable() {
@Override
public void run() {
finished.release();
}
});
tasks.start(false);
Assert.assertTrue(secondTaskFinished.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
PostTask.postTask(UiThreadTaskTraits.DEFAULT, new TestRunnable(messages, "High Priority"));
waitForHighPriorityTask.release();
Assert.assertTrue(finished.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
Assert.assertEquals(expectedMessages, messages);
}
@Test
@SmallTest
public void testCanCancel() throws Exception {
List<String> expectedMessages = Arrays.asList(new String[] {"First", "Second"});
final List<String> messages = new ArrayList<>();
final ChainedTasks tasks = new ChainedTasks();
final Semaphore finished = new Semaphore(0);
tasks.add(new TestRunnable(messages, "First"));
tasks.add(new TestRunnable(messages, "Second"));
tasks.add(new Runnable() {
@Override
public void run() {
tasks.cancel();
}
});
tasks.add(new TestRunnable(messages, "Third"));
tasks.add(new Runnable() {
@Override
public void run() {
finished.release();
}
});
tasks.start(false);
Assert.assertFalse(finished.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
Assert.assertEquals(expectedMessages, messages);
}
@Test
@SmallTest
public void testThreadRestrictions() throws Exception {
ChainedTasks tasks = new ChainedTasks();
tasks.start(false);
try {
tasks.cancel();
Assert.fail("Cancel should not be callable from a non-UI thread");
} catch (IllegalStateException e) {
// Expected.
}
}
}