1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.media.taptotransfer
18 
19 import android.app.StatusBarManager
20 import android.content.Context
21 import android.media.MediaRoute2Info
22 import androidx.test.filters.SmallTest
23 import com.android.systemui.SysuiTestCase
24 import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
25 import com.android.systemui.media.taptotransfer.sender.ChipStateSender
26 import com.android.systemui.statusbar.commandline.Command
27 import com.android.systemui.statusbar.commandline.CommandRegistry
28 import com.android.systemui.util.concurrency.FakeExecutor
29 import com.android.systemui.util.mockito.any
30 import com.android.systemui.util.mockito.argumentCaptor
31 import com.android.systemui.util.mockito.capture
32 import com.android.systemui.util.mockito.eq
33 import com.android.systemui.util.mockito.nullable
34 import com.android.systemui.util.time.FakeSystemClock
35 import com.google.common.truth.Truth.assertThat
36 import org.junit.Before
37 import org.junit.Test
38 import org.mockito.Mock
39 import org.mockito.Mockito.verify
40 import org.mockito.MockitoAnnotations
41 import java.io.PrintWriter
42 import java.io.StringWriter
43 import java.util.concurrent.Executor
44 
45 @SmallTest
46 class MediaTttCommandLineHelperTest : SysuiTestCase() {
47 
48     private val inlineExecutor = Executor { command -> command.run() }
49     private val commandRegistry = CommandRegistry(context, inlineExecutor)
50     private val pw = PrintWriter(StringWriter())
51 
52     private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper
53 
54     @Mock
55     private lateinit var statusBarManager: StatusBarManager
56 
57     @Before
58     fun setUp() {
59         MockitoAnnotations.initMocks(this)
60         context.addMockSystemService(Context.STATUS_BAR_SERVICE, statusBarManager)
61         mediaTttCommandLineHelper =
62             MediaTttCommandLineHelper(
63                 commandRegistry,
64                 context,
65                 FakeExecutor(FakeSystemClock()),
66             )
67         mediaTttCommandLineHelper.start()
68     }
69 
70     @Test(expected = IllegalStateException::class)
71     fun constructor_senderCommandAlreadyRegistered() {
72         // Since creating the chip controller should automatically register the sender command, it
73         // should throw when registering it again.
74         commandRegistry.registerCommand(SENDER_COMMAND) { EmptyCommand() }
75     }
76 
77     @Test(expected = IllegalStateException::class)
78     fun constructor_receiverCommandAlreadyRegistered() {
79         // Since creating the chip controller should automatically register the receiver command, it
80         // should throw when registering it again.
81         commandRegistry.registerCommand(RECEIVER_COMMAND) { EmptyCommand() }
82     }
83 
84     @Test
85     fun sender_almostCloseToStartCast_serviceCallbackCalled() {
86         commandRegistry.onShellCommand(
87             pw, getSenderCommand(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.name)
88         )
89 
90         val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
91         verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
92             eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST),
93             capture(routeInfoCaptor),
94             nullable(),
95             nullable())
96         assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
97     }
98 
99     @Test
100     fun sender_almostCloseToEndCast_serviceCallbackCalled() {
101         commandRegistry.onShellCommand(
102             pw, getSenderCommand(ChipStateSender.ALMOST_CLOSE_TO_END_CAST.name)
103         )
104 
105         val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
106         verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
107             eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST),
108             capture(routeInfoCaptor),
109             nullable(),
110             nullable())
111         assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
112     }
113 
114     @Test
115     fun sender_transferToReceiverTriggered_chipDisplayWithCorrectState() {
116         commandRegistry.onShellCommand(
117             pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.name)
118         )
119 
120         val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
121         verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
122             eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED),
123             capture(routeInfoCaptor),
124             nullable(),
125             nullable())
126         assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
127     }
128 
129     @Test
130     fun sender_transferToThisDeviceTriggered_chipDisplayWithCorrectState() {
131         commandRegistry.onShellCommand(
132             pw, getSenderCommand(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.name)
133         )
134 
135         verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
136             eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED),
137             any(),
138             nullable(),
139             nullable())
140     }
141 
142     @Test
143     fun sender_transferToReceiverSucceeded_chipDisplayWithCorrectState() {
144         commandRegistry.onShellCommand(
145             pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED.name)
146         )
147 
148         val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
149         verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
150             eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED),
151             capture(routeInfoCaptor),
152             nullable(),
153             nullable())
154         assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
155     }
156 
157     @Test
158     fun sender_transferToThisDeviceSucceeded_chipDisplayWithCorrectState() {
159         commandRegistry.onShellCommand(
160             pw, getSenderCommand(ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED.name)
161         )
162 
163         val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
164         verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
165             eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED),
166             capture(routeInfoCaptor),
167             nullable(),
168             nullable())
169         assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
170     }
171 
172     @Test
173     fun sender_transferToReceiverFailed_serviceCallbackCalled() {
174         commandRegistry.onShellCommand(
175             pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED.name)
176         )
177 
178         verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
179             eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED),
180             any(),
181             nullable(),
182             nullable())
183     }
184 
185     @Test
186     fun sender_transferToThisDeviceFailed_serviceCallbackCalled() {
187         commandRegistry.onShellCommand(
188             pw, getSenderCommand(ChipStateSender.TRANSFER_TO_THIS_DEVICE_FAILED.name)
189         )
190 
191         verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
192             eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED),
193             any(),
194             nullable(),
195             nullable())
196     }
197 
198     @Test
199     fun sender_farFromReceiver_serviceCallbackCalled() {
200         commandRegistry.onShellCommand(
201             pw, getSenderCommand(ChipStateSender.FAR_FROM_RECEIVER.name)
202         )
203 
204         verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
205             eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER),
206             any(),
207             nullable(),
208             nullable())
209     }
210 
211     @Test
212     fun receiver_closeToSender_serviceCallbackCalled() {
213         commandRegistry.onShellCommand(
214             pw, getReceiverCommand(ChipStateReceiver.CLOSE_TO_SENDER.name)
215         )
216 
217         verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
218             eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER),
219             any(),
220             nullable(),
221             nullable()
222         )
223     }
224 
225     @Test
226     fun receiver_farFromSender_serviceCallbackCalled() {
227         commandRegistry.onShellCommand(
228             pw, getReceiverCommand(ChipStateReceiver.FAR_FROM_SENDER.name)
229         )
230 
231         verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
232             eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER),
233             any(),
234             nullable(),
235             nullable()
236         )
237     }
238 
239     private fun getSenderCommand(displayState: String): Array<String> =
240         arrayOf(SENDER_COMMAND, DEVICE_NAME, displayState)
241 
242     private fun getReceiverCommand(displayState: String): Array<String> =
243         arrayOf(RECEIVER_COMMAND, displayState)
244 
245     class EmptyCommand : Command {
246         override fun execute(pw: PrintWriter, args: List<String>) {
247         }
248 
249         override fun help(pw: PrintWriter) {
250         }
251     }
252 }
253 
254 private const val DEVICE_NAME = "My Tablet"
255