1 // Copyright 2020 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base; 6 7 import static org.mockito.Mockito.times; 8 import static org.mockito.Mockito.verify; 9 import static org.mockito.Mockito.verifyNoMoreInteractions; 10 11 import org.junit.Test; 12 import org.junit.runner.RunWith; 13 import org.mockito.Mockito; 14 import org.robolectric.annotation.Config; 15 16 import org.chromium.base.test.BaseRobolectricTestRunner; 17 18 /** Test class for {@link CallbackController}, which also describes typical usage. */ 19 @RunWith(BaseRobolectricTestRunner.class) 20 @Config(manifest = Config.NONE) 21 public class CallbackControllerTest { 22 /** Callbacks in this test act on {@code CallbackTarget}. */ 23 private static class CallbackTarget { runnableTarget()24 public void runnableTarget() {} 25 callbackTarget(boolean arg)26 public void callbackTarget(boolean arg) {} 27 } 28 29 @Test testInstanceCallback()30 public void testInstanceCallback() { 31 CallbackController callbackController = new CallbackController(); 32 CallbackTarget target = Mockito.mock(CallbackTarget.class); 33 Callback<Boolean> wrapped = callbackController.makeCancelable(target::callbackTarget); 34 35 wrapped.onResult(true); 36 verify(target).callbackTarget(true); 37 38 // Execution possible multiple times. 39 wrapped.onResult(true); 40 verify(target, times(2)).callbackTarget(true); 41 42 // Won't trigger after CallbackController is destroyed. 43 callbackController.destroy(); 44 wrapped.onResult(true); 45 verifyNoMoreInteractions(target); 46 } 47 48 @Test testInstanceRunnable()49 public void testInstanceRunnable() { 50 CallbackController callbackController = new CallbackController(); 51 CallbackTarget target = Mockito.mock(CallbackTarget.class); 52 Runnable wrapped = callbackController.makeCancelable(target::runnableTarget); 53 54 wrapped.run(); 55 verify(target).runnableTarget(); 56 57 // Execution possible multiple times. 58 wrapped.run(); 59 verify(target, times(2)).runnableTarget(); 60 61 // Won't trigger after CallbackController is destroyed. 62 callbackController.destroy(); 63 wrapped.run(); 64 verifyNoMoreInteractions(target); 65 } 66 67 @Test testLambdaCallback()68 public void testLambdaCallback() { 69 CallbackController callbackController = new CallbackController(); 70 CallbackTarget target = Mockito.mock(CallbackTarget.class); 71 Callback<Boolean> wrapped = 72 callbackController.makeCancelable(value -> target.callbackTarget(value)); 73 74 wrapped.onResult(true); 75 verify(target).callbackTarget(true); 76 77 // Execution possible multiple times. 78 wrapped.onResult(true); 79 verify(target, times(2)).callbackTarget(true); 80 81 // Won't trigger after CallbackController is destroyed. 82 callbackController.destroy(); 83 wrapped.onResult(true); 84 verifyNoMoreInteractions(target); 85 } 86 87 @Test testLambdaRunnable()88 public void testLambdaRunnable() { 89 CallbackController callbackController = new CallbackController(); 90 CallbackTarget target = Mockito.mock(CallbackTarget.class); 91 Runnable wrapped = callbackController.makeCancelable(() -> target.runnableTarget()); 92 93 wrapped.run(); 94 verify(target).runnableTarget(); 95 96 // Execution possible multiple times. 97 wrapped.run(); 98 verify(target, times(2)).runnableTarget(); 99 100 // Won't trigger after CallbackController is destroyed. 101 callbackController.destroy(); 102 wrapped.run(); 103 verifyNoMoreInteractions(target); 104 } 105 106 @Test testNestedRunnable()107 public void testNestedRunnable() { 108 CallbackController callbackController = new CallbackController(); 109 CallbackTarget target = Mockito.mock(CallbackTarget.class); 110 Runnable makeCancelableAndRun = 111 () -> callbackController.makeCancelable(target::runnableTarget).run(); 112 Runnable wrapped = callbackController.makeCancelable(makeCancelableAndRun); 113 114 // Inside of the wrapping CancelableRunnable#run(), the inner make/run is performed. This 115 // verifies the lock is reentrant. 116 wrapped.run(); 117 verify(target).runnableTarget(); 118 119 wrapped.run(); 120 verify(target, times(2)).runnableTarget(); 121 122 callbackController.destroy(); 123 wrapped.run(); 124 verifyNoMoreInteractions(target); 125 } 126 127 @Test testNestedDestroy()128 public void testNestedDestroy() { 129 // Destroying from within a callback should work fine. 130 CallbackController callbackController = new CallbackController(); 131 CallbackTarget target = Mockito.mock(CallbackTarget.class); 132 Runnable wrapped = callbackController.makeCancelable(callbackController::destroy); 133 134 wrapped.run(); 135 } 136 } 137