Skip to content

Commit a684178

Browse files
committed
Add tests and made some minor changes
1 parent ef26687 commit a684178

File tree

3 files changed

+232
-226
lines changed

3 files changed

+232
-226
lines changed

android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/talpid/util/ConnectivityManagerUtilKtTest.kt

+176-61
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
11
package net.mullvad.mullvadvpn.talpid.util
22

33
import android.net.ConnectivityManager
4+
import android.net.LinkAddress
5+
import android.net.LinkProperties
46
import android.net.Network
7+
import android.net.NetworkCapabilities
58
import app.cash.turbine.test
69
import io.mockk.every
710
import io.mockk.mockk
11+
import io.mockk.mockkObject
812
import io.mockk.mockkStatic
13+
import io.mockk.verify
14+
import java.net.DatagramSocket
15+
import java.net.Inet4Address
16+
import java.net.Inet6Address
917
import kotlin.test.assertEquals
1018
import kotlin.time.Duration.Companion.milliseconds
1119
import kotlin.time.Duration.Companion.seconds
1220
import kotlinx.coroutines.channels.awaitClose
1321
import kotlinx.coroutines.delay
1422
import kotlinx.coroutines.flow.callbackFlow
1523
import kotlinx.coroutines.test.runTest
24+
import net.mullvad.talpid.model.Connectivity
25+
import net.mullvad.talpid.util.IpUtils
1626
import net.mullvad.talpid.util.NetworkEvent
27+
import net.mullvad.talpid.util.defaultNetworkEvents
1728
import net.mullvad.talpid.util.hasInternetConnectivity
18-
import net.mullvad.talpid.util.networkEvents
19-
import net.mullvad.talpid.util.networksWithInternetConnectivity
2029
import org.junit.jupiter.api.BeforeEach
2130
import org.junit.jupiter.api.Test
2231

