1 /* 2 * Copyright 2024 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.photopicker.core.network 18 19 import android.content.Context 20 import android.net.ConnectivityManager 21 import android.net.Network 22 import android.net.NetworkCapabilities 23 import android.net.NetworkRequest 24 import androidx.test.ext.junit.runners.AndroidJUnit4 25 import androidx.test.filters.SmallTest 26 import com.android.photopicker.util.test.capture 27 import com.android.photopicker.util.test.mockSystemService 28 import com.android.photopicker.util.test.whenever 29 import com.google.common.truth.Truth.assertThat 30 import kotlinx.coroutines.ExperimentalCoroutinesApi 31 import kotlinx.coroutines.flow.first 32 import kotlinx.coroutines.flow.toList 33 import kotlinx.coroutines.launch 34 import kotlinx.coroutines.test.advanceTimeBy 35 import kotlinx.coroutines.test.runTest 36 import org.junit.Before 37 import org.junit.Test 38 import org.junit.runner.RunWith 39 import org.mockito.ArgumentCaptor 40 import org.mockito.Captor 41 import org.mockito.Mock 42 import org.mockito.Mockito.verify 43 import org.mockito.MockitoAnnotations 44 45 /** Unit tests for the [NetworkManager] */ 46 @SmallTest 47 @RunWith(AndroidJUnit4::class) 48 @OptIn(ExperimentalCoroutinesApi::class) 49 class NetworkMonitorTest { 50 lateinit var networkMonitor: NetworkMonitor 51 52 @Mock lateinit var context: Context 53 @Mock lateinit var mockNetwork: Network 54 @Mock lateinit var mockConnectivityManager: ConnectivityManager 55 @Captor lateinit var callback: ArgumentCaptor<ConnectivityManager.NetworkCallback> 56 @Captor lateinit var networkRequest: ArgumentCaptor<NetworkRequest> 57 58 @Before setupnull59 fun setup() { 60 MockitoAnnotations.initMocks(this) 61 mockSystemService(context, ConnectivityManager::class.java) { mockConnectivityManager } 62 whenever(mockConnectivityManager.activeNetwork) { mockNetwork } 63 whenever(mockConnectivityManager.getNetworkCapabilities(mockNetwork)) { 64 NetworkCapabilities.Builder() 65 .apply { addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) } 66 .build() 67 } 68 } 69 70 /** Ensures the initial [NetworkStatus] is emitted before any callbacks are received. */ 71 @Test testInitialNetworkIsAvailablenull72 fun testInitialNetworkIsAvailable() { 73 runTest { // this: TestScope 74 networkMonitor = NetworkMonitor(context, this.backgroundScope) 75 launch { 76 val reportedStatus = networkMonitor.networkStatus.first() 77 assertThat(reportedStatus).isEqualTo(NetworkStatus.Available) 78 } 79 } 80 } 81 82 /** Ensures the [NetworkMonitor] correctly sets up a [ConnectivityManager.NetworkCallback] */ 83 @Test testRegistersNetworkCallbacknull84 fun testRegistersNetworkCallback() { 85 runTest { 86 networkMonitor = NetworkMonitor(context, this.backgroundScope) 87 launch { 88 val reportedStatus = networkMonitor.networkStatus.first() 89 assertThat(reportedStatus).isEqualTo(NetworkStatus.Available) 90 } 91 advanceTimeBy(100) 92 verify(mockConnectivityManager) 93 .registerNetworkCallback(capture(networkRequest), capture(callback)) 94 95 val request: NetworkRequest = networkRequest.getValue() 96 val callback: ConnectivityManager.NetworkCallback = callback.getValue() 97 98 assertThat(callback).isNotNull() 99 assertThat(request.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)).isTrue() 100 } 101 } 102 103 /** 104 * Ensures the [ConnectivityManager.NetworkCallback] emits the correct [NetworkStatus] when 105 * called. 106 */ 107 @Test testCallbackEmitsNewNetworkStatusnull108 fun testCallbackEmitsNewNetworkStatus() { 109 runTest { 110 networkMonitor = NetworkMonitor(context, this.backgroundScope) 111 val emissions = mutableListOf<NetworkStatus>() 112 backgroundScope.launch { networkMonitor.networkStatus.toList(emissions) } 113 advanceTimeBy(100) 114 verify(mockConnectivityManager) 115 .registerNetworkCallback(capture(networkRequest), capture(callback)) 116 117 val callback: ConnectivityManager.NetworkCallback = callback.getValue() 118 119 assertThat(emissions.removeFirst()).isEqualTo(NetworkStatus.Available) 120 121 callback.onUnavailable() 122 advanceTimeBy(100) 123 124 assertThat(emissions.removeFirst()).isEqualTo(NetworkStatus.Unavailable) 125 126 callback.onAvailable(mockNetwork) 127 advanceTimeBy(100) 128 129 assertThat(emissions.removeFirst()).isEqualTo(NetworkStatus.Available) 130 131 callback.onLost(mockNetwork) 132 advanceTimeBy(100) 133 134 assertThat(emissions.removeFirst()).isEqualTo(NetworkStatus.Unavailable) 135 } 136 } 137 138 /** Ensures new subscribers to the flow only receive the latest [NetworkStatus] */ 139 @Test testNetworkStatusReplayedForNewSubscribersnull140 fun testNetworkStatusReplayedForNewSubscribers() { 141 runTest { 142 networkMonitor = NetworkMonitor(context, this.backgroundScope) 143 val allEmissions = mutableListOf<NetworkStatus>() 144 backgroundScope.launch { networkMonitor.networkStatus.toList(allEmissions) } 145 advanceTimeBy(100) 146 verify(mockConnectivityManager) 147 .registerNetworkCallback(capture(networkRequest), capture(callback)) 148 149 val callback: ConnectivityManager.NetworkCallback = callback.getValue() 150 151 callback.onUnavailable() 152 advanceTimeBy(100) 153 154 callback.onAvailable(mockNetwork) 155 advanceTimeBy(100) 156 157 callback.onLost(mockNetwork) 158 advanceTimeBy(100) 159 160 assertThat(allEmissions.size).isEqualTo(4) 161 162 // Register a new collector, which should jump straight to the end of emissions. 163 val emissions = mutableListOf<NetworkStatus>() 164 backgroundScope.launch { networkMonitor.networkStatus.toList(emissions) } 165 advanceTimeBy(100) 166 167 assertThat(emissions.first()).isEqualTo(NetworkStatus.Unavailable) 168 assertThat(emissions.size).isEqualTo(1) 169 } 170 } 171 172 /** Ensures only new values are emitted from the NetworkStatus flow. */ 173 @Test testNetworkStatusIsDistinctUntilChangednull174 fun testNetworkStatusIsDistinctUntilChanged() { 175 runTest { 176 networkMonitor = NetworkMonitor(context, this.backgroundScope) 177 val emissions = mutableListOf<NetworkStatus>() 178 backgroundScope.launch { networkMonitor.networkStatus.toList(emissions) } 179 advanceTimeBy(100) 180 verify(mockConnectivityManager) 181 .registerNetworkCallback(capture(networkRequest), capture(callback)) 182 183 val callback: ConnectivityManager.NetworkCallback = callback.getValue() 184 185 callback.onAvailable(mockNetwork) 186 advanceTimeBy(100) 187 188 callback.onAvailable(mockNetwork) 189 advanceTimeBy(100) 190 191 callback.onAvailable(mockNetwork) 192 advanceTimeBy(100) 193 194 assertThat(emissions.first()).isEqualTo(NetworkStatus.Available) 195 assertThat(emissions.size).isEqualTo(1) 196 } 197 } 198 } 199