| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.base; |
| |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.verifyNoMoreInteractions; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Mockito; |
| import org.robolectric.annotation.Config; |
| |
| import org.chromium.base.test.BaseRobolectricTestRunner; |
| |
| /** Test class for {@link CallbackController}, which also describes typical usage. */ |
| @RunWith(BaseRobolectricTestRunner.class) |
| @Config(manifest = Config.NONE) |
| public class CallbackControllerTest { |
| /** Callbacks in this test act on {@code CallbackTarget}. */ |
| private static class CallbackTarget { |
| public void runnableTarget() {} |
| |
| public void callbackTarget(boolean arg) {} |
| } |
| |
| @Test |
| public void testInstanceCallback() { |
| CallbackController callbackController = new CallbackController(); |
| CallbackTarget target = Mockito.mock(CallbackTarget.class); |
| Callback<Boolean> wrapped = callbackController.makeCancelable(target::callbackTarget); |
| |
| wrapped.onResult(true); |
| verify(target).callbackTarget(true); |
| |
| // Execution possible multiple times. |
| wrapped.onResult(true); |
| verify(target, times(2)).callbackTarget(true); |
| |
| // Won't trigger after CallbackController is destroyed. |
| callbackController.destroy(); |
| wrapped.onResult(true); |
| verifyNoMoreInteractions(target); |
| } |
| |
| @Test |
| public void testInstanceRunnable() { |
| CallbackController callbackController = new CallbackController(); |
| CallbackTarget target = Mockito.mock(CallbackTarget.class); |
| Runnable wrapped = callbackController.makeCancelable(target::runnableTarget); |
| |
| wrapped.run(); |
| verify(target).runnableTarget(); |
| |
| // Execution possible multiple times. |
| wrapped.run(); |
| verify(target, times(2)).runnableTarget(); |
| |
| // Won't trigger after CallbackController is destroyed. |
| callbackController.destroy(); |
| wrapped.run(); |
| verifyNoMoreInteractions(target); |
| } |
| |
| @Test |
| public void testLambdaCallback() { |
| CallbackController callbackController = new CallbackController(); |
| CallbackTarget target = Mockito.mock(CallbackTarget.class); |
| Callback<Boolean> wrapped = |
| callbackController.makeCancelable(value -> target.callbackTarget(value)); |
| |
| wrapped.onResult(true); |
| verify(target).callbackTarget(true); |
| |
| // Execution possible multiple times. |
| wrapped.onResult(true); |
| verify(target, times(2)).callbackTarget(true); |
| |
| // Won't trigger after CallbackController is destroyed. |
| callbackController.destroy(); |
| wrapped.onResult(true); |
| verifyNoMoreInteractions(target); |
| } |
| |
| @Test |
| public void testLambdaRunnable() { |
| CallbackController callbackController = new CallbackController(); |
| CallbackTarget target = Mockito.mock(CallbackTarget.class); |
| Runnable wrapped = callbackController.makeCancelable(() -> target.runnableTarget()); |
| |
| wrapped.run(); |
| verify(target).runnableTarget(); |
| |
| // Execution possible multiple times. |
| wrapped.run(); |
| verify(target, times(2)).runnableTarget(); |
| |
| // Won't trigger after CallbackController is destroyed. |
| callbackController.destroy(); |
| wrapped.run(); |
| verifyNoMoreInteractions(target); |
| } |
| |
| @Test |
| public void testNestedRunnable() { |
| CallbackController callbackController = new CallbackController(); |
| CallbackTarget target = Mockito.mock(CallbackTarget.class); |
| Runnable makeCancelableAndRun = |
| () -> callbackController.makeCancelable(target::runnableTarget).run(); |
| Runnable wrapped = callbackController.makeCancelable(makeCancelableAndRun); |
| |
| // Inside of the wrapping CancelableRunnable#run(), the inner make/run is performed. This |
| // verifies the lock is reentrant. |
| wrapped.run(); |
| verify(target).runnableTarget(); |
| |
| wrapped.run(); |
| verify(target, times(2)).runnableTarget(); |
| |
| callbackController.destroy(); |
| wrapped.run(); |
| verifyNoMoreInteractions(target); |
| } |
| |
| @Test |
| public void testNestedDestroy() { |
| // Destroying from within a callback should work fine. |
| CallbackController callbackController = new CallbackController(); |
| CallbackTarget target = Mockito.mock(CallbackTarget.class); |
| Runnable wrapped = callbackController.makeCancelable(callbackController::destroy); |
| |
| wrapped.run(); |
| } |
| } |