1 /*
<lambda>null2 * Copyright (C) 2020 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.testutils
18
19 import android.app.usage.NetworkStatsManager
20 import android.content.Context
21 import android.net.INetworkStatsService
22 import android.net.INetworkStatsSession
23 import android.net.NetworkStats
24 import android.net.NetworkTemplate
25 import android.net.NetworkTemplate.MATCH_MOBILE
26 import android.text.TextUtils
27 import com.android.modules.utils.build.SdkLevel
28 import kotlin.test.assertTrue
29 import org.mockito.ArgumentMatchers.any
30 import org.mockito.ArgumentMatchers.anyBoolean
31 import org.mockito.ArgumentMatchers.anyInt
32 import org.mockito.ArgumentMatchers.anyLong
33 import org.mockito.Mockito
34
35 @JvmOverloads
36 fun orderInsensitiveEquals(
37 leftStats: NetworkStats,
38 rightStats: NetworkStats,
39 compareTime: Boolean = false
40 ): Boolean {
41 if (leftStats == rightStats) return true
42 if (compareTime && leftStats.elapsedRealtime != rightStats.elapsedRealtime) {
43 return false
44 }
45
46 // While operations such as add/subtract will preserve empty entries. This will make
47 // the result be hard to verify during test. Remove them before comparing since they
48 // are not really affect correctness.
49 // TODO (b/152827872): Remove empty entries after addition/subtraction.
50 val leftTrimmedEmpty = leftStats.removeEmptyEntries()
51 val rightTrimmedEmpty = rightStats.removeEmptyEntries()
52
53 if (leftTrimmedEmpty.size() != rightTrimmedEmpty.size()) return false
54 val left = NetworkStats.Entry()
55 val right = NetworkStats.Entry()
56 // Order insensitive compare.
57 for (i in 0 until leftTrimmedEmpty.size()) {
58 leftTrimmedEmpty.getValues(i, left)
59 val j: Int = rightTrimmedEmpty.findIndexHinted(left.iface, left.uid, left.set, left.tag,
60 left.metered, left.roaming, left.defaultNetwork, i)
61 if (j == -1) return false
62 rightTrimmedEmpty.getValues(j, right)
63 if (SdkLevel.isAtLeastT()) {
64 if (left != right) return false
65 } else {
66 if (!checkEntryEquals(left, right)) return false
67 }
68 }
69 return true
70 }
71
72 /**
73 * Assert that the two {@link NetworkStats.Entry} are equals.
74 */
assertEntryEqualsnull75 fun assertEntryEquals(left: NetworkStats.Entry, right: NetworkStats.Entry) {
76 assertTrue(checkEntryEquals(left, right))
77 }
78
79 // TODO: Make all callers use NetworkStats.Entry#equals once S- downstreams
80 // are no longer supported. Because NetworkStats is mainlined on T+ and
81 // NetworkStats.Entry#equals in S- does not support null iface.
checkEntryEqualsnull82 fun checkEntryEquals(left: NetworkStats.Entry, right: NetworkStats.Entry): Boolean {
83 return TextUtils.equals(left.iface, right.iface) &&
84 left.uid == right.uid &&
85 left.set == right.set &&
86 left.tag == right.tag &&
87 left.metered == right.metered &&
88 left.roaming == right.roaming &&
89 left.defaultNetwork == right.defaultNetwork &&
90 left.rxBytes == right.rxBytes &&
91 left.rxPackets == right.rxPackets &&
92 left.txBytes == right.txBytes &&
93 left.txPackets == right.txPackets &&
94 left.operations == right.operations
95 }
96
97 /**
98 * Assert that two {@link NetworkStats} are equals, assuming the order of the records are not
99 * necessarily the same.
100 *
101 * @note {@code elapsedRealtime} is not compared by default, given that in test cases that is not
102 * usually used.
103 */
104 @JvmOverloads
assertNetworkStatsEqualsnull105 fun assertNetworkStatsEquals(
106 expected: NetworkStats,
107 actual: NetworkStats,
108 compareTime: Boolean = false
109 ) {
110 assertTrue(orderInsensitiveEquals(expected, actual, compareTime),
111 "expected: $expected but was: $actual")
112 }
113
114 /**
115 * Assert that after being parceled then unparceled, {@link NetworkStats} is equal to the original
116 * object.
117 */
assertParcelingIsLosslessnull118 fun assertParcelingIsLossless(stats: NetworkStats) {
119 assertParcelingIsLossless(stats) { a, b -> orderInsensitiveEquals(a, b) }
120 }
121
122 /**
123 * Make a {@link android.app.usage.NetworkStats} instance from
124 * a {@link android.net.NetworkStats} instance.
125 */
126 // It's not possible to directly create a mocked `NetworkStats` instance
127 // because of limitations with `NetworkStats#getNextBucket`.
128 // As a workaround for testing, create a mock by controlling the return values
129 // from the mocked service that provides the `NetworkStats` data.
130 // Notes:
131 // 1. The order of records in the final `NetworkStats` object might change or
132 // some records might be merged if there are items with duplicate keys.
133 // 2. The interface and operations fields will be empty since there is
134 // no such field in the {@link android.app.usage.NetworkStats}.
makePublicStatsFromAndroidNetStatsnull135 fun makePublicStatsFromAndroidNetStats(androidNetStats: NetworkStats):
136 android.app.usage.NetworkStats {
137 val mockService = Mockito.mock(INetworkStatsService::class.java)
138 val manager = NetworkStatsManager(Mockito.mock(Context::class.java), mockService)
139 val mockStatsSession = Mockito.mock(INetworkStatsSession::class.java)
140
141 Mockito.doReturn(mockStatsSession).`when`(mockService)
142 .openSessionForUsageStats(anyInt(), any())
143 Mockito.doReturn(androidNetStats).`when`(mockStatsSession).getSummaryForAllUid(
144 any(NetworkTemplate::class.java), anyLong(), anyLong(), anyBoolean())
145 return manager.querySummary(
146 NetworkTemplate.Builder(MATCH_MOBILE).build(),
147 Long.MIN_VALUE, Long.MAX_VALUE
148 )
149 }
150