1 /* 2 * Copyright (C) 2023 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.server.net 18 19 import android.util.IndentingPrintWriter 20 import com.android.server.net.NetworkStatsEventLogger.MAX_POLL_REASON 21 import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_DUMPSYS 22 import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_FORCE_UPDATE 23 import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_GLOBAL_ALERT 24 import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_NETWORK_STATUS_CHANGED 25 import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_OPEN_SESSION 26 import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_PERIODIC 27 import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_RAT_CHANGED 28 import com.android.server.net.NetworkStatsEventLogger.POLL_REASON_REG_CALLBACK 29 import com.android.server.net.NetworkStatsEventLogger.PollEvent 30 import com.android.server.net.NetworkStatsEventLogger.PollEvent.pollReasonNameOf 31 import com.android.testutils.DevSdkIgnoreRunner 32 import java.io.StringWriter 33 import kotlin.test.assertEquals 34 import kotlin.test.assertFailsWith 35 import kotlin.test.assertFalse 36 import kotlin.test.assertTrue 37 import org.junit.Test 38 import org.junit.runner.RunWith 39 40 const val TEST_PERSIST_FLAG = 0x101 41 42 @RunWith(DevSdkIgnoreRunner::class) 43 class NetworkStatsEventLoggerTest { 44 val logger = NetworkStatsEventLogger() 45 val stringWriter = TestStringWriter() 46 val pw = IndentingPrintWriter(stringWriter) 47 48 @Test testDump_invalidnull49 fun testDump_invalid() { 50 // Verify it won't crash. 51 logger.dump(pw) 52 // Clear output buffer. 53 stringWriter.getOutputAndClear() 54 55 // Verify log invalid event throws. And nothing output in the dump. 56 val invalidReasons = listOf(-1, MAX_POLL_REASON + 1) 57 invalidReasons.forEach { 58 assertFailsWith<IllegalArgumentException> { 59 logger.logPollEvent(TEST_PERSIST_FLAG, PollEvent(it)) 60 } 61 logger.dumpRecentPollEvents(pw) 62 val output = stringWriter.getOutputAndClear() 63 assertStringNotContains(output, pollReasonNameOf(it)) 64 } 65 } 66 67 @Test testDump_validnull68 fun testDump_valid() { 69 // Choose arbitrary set of reasons for testing. 70 val loggedReasons = listOf( 71 POLL_REASON_GLOBAL_ALERT, 72 POLL_REASON_FORCE_UPDATE, 73 POLL_REASON_DUMPSYS, 74 POLL_REASON_PERIODIC, 75 POLL_REASON_RAT_CHANGED 76 ) 77 val nonLoggedReasons = listOf( 78 POLL_REASON_NETWORK_STATUS_CHANGED, 79 POLL_REASON_OPEN_SESSION, 80 POLL_REASON_REG_CALLBACK) 81 82 // Add some valid records. 83 loggedReasons.forEach { 84 logger.logPollEvent(TEST_PERSIST_FLAG, PollEvent(it)) 85 } 86 87 // Collect dumps. 88 logger.dumpRecentPollEvents(pw) 89 val outputRecentEvents = stringWriter.getOutputAndClear() 90 logger.dumpPollCountsPerReason(pw) 91 val outputCountsPerReason = stringWriter.getOutputAndClear() 92 93 // Verify the output contains at least necessary information. 94 loggedReasons.forEach { 95 // Verify all events are shown in the recent event dump. 96 val eventString = PollEvent(it).toString() 97 assertStringContains(outputRecentEvents, TEST_PERSIST_FLAG.toString()) 98 assertStringContains(eventString, pollReasonNameOf(it)) 99 assertStringContains(outputRecentEvents, eventString) 100 // Verify counts are 1 for each reason. 101 assertCountForReason(outputCountsPerReason, it, 1) 102 } 103 104 // Verify the output remains untouched for other reasons. 105 nonLoggedReasons.forEach { 106 assertStringNotContains(outputRecentEvents, PollEvent(it).toString()) 107 assertCountForReason(outputCountsPerReason, it, 0) 108 } 109 } 110 111 @Test testDump_maxEventLogsnull112 fun testDump_maxEventLogs() { 113 // Choose arbitrary reason. 114 val reasonToBeTested = POLL_REASON_PERIODIC 115 val repeatCount = NetworkStatsEventLogger.MAX_EVENTS_LOGS * 2 116 117 // Collect baseline. 118 logger.dumpRecentPollEvents(pw) 119 val lineCountBaseLine = getLineCount(stringWriter.getOutputAndClear()) 120 121 repeat(repeatCount) { 122 logger.logPollEvent(TEST_PERSIST_FLAG, PollEvent(reasonToBeTested)) 123 } 124 125 // Collect dump. 126 logger.dumpRecentPollEvents(pw) 127 val lineCountAfterTest = getLineCount(stringWriter.getOutputAndClear()) 128 129 // Verify line count increment is limited. 130 assertEquals( 131 NetworkStatsEventLogger.MAX_EVENTS_LOGS, 132 lineCountAfterTest - lineCountBaseLine 133 ) 134 135 // Verify count per reason increased for the testing reason. 136 logger.dumpPollCountsPerReason(pw) 137 val outputCountsPerReason = stringWriter.getOutputAndClear() 138 for (reason in 0..MAX_POLL_REASON) { 139 assertCountForReason( 140 outputCountsPerReason, 141 reason, 142 if (reason == reasonToBeTested) repeatCount else 0 143 ) 144 } 145 } 146 getLineCountnull147 private fun getLineCount(multilineString: String) = multilineString.lines().size 148 149 private fun assertStringContains(got: String, want: String) { 150 assertTrue(got.contains(want), "Wanted: $want, but got: $got") 151 } 152 assertStringNotContainsnull153 private fun assertStringNotContains(got: String, unwant: String) { 154 assertFalse(got.contains(unwant), "Unwanted: $unwant, but got: $got") 155 } 156 157 /** 158 * Assert the reason and the expected count are at the same line. 159 */ assertCountForReasonnull160 private fun assertCountForReason(dump: String, reason: Int, expectedCount: Int) { 161 // Matches strings like "GLOBAL_ALERT: 50" but not too strict since the format might change. 162 val regex = Regex(pollReasonNameOf(reason) + "[^0-9]+" + expectedCount) 163 assertEquals( 164 1, 165 regex.findAll(dump).count(), 166 "Unexpected output: $dump " + " for reason: " + pollReasonNameOf(reason) 167 ) 168 } 169 170 class TestStringWriter : StringWriter() { <lambda>null171 fun getOutputAndClear() = toString().also { buffer.setLength(0) } 172 } 173 } 174