@@ -26,57 +35,80 @@ class ConnectivityManagerUtilKtTest {
2635
@BeforeEach
2736
fun setup() {
2837
mockkStatic(CONNECTIVITY_MANAGER_UTIL_CLASS)
38+
mockkObject(IpUtils)
2939
}
3040

3141
/** User being online, the listener should emit once with `true` */
3242
@Test
3343
fun userIsOnline() = runTest {
3444
val network = mockk<Network>()
35-
every { connectivityManager.networksWithInternetConnectivity() } returns setOf(network)
36-
every { connectivityManager.networkEvents(any()) } returns
45+
val linkProperties = mockk<LinkProperties>()
46+
val ipv4Address: Inet4Address = mockk()
47+
val ipv6Address: Inet6Address = mockk()
48+
val linkIpv4Address: LinkAddress = mockk()
49+
val linkIpv6Address: LinkAddress = mockk()
50+
every { linkIpv4Address.address } returns ipv4Address
51+
every { linkIpv6Address.address } returns ipv6Address
52+
every { linkProperties.linkAddresses } returns
53+
mutableListOf(linkIpv4Address, linkIpv6Address)
54+
val mockProtect = mockk<(socket: DatagramSocket) -> Boolean>(relaxed = true)
55+
every { connectivityManager.defaultNetworkEvents() } returns
3756
callbackFlow {
3857
delay(100.milliseconds) // Simulate connectivity listener being a bit slow
3958
send(NetworkEvent.Available(network))
59+
delay(100.milliseconds) // Simulate connectivity listener being a bit slow
60+
send(NetworkEvent.LinkPropertiesChanged(network, linkProperties))
4061
awaitClose {}
4162
}
4263

43-
connectivityManager.hasInternetConnectivity().test {
64+
connectivityManager.hasInternetConnectivity(mockProtect).test {
4465
// Since initial state and listener both return `true` within debounce we only see one
4566
// event
46-
assertEquals(true, awaitItem())
67+
assertEquals(Connectivity.Status(true, true), awaitItem())
4768
expectNoEvents()
4869
}
4970
}
5071

5172
/** User being offline, the listener should emit once with `false` */
5273
@Test
5374
fun userIsOffline() = runTest {
54-
every { connectivityManager.networksWithInternetConnectivity() } returns setOf()
55-
every { connectivityManager.networkEvents(any()) } returns callbackFlow { awaitClose {} }
75+
val mockProtect = mockk<(socket: DatagramSocket) -> Boolean>(relaxed = true)
76+
every { connectivityManager.defaultNetworkEvents() } returns callbackFlow { awaitClose {} }
5677

57-
connectivityManager.hasInternetConnectivity().test {
78+
connectivityManager.hasInternetConnectivity(mockProtect).test {
5879
// Initially offline and no network events, so we should get a single `false` event
59-
assertEquals(false, awaitItem())
80+
assertEquals(Connectivity.Status(false, false), awaitItem())
6081
expectNoEvents()
6182
}
6283
}
6384

6485
/** User starting offline and then turning on a online after a while */
6586
@Test
6687
fun initiallyOfflineThenBecomingOnline() = runTest {
67-
every { connectivityManager.networksWithInternetConnectivity() } returns emptySet()
68-
every { connectivityManager.networkEvents(any()) } returns
88+
val network = mockk<Network>()
89+
val linkProperties = mockk<LinkProperties>()
90+
val ipv4Address: Inet4Address = mockk()
91+
val ipv6Address: Inet6Address = mockk()
92+
val linkIpv4Address: LinkAddress = mockk()
93+
val linkIpv6Address: LinkAddress = mockk()
94+
every { linkIpv4Address.address } returns ipv4Address
95+
every { linkIpv6Address.address } returns ipv6Address
96+
every { linkProperties.linkAddresses } returns
97+
mutableListOf(linkIpv4Address, linkIpv6Address)
98+
val mockProtect = mockk<(socket: DatagramSocket) -> Boolean>(relaxed = true)
99+
every { connectivityManager.defaultNetworkEvents() } returns
69100
callbackFlow {
70101
// Simulate offline for a little while
71102
delay(5.seconds)
72103
// Then become online
73104
send(NetworkEvent.Available(mockk()))
105+
send(NetworkEvent.LinkPropertiesChanged(network, linkProperties))
74106
awaitClose {}
75107
}
76108

77-
connectivityManager.hasInternetConnectivity().test {
78-
assertEquals(false, awaitItem())
79-
assertEquals(true, awaitItem())
109+
connectivityManager.hasInternetConnectivity(protect = mockProtect).test {
110+
assertEquals(Connectivity.Status(false, false), awaitItem())
111+
assertEquals(Connectivity.Status(true, true), awaitItem())
80112
expectNoEvents()
81113
}
82114
}
@@ -85,46 +117,30 @@ class ConnectivityManagerUtilKtTest {
85117
@Test
86118
fun initiallyOnlineAndThenTurningBecomingOffline() = runTest {
87119
val network = mockk<Network>()
88-
every { connectivityManager.networksWithInternetConnectivity() } returns setOf(network)
89-
every { connectivityManager.networkEvents(any()) } returns
120+
val linkProperties = mockk<LinkProperties>()
121+
val ipv4Address: Inet4Address = mockk()
122+
val ipv6Address: Inet6Address = mockk()
123+
val linkIpv4Address: LinkAddress = mockk()
124+
val linkIpv6Address: LinkAddress = mockk()
125+
every { linkIpv4Address.address } returns ipv4Address
126+
every { linkIpv6Address.address } returns ipv6Address
127+
every { linkProperties.linkAddresses } returns
128+
mutableListOf(linkIpv4Address, linkIpv6Address)
129+
val mockProtect = mockk<(socket: DatagramSocket) -> Boolean>(relaxed = true)
130+
every { connectivityManager.defaultNetworkEvents() } returns
90131
callbackFlow {
91132
// Starting as online
92133
send(NetworkEvent.Available(network))
134+
send(NetworkEvent.LinkPropertiesChanged(network, linkProperties))
93135
delay(5.seconds)
94136
// Then becoming offline
95137
send(NetworkEvent.Lost(network))
96138
awaitClose {}
97139
}
98140

99-
connectivityManager.hasInternetConnectivity().test {
100-
assertEquals(true, awaitItem())
101-
assertEquals(false, awaitItem())
102-
expectNoEvents()
103-
}
104-
}
105-
106-
/**
107-
* User turning on Airplane mode as our connectivity listener starts so we never get any
108-
* onAvailable event from our listener. Initial value will be `true`, followed by no
109-
* `networkEvent` and then turning on network again after 5 seconds
110-
*/
111-
@Test
112-
fun incorrectInitialValueThenBecomingOnline() = runTest {
113-
every { connectivityManager.networksWithInternetConnectivity() } returns setOf(mockk())
114-
every { connectivityManager.networkEvents(any()) } returns
115-
callbackFlow {
116-
delay(5.seconds)
117-
send(NetworkEvent.Available(mockk()))
118-
awaitClose {}
119-
}
120-
121-
connectivityManager.hasInternetConnectivity().test {
122-
// Initial value is connected
123-
assertEquals(true, awaitItem())
124-
// Debounce time has passed, and we never received any network events, so we are offline
125-
assertEquals(false, awaitItem())
126-
// Network is back online
127-
assertEquals(true, awaitItem())
141+
connectivityManager.hasInternetConnectivity(mockProtect).test {
142+
assertEquals(Connectivity.Status(true, true), awaitItem())
143+
assertEquals(Connectivity.Status(false, false), awaitItem())
128144
expectNoEvents()
129145
}
130146
}
@@ -133,26 +149,38 @@ class ConnectivityManagerUtilKtTest {
133149
@Test
134150
fun roamingFromCellularToWifi() = runTest {
135151
val wifiNetwork = mockk<Network>()
152+
val wifiNetworkLinkProperties = mockk<LinkProperties>()
153+
every { wifiNetworkLinkProperties.linkAddresses } returns
154+
listOf(mockk<LinkAddress> { every { address } returns mockk<Inet4Address>() })
136155
val cellularNetwork = mockk<Network>()
156+
val cellularNetworkLinkProperties = mockk<LinkProperties>()
157+
every { cellularNetworkLinkProperties.linkAddresses } returns
158+
listOf(mockk<LinkAddress> { every { address } returns mockk<Inet4Address>() })
159+
val mockProtect = mockk<(socket: DatagramSocket) -> Boolean>(relaxed = true)
137160

138-
every { connectivityManager.networksWithInternetConnectivity() } returns
139-
setOf(cellularNetwork)
140-
every { connectivityManager.networkEvents(any()) } returns
161+
every { connectivityManager.defaultNetworkEvents() } returns
141162
callbackFlow {
142163
send(NetworkEvent.Available(cellularNetwork))
164+
send(
165+
NetworkEvent.LinkPropertiesChanged(
166+
cellularNetwork,
167+
cellularNetworkLinkProperties,
168+
)
169+
)
143170
delay(5.seconds)
144171
// Turning on WiFi, we'll have duplicate networks until phone decides to turn of
145172
// cellular
146173
send(NetworkEvent.Available(wifiNetwork))
174+
send(NetworkEvent.LinkPropertiesChanged(wifiNetwork, wifiNetworkLinkProperties))
147175
delay(30.seconds)
148176
// Phone turning off cellular network
149177
send(NetworkEvent.Lost(cellularNetwork))
150178
awaitClose {}
151179
}
152180

153-
connectivityManager.hasInternetConnectivity().test {
181+
connectivityManager.hasInternetConnectivity(mockProtect).test {
154182
// We should always only see us being online
155-
assertEquals(true, awaitItem())
183+
assertEquals(Connectivity.Status(ipv4 = true, ipv6 = false), awaitItem())
156184
expectNoEvents()
157185
}
158186
}
@@ -161,23 +189,36 @@ class ConnectivityManagerUtilKtTest {
161189
@Test
162190
fun roamingFromWifiToCellular() = runTest {
163191
val wifiNetwork = mockk<Network>()
192+
val wifiNetworkLinkProperties = mockk<LinkProperties>()
193+
every { wifiNetworkLinkProperties.linkAddresses } returns
194+
listOf(mockk<LinkAddress> { every { address } returns mockk<Inet4Address>() })
164195
val cellularNetwork = mockk<Network>()
196+
val cellularNetworkLinkProperties = mockk<LinkProperties>()
197+
every { cellularNetworkLinkProperties.linkAddresses } returns
198+
listOf(mockk<LinkAddress> { every { address } returns mockk<Inet4Address>() })
199+
val mockProtect = mockk<(socket: DatagramSocket) -> Boolean>(relaxed = true)
165200

166-
every { connectivityManager.networksWithInternetConnectivity() } returns setOf(wifiNetwork)
167-
every { connectivityManager.networkEvents(any()) } returns
201+
every { connectivityManager.defaultNetworkEvents() } returns
168202
callbackFlow {
169203
send(NetworkEvent.Available(wifiNetwork))
204+
send(NetworkEvent.LinkPropertiesChanged(wifiNetwork, wifiNetworkLinkProperties))
170205
delay(5.seconds)
171206
send(NetworkEvent.Lost(wifiNetwork))
172207
// We will have no network for a little time until cellular chip is on.
173208
delay(150.milliseconds)
174209
send(NetworkEvent.Available(cellularNetwork))
210+
send(
211+
NetworkEvent.LinkPropertiesChanged(
212+
cellularNetwork,
213+
cellularNetworkLinkProperties,
214+
)
215+
)
175216
awaitClose {}
176217
}
177218

178-
connectivityManager.hasInternetConnectivity().test {
219+
connectivityManager.hasInternetConnectivity(mockProtect).test {
179220
// We should always only see us being online, small offline state is caught by debounce
180-
assertEquals(true, awaitItem())
221+
assertEquals(Connectivity.Status(ipv4 = true, ipv6 = false), awaitItem())
181222
expectNoEvents()
182223
}
183224
}
@@ -186,31 +227,105 @@ class ConnectivityManagerUtilKtTest {
186227
@Test
187228
fun slowRoamingFromWifiToCellular() = runTest {
188229
val wifiNetwork = mockk<Network>()
230+
val wifiNetworkLinkProperties = mockk<LinkProperties>()
231+
every { wifiNetworkLinkProperties.linkAddresses } returns
232+
listOf(mockk<LinkAddress> { every { address } returns mockk<Inet6Address>() })
189233
val cellularNetwork = mockk<Network>()
234+
val cellularNetworkLinkProperties = mockk<LinkProperties>()
235+
every { cellularNetworkLinkProperties.linkAddresses } returns
236+
listOf(mockk<LinkAddress> { every { address } returns mockk<Inet6Address>() })
237+
val mockProtect = mockk<(socket: DatagramSocket) -> Boolean>(relaxed = true)
190238

191-
every { connectivityManager.networksWithInternetConnectivity() } returns setOf(wifiNetwork)
192-
every { connectivityManager.networkEvents(any()) } returns
239+
every { connectivityManager.defaultNetworkEvents() } returns
193240
callbackFlow {
194241
send(NetworkEvent.Available(wifiNetwork))
242+
send(NetworkEvent.LinkPropertiesChanged(wifiNetwork, wifiNetworkLinkProperties))
195243
delay(5.seconds)
196244
send(NetworkEvent.Lost(wifiNetwork))
197245
// We will have no network for a little time until cellular chip is on.
198246
delay(500.milliseconds)
199247
send(NetworkEvent.Available(cellularNetwork))
248+
send(
249+
NetworkEvent.LinkPropertiesChanged(
250+
cellularNetwork,
251+
cellularNetworkLinkProperties,
252+
)
253+
)
200254
awaitClose {}
201255
}
202256

203-
connectivityManager.hasInternetConnectivity().test {
257+
connectivityManager.hasInternetConnectivity(protect = mockProtect).test {
204258
// Wifi is online
205-
assertEquals(true, awaitItem())
259+
assertEquals(Connectivity.Status(false, true), awaitItem())
206260
// We didn't get any network within debounce time, so we are offline
207-
assertEquals(false, awaitItem())
261+
assertEquals(Connectivity.Status(false, false), awaitItem())
208262
// Cellular network is online
209-
assertEquals(true, awaitItem())
263+
assertEquals(Connectivity.Status(false, true), awaitItem())
264+
expectNoEvents()
265+
}
266+
}
267+
268+
/** Switching between networks with different configurations. */
269+
@Test
270+
fun roamingFromWifiWithIpv6OnlyToWifiWithIpv4Only() = runTest {
271+
val ipv6Network = mockk<Network>()
272+
val ipv6NetworkLinkProperties = mockk<LinkProperties>()
273+
every { ipv6NetworkLinkProperties.linkAddresses } returns
274+
listOf(mockk<LinkAddress> { every { address } returns mockk<Inet4Address>() })
275+
val ipv4Network = mockk<Network>()
276+
val ipv4NetworkkLinkProperties = mockk<LinkProperties>()
277+
every { ipv4NetworkkLinkProperties.linkAddresses } returns
278+
listOf(mockk<LinkAddress> { every { address } returns mockk<Inet6Address>() })
279+
val mockProtect = mockk<(socket: DatagramSocket) -> Boolean>(relaxed = true)
280+
281+
every { connectivityManager.defaultNetworkEvents() } returns
282+
callbackFlow {
283+
send(NetworkEvent.Available(ipv6Network))
284+
send(NetworkEvent.LinkPropertiesChanged(ipv6Network, ipv6NetworkLinkProperties))
285+
delay(5.seconds)
286+
send(NetworkEvent.Lost(ipv6Network))
287+
delay(100.milliseconds)
288+
send(NetworkEvent.Available(ipv4Network))
289+
send(NetworkEvent.LinkPropertiesChanged(ipv4Network, ipv4NetworkkLinkProperties))
290+
awaitClose {}
291+
}
292+
293+
connectivityManager.hasInternetConnectivity(protect = mockProtect).test {
294+
// Ipv4 network is online
295+
assertEquals(Connectivity.Status(true, false), awaitItem())
296+
// Ipv6 network is online
297+
assertEquals(Connectivity.Status(false, true), awaitItem())
210298
expectNoEvents()
211299
}
212300
}
213301

302+
/** Vpn network should NOT check link properties but should rather use socket implementation */
303+
@Test
304+
fun checkVpnNetworkUsingSocketImplementation() = runTest {
305+
val vpnNetwork = mockk<Network>()
306+
val capabilities = mockk<NetworkCapabilities>()
307+
every { capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) } returns
308+
false
309+
val mockProtect = mockk<(socket: DatagramSocket) -> Boolean>()
310+
every { IpUtils.hasIPv4(any()) } returns true
311+
every { IpUtils.hasIPv6(any()) } returns true
312+
313+
every { connectivityManager.defaultNetworkEvents() } returns
314+
callbackFlow {
315+
send(NetworkEvent.Available(vpnNetwork))
316+
send(NetworkEvent.CapabilitiesChanged(vpnNetwork, capabilities))
317+
awaitClose {}
318+
}
319+
320+
connectivityManager.hasInternetConnectivity(protect = mockProtect).test {
321+
// Network is online
322+
assertEquals(Connectivity.Status(true, true), awaitItem())
323+
}
324+
325+
verify(exactly = 1) { IpUtils.hasIPv4(any()) }
326+
verify(exactly = 1) { IpUtils.hasIPv6(any()) }
327+
}
328+
214329
companion object {
215330
private const val CONNECTIVITY_MANAGER_UTIL_CLASS =
216331
"net.mullvad.talpid.util.ConnectivityManagerUtilKt"

0 commit comments

Comments
 (0)