Merge "Add test for benchmarking NetworkStatsRecorder" into main
This commit is contained in:
@@ -31,6 +31,7 @@ android_test {
|
|||||||
],
|
],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"androidx.test.rules",
|
"androidx.test.rules",
|
||||||
|
"mockito-target-minus-junit4",
|
||||||
"net-tests-utils",
|
"net-tests-utils",
|
||||||
"service-connectivity-pre-jarjar",
|
"service-connectivity-pre-jarjar",
|
||||||
"service-connectivity-tiramisu-pre-jarjar",
|
"service-connectivity-tiramisu-pre-jarjar",
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2023 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.server.net.benchmarktests
|
|
||||||
|
|
||||||
import android.net.NetworkStatsCollection
|
|
||||||
import androidx.test.InstrumentationRegistry
|
|
||||||
import com.android.internal.util.FileRotator.Reader
|
|
||||||
import com.android.server.connectivity.benchmarktests.R
|
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.DataInputStream
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import java.util.zip.ZipInputStream
|
|
||||||
import kotlin.test.assertTrue
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.junit.runners.JUnit4
|
|
||||||
|
|
||||||
@RunWith(JUnit4::class)
|
|
||||||
class NetworkStatsCollectionTest {
|
|
||||||
private val DEFAULT_BUFFER_SIZE = 8192
|
|
||||||
private val UID_COLLECTION_BUCKET_DURATION_MS = TimeUnit.HOURS.toMillis(2)
|
|
||||||
|
|
||||||
private val uidTestFiles: List<File> by lazy {
|
|
||||||
// These file generated by using real user dataset which has many uid records and agreed to
|
|
||||||
// share the dataset for testing purpose. These dataset can be extracted from rooted
|
|
||||||
// devices by using "adb pull /data/misc/apexdata/com.android.tethering/netstats" command.
|
|
||||||
val zipInputStream = ZipInputStream(getInputStreamForResource(R.raw.netstats_many_uids_zip))
|
|
||||||
getSortedListForPrefix(unzipToTempDir(zipInputStream), "uid")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testReadCollection_manyUids() {
|
|
||||||
val collection = NetworkStatsCollection(UID_COLLECTION_BUCKET_DURATION_MS)
|
|
||||||
for (file in uidTestFiles) {
|
|
||||||
readFile(file, collection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getInputStreamForResource(resourceId: Int): DataInputStream {
|
|
||||||
return DataInputStream(
|
|
||||||
InstrumentationRegistry.getContext()
|
|
||||||
.getResources().openRawResource(resourceId)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unzipToTempDir(zis: ZipInputStream): File {
|
|
||||||
val statsDir =
|
|
||||||
Files.createTempDirectory(NetworkStatsCollectionTest::class.simpleName).toFile()
|
|
||||||
while (true) {
|
|
||||||
val entryName = zis.nextEntry?.name ?: break
|
|
||||||
val file = File(statsDir, entryName)
|
|
||||||
FileOutputStream(file).use { zis.copyTo(it, DEFAULT_BUFFER_SIZE) }
|
|
||||||
}
|
|
||||||
return statsDir
|
|
||||||
}
|
|
||||||
|
|
||||||
// List [xt|uid|uid_tag].<start>-<end> files under the given directory.
|
|
||||||
private fun getSortedListForPrefix(statsDir: File, prefix: String): List<File> {
|
|
||||||
assertTrue(statsDir.exists())
|
|
||||||
return (statsDir.list() ?: arrayOf()).mapNotNull {
|
|
||||||
if (it.startsWith("$prefix.")) File(statsDir, it) else null
|
|
||||||
}.sorted()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readFile(file: File, reader: Reader) =
|
|
||||||
BufferedInputStream(FileInputStream(file)).use {
|
|
||||||
reader.read(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.net.benchmarktests
|
||||||
|
|
||||||
|
import android.net.NetworkStats.NonMonotonicObserver
|
||||||
|
import android.net.NetworkStatsCollection
|
||||||
|
import android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID
|
||||||
|
import android.os.DropBoxManager
|
||||||
|
import androidx.test.InstrumentationRegistry
|
||||||
|
import com.android.internal.util.FileRotator
|
||||||
|
import com.android.internal.util.FileRotator.Reader
|
||||||
|
import com.android.server.connectivity.benchmarktests.R
|
||||||
|
import com.android.server.net.NetworkStatsRecorder
|
||||||
|
import java.io.BufferedInputStream
|
||||||
|
import java.io.DataInputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.zip.ZipInputStream
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.JUnit4
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
|
||||||
|
@RunWith(JUnit4::class)
|
||||||
|
class NetworkStatsTest {
|
||||||
|
companion object {
|
||||||
|
private val DEFAULT_BUFFER_SIZE = 8192
|
||||||
|
private val UID_COLLECTION_BUCKET_DURATION_MS = TimeUnit.HOURS.toMillis(2)
|
||||||
|
private val UID_RECORDER_ROTATE_AGE_MS = TimeUnit.DAYS.toMillis(15)
|
||||||
|
private val UID_RECORDER_DELETE_AGE_MS = TimeUnit.DAYS.toMillis(90)
|
||||||
|
|
||||||
|
private val testFilesDir by lazy {
|
||||||
|
// These file generated by using real user dataset which has many uid records
|
||||||
|
// and agreed to share the dataset for testing purpose. These dataset can be
|
||||||
|
// extracted from rooted devices by using
|
||||||
|
// "adb pull /data/misc/apexdata/com.android.tethering/netstats" command.
|
||||||
|
val zipInputStream =
|
||||||
|
ZipInputStream(getInputStreamForResource(R.raw.netstats_many_uids_zip))
|
||||||
|
unzipToTempDir(zipInputStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val uidTestFiles: List<File> by lazy {
|
||||||
|
getSortedListForPrefix(testFilesDir, "uid")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test results shows the test cases who read the file first will take longer time to
|
||||||
|
// execute, and reading time getting shorter each time. Read files several times prior to
|
||||||
|
// tests to minimize the impact. This cannot live in setUp() since the time
|
||||||
|
// spent on the file reading will be attributed to the time spent on the individual
|
||||||
|
// test case.
|
||||||
|
@JvmStatic
|
||||||
|
@BeforeClass
|
||||||
|
fun setUpOnce() {
|
||||||
|
for (i in 1..10) {
|
||||||
|
val collection = NetworkStatsCollection(UID_COLLECTION_BUCKET_DURATION_MS)
|
||||||
|
for (file in uidTestFiles) {
|
||||||
|
readFile(file, collection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getInputStreamForResource(resourceId: Int): DataInputStream {
|
||||||
|
return DataInputStream(
|
||||||
|
InstrumentationRegistry.getContext()
|
||||||
|
.getResources().openRawResource(resourceId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unzipToTempDir(zis: ZipInputStream): File {
|
||||||
|
val statsDir =
|
||||||
|
Files.createTempDirectory(NetworkStatsTest::class.simpleName).toFile()
|
||||||
|
while (true) {
|
||||||
|
val entryName = zis.nextEntry?.name ?: break
|
||||||
|
val file = File(statsDir, entryName)
|
||||||
|
FileOutputStream(file).use { zis.copyTo(it, DEFAULT_BUFFER_SIZE) }
|
||||||
|
}
|
||||||
|
return statsDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// List [xt|uid|uid_tag].<start>-<end> files under the given directory.
|
||||||
|
private fun getSortedListForPrefix(statsDir: File, prefix: String): List<File> {
|
||||||
|
assertTrue(statsDir.exists())
|
||||||
|
return (statsDir.list() ?: arrayOf()).mapNotNull {
|
||||||
|
if (it.startsWith("$prefix.")) File(statsDir, it) else null
|
||||||
|
}.sorted()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readFile(file: File, reader: Reader) =
|
||||||
|
BufferedInputStream(FileInputStream(file)).use {
|
||||||
|
reader.read(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testReadCollection_manyUids() {
|
||||||
|
for (i in 1..10) {
|
||||||
|
val collection = NetworkStatsCollection(UID_COLLECTION_BUCKET_DURATION_MS)
|
||||||
|
for (file in uidTestFiles) {
|
||||||
|
readFile(file, collection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testReadFromRecorder_manyUids() {
|
||||||
|
for (i in 1..10) {
|
||||||
|
val recorder = NetworkStatsRecorder(
|
||||||
|
FileRotator(
|
||||||
|
testFilesDir, PREFIX_UID, UID_RECORDER_ROTATE_AGE_MS, UID_RECORDER_DELETE_AGE_MS
|
||||||
|
),
|
||||||
|
mock<NonMonotonicObserver<String>>(),
|
||||||
|
mock(DropBoxManager::class.java),
|
||||||
|
PREFIX_UID,
|
||||||
|
UID_COLLECTION_BUCKET_DURATION_MS,
|
||||||
|
false /* includeTags */,
|
||||||
|
false /* wipeOnError */
|
||||||
|
)
|
||||||
|
recorder.orLoadCompleteLocked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> mock(): T = mock(T::class.java)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user