| // 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.content.browser.scheduler; |
| |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.hamcrest.collection.IsIterableContainingInOrder.contains; |
| |
| import android.annotation.TargetApi; |
| import android.os.Build; |
| import android.support.test.filters.MediumTest; |
| |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import org.chromium.base.task.PostTask; |
| import org.chromium.base.task.TaskRunner; |
| import org.chromium.base.task.TaskTraits; |
| import org.chromium.base.test.BaseJUnit4ClassRunner; |
| import org.chromium.base.test.task.SchedulerTestHelpers; |
| import org.chromium.base.test.task.TaskSchedulerTestHelpers; |
| import org.chromium.base.test.util.MinAndroidSdkLevel; |
| import org.chromium.content.app.ContentMain; |
| import org.chromium.content_public.browser.test.NativeLibraryTestRule; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| /** |
| * Test class for {@link PostTask}. |
| * |
| * Due to layering concerns we can't test native backed task posting in base, so we do it here |
| * instead. |
| */ |
| @RunWith(BaseJUnit4ClassRunner.class) |
| @MinAndroidSdkLevel(23) |
| @TargetApi(Build.VERSION_CODES.M) |
| public class NativePostTaskTest { |
| @Rule |
| public NativeLibraryTestRule mNativeLibraryTestRule = new NativeLibraryTestRule(); |
| |
| @After |
| public void tearDown() { |
| TaskSchedulerTestHelpers.disableTaskSchedulerExecutionForTesting(); |
| } |
| |
| @Test |
| @MediumTest |
| public void testNativePostTask() throws Exception { |
| startNativeScheduler(); |
| |
| // This should not timeout. |
| final Object lock = new Object(); |
| final AtomicBoolean taskExecuted = new AtomicBoolean(); |
| PostTask.postTask(new TaskTraits(), new Runnable() { |
| @Override |
| public void run() { |
| synchronized (lock) { |
| taskExecuted.set(true); |
| lock.notify(); |
| } |
| } |
| }); |
| synchronized (lock) { |
| try { |
| while (!taskExecuted.get()) { |
| lock.wait(); |
| } |
| } catch (InterruptedException ie) { |
| ie.printStackTrace(); |
| } |
| } |
| } |
| |
| @Test |
| @MediumTest |
| public void testNativePostDelayedTask() throws Exception { |
| final Object lock = new Object(); |
| final AtomicBoolean taskExecuted = new AtomicBoolean(); |
| PostTask.postDelayedTask(new TaskTraits(), new Runnable() { |
| @Override |
| public void run() { |
| synchronized (lock) { |
| taskExecuted.set(true); |
| lock.notify(); |
| } |
| } |
| }, 1); |
| |
| // We verify that the task didn't get scheduled before the native scheduler is initialised |
| Assert.assertFalse(taskExecuted.get()); |
| startNativeScheduler(); |
| |
| try { |
| // The task should now be scheduled at some point after the delay, so the test shouldn't |
| // time out. |
| synchronized (lock) { |
| while (!taskExecuted.get()) { |
| lock.wait(); |
| } |
| } |
| } catch (InterruptedException ie) { |
| ie.printStackTrace(); |
| } |
| } |
| |
| @Test |
| @MediumTest |
| public void testCreateTaskRunner() throws Exception { |
| startNativeScheduler(); |
| TaskRunner taskQueue = PostTask.createTaskRunner(new TaskTraits()); |
| // This should not time out. |
| SchedulerTestHelpers.postDelayedTaskAndBlockUntilRun(taskQueue, 1); |
| } |
| |
| private void testRunningTasksInSequence(TaskRunner taskQueue) { |
| List<Integer> orderListImmediate = new ArrayList<>(); |
| List<Integer> orderListDelayed = new ArrayList<>(); |
| |
| SchedulerTestHelpers.postThreeTasksInOrder(taskQueue, orderListImmediate); |
| SchedulerTestHelpers.postTaskAndBlockUntilRun(taskQueue); |
| |
| assertThat(orderListImmediate, contains(1, 2, 3)); |
| |
| SchedulerTestHelpers.postThreeDelayedTasksInOrder(taskQueue, orderListDelayed); |
| SchedulerTestHelpers.postDelayedTaskAndBlockUntilRun(taskQueue, 1); |
| |
| assertThat(orderListDelayed, contains(1, 2, 3)); |
| } |
| |
| @Test |
| @MediumTest |
| public void testCreateSequencedTaskRunner() throws Exception { |
| startNativeScheduler(); |
| TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits()); |
| testRunningTasksInSequence(taskQueue); |
| } |
| |
| @Test |
| @MediumTest |
| public void testCreateSingleThreadSequencedTaskRunner() throws Exception { |
| startNativeScheduler(); |
| TaskRunner taskQueue = PostTask.createSingleThreadTaskRunner(new TaskTraits()); |
| testRunningTasksInSequence(taskQueue); |
| } |
| |
| private void performSequencedTestSchedulerMigration(TaskRunner taskQueue, |
| List<Integer> orderListImmediate, List<Integer> orderListDelayed) throws Exception { |
| SchedulerTestHelpers.postThreeTasksInOrder(taskQueue, orderListImmediate); |
| SchedulerTestHelpers.postThreeDelayedTasksInOrder(taskQueue, orderListDelayed); |
| |
| postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun(taskQueue, new Runnable() { |
| @Override |
| public void run() { |
| orderListImmediate.add(4); |
| } |
| }); |
| // We wait until all the delayed tasks have been scheduled. |
| SchedulerTestHelpers.postDelayedTaskAndBlockUntilRun(taskQueue, 1); |
| } |
| |
| @Test |
| @MediumTest |
| public void testCreateTaskRunnerMigrationToNative() throws Exception { |
| final Object lock = new Object(); |
| final AtomicBoolean taskExecuted = new AtomicBoolean(); |
| TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits()); |
| |
| taskQueue.postDelayedTask(new Runnable() { |
| @Override |
| public void run() { |
| synchronized (lock) { |
| taskExecuted.set(true); |
| lock.notify(); |
| } |
| } |
| }, 1); |
| |
| // We verify that the task didn't get scheduled before the native scheduler is initialised |
| Assert.assertFalse(taskExecuted.get()); |
| startNativeScheduler(); |
| |
| try { |
| // The task should now be scheduled at some point after the delay, so the test shouldn't |
| // time out. |
| synchronized (lock) { |
| while (!taskExecuted.get()) { |
| lock.wait(); |
| } |
| } |
| } catch (InterruptedException ie) { |
| ie.printStackTrace(); |
| } |
| } |
| |
| @Test |
| @MediumTest |
| public void testCreateSequencedTaskRunnerMigrationToNative() throws Exception { |
| List<Integer> orderListImmediate = new ArrayList<>(); |
| List<Integer> orderListDelayed = new ArrayList<>(); |
| TaskRunner taskQueue = PostTask.createSequencedTaskRunner(new TaskTraits()); |
| performSequencedTestSchedulerMigration(taskQueue, orderListImmediate, orderListDelayed); |
| |
| assertThat(orderListImmediate, contains(1, 2, 3, 4)); |
| assertThat(orderListDelayed, contains(1, 2, 3)); |
| } |
| |
| @Test |
| @MediumTest |
| public void testCreateSingleThreadSequencedTaskRunnerMigrationToNative() throws Exception { |
| List<Integer> orderListImmediate = new ArrayList<>(); |
| List<Integer> orderListDelayed = new ArrayList<>(); |
| TaskRunner taskQueue = PostTask.createSingleThreadTaskRunner(new TaskTraits()); |
| performSequencedTestSchedulerMigration(taskQueue, orderListImmediate, orderListDelayed); |
| |
| assertThat(orderListImmediate, contains(1, 2, 3, 4)); |
| assertThat(orderListDelayed, contains(1, 2, 3)); |
| } |
| |
| private void postRepeatingTaskAndStartNativeSchedulerThenWaitForTaskToRun( |
| TaskRunner taskQueue, Runnable taskToRunAfterNativeSchedulerLoaded) throws Exception { |
| final Object lock = new Object(); |
| final AtomicBoolean taskRun = new AtomicBoolean(); |
| final AtomicBoolean nativeSchedulerStarted = new AtomicBoolean(); |
| |
| // Post a task that reposts itself until nativeSchedulerStarted is set to true. This tests |
| // that tasks posted before the native library is loaded still run afterwards. |
| taskQueue.postTask(new Runnable() { |
| @Override |
| public void run() { |
| if (nativeSchedulerStarted.compareAndSet(true, true)) { |
| taskToRunAfterNativeSchedulerLoaded.run(); |
| synchronized (lock) { |
| taskRun.set(true); |
| lock.notify(); |
| } |
| } else { |
| taskQueue.postTask(this); |
| } |
| } |
| }); |
| |
| startNativeScheduler(); |
| nativeSchedulerStarted.set(true); |
| |
| synchronized (lock) { |
| try { |
| while (!taskRun.get()) { |
| lock.wait(); |
| } |
| } catch (InterruptedException ie) { |
| ie.printStackTrace(); |
| } |
| } |
| } |
| |
| private void startNativeScheduler() throws Exception { |
| mNativeLibraryTestRule.loadNativeLibraryNoBrowserProcess(); |
| ContentMain.start(/* startServiceManagerOnly */ true); |
| TaskSchedulerTestHelpers.enableTaskSchedulerExecutionForTesting(); |
| } |
| } |