1 /* 2 * Copyright (C) 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 package android.net.util 17 18 import android.net.ip.ConnectivityPacketTracker 19 import android.os.HandlerThread 20 import androidx.test.filters.SmallTest 21 import com.android.testutils.DevSdkIgnoreRunner 22 import com.android.testutils.FunctionalUtils.ThrowingSupplier 23 import com.android.testutils.assertThrows 24 import com.android.testutils.visibleOnHandlerThread 25 import kotlin.test.assertEquals 26 import kotlin.test.assertTrue 27 import org.junit.After 28 import org.junit.Before 29 import org.junit.Test 30 import org.mockito.ArgumentMatchers.any 31 import org.mockito.ArgumentMatchers.anyInt 32 import org.mockito.ArgumentMatchers.eq 33 import org.mockito.Mockito 34 import org.mockito.Mockito.clearInvocations 35 import org.mockito.Mockito.doReturn 36 import org.mockito.Mockito.mock 37 import org.mockito.Mockito.timeout 38 import org.mockito.Mockito.verify 39 import org.mockito.Mockito.verifyZeroInteractions 40 41 /** 42 * Test for RawPacketTracker. 43 */ 44 @SmallTest 45 @DevSdkIgnoreRunner.MonitorThreadLeak 46 class RawPacketTrackerTest { 47 companion object { 48 private const val TEST_TIMEOUT_MS: Long = 1000 49 private const val TEST_MAX_CAPTURE_TIME_MS: Long = 1000 50 private const val TAG = "RawPacketTrackerTest" 51 } 52 53 private val deps = mock(RawPacketTracker.Dependencies::class.java) 54 private val tracker = mock(ConnectivityPacketTracker::class.java) 55 private val ifaceName = "lo" <lambda>null56 private val handlerThread by lazy { 57 HandlerThread("$TAG-handler-thread").apply { start() } 58 } 59 private lateinit var rawTracker: RawPacketTracker 60 61 @Before setUpnull62 fun setUp() { 63 doReturn(handlerThread).`when`(deps).createHandlerThread() 64 doReturn(handlerThread.looper).`when`(deps).getLooper(any()) 65 doReturn(tracker).`when`(deps).createPacketTracker(any(), any(), anyInt()) 66 rawTracker = RawPacketTracker(deps) 67 } 68 69 @After tearDownnull70 fun tearDown() { 71 Mockito.framework().clearInlineMocks() 72 handlerThread.quitSafely() 73 handlerThread.join() 74 } 75 76 @Test testStartCapturenull77 fun testStartCapture() { 78 // start capturing 79 startCaptureOnHandler(ifaceName) 80 verifySetCapture(true, 1) 81 82 assertTrue(rawTracker.handler.hasMessages(RawPacketTracker.CMD_STOP_CAPTURE)) 83 } 84 85 @Test testInvalidStartCapturenull86 fun testInvalidStartCapture() { 87 // start capturing with negative timeout 88 assertThrows(IllegalArgumentException::class.java) { 89 startCaptureOnHandler(ifaceName, -1) 90 } 91 } 92 93 @Test testStopCapturenull94 fun testStopCapture() { 95 // start capturing 96 startCaptureOnHandler(ifaceName) 97 // simulate capture status for stop capturing 98 verifySetCapture(true, 1) 99 100 // stop capturing 101 stopCaptureOnHandler(ifaceName) 102 verifySetCapture(false, 1) 103 verifyZeroInteractions(tracker) 104 } 105 106 @Test testDuplicatedStartAndStopnull107 fun testDuplicatedStartAndStop() { 108 // start capture with a long timeout 109 startCaptureOnHandler(ifaceName, 10_000) 110 verifySetCapture(true, 1) 111 112 // start capturing for multiple times 113 for (i in 1..10) { 114 assertThrows(RuntimeException::class.java) { 115 startCaptureOnHandler(ifaceName) 116 } 117 } 118 119 // expect no duplicated start capture 120 verifySetCapture(true, 0) 121 122 // stop capturing for multiple times 123 stopCaptureOnHandler(ifaceName) 124 verifySetCapture(false, 1) 125 for (i in 1..10) { 126 assertThrows(RuntimeException::class.java) { 127 stopCaptureOnHandler(ifaceName) 128 } 129 } 130 131 verifySetCapture(false, 0) 132 verifyZeroInteractions(tracker) 133 } 134 135 @Test testMatchedPacketCountnull136 fun testMatchedPacketCount() { 137 val matchedPkt = "12345" 138 val notMatchedPkt = "54321" 139 140 // simulate get matched packet count 141 doReturn(1).`when`(tracker).getMatchedPacketCount(matchedPkt) 142 // simulate get not matched packet count 143 doReturn(0).`when`(tracker).getMatchedPacketCount(notMatchedPkt) 144 145 // start capture 146 startCaptureOnHandler(ifaceName) 147 148 assertEquals(1, getMatchedPktCntOnHandler(ifaceName, matchedPkt)) 149 assertEquals(0, getMatchedPktCntOnHandler(ifaceName, notMatchedPkt)) 150 151 // for non-existed interface 152 val nonExistedIface = "non-existed-iface" 153 assertThrows(RuntimeException::class.java) { 154 getMatchedPktCntOnHandler(nonExistedIface, matchedPkt) 155 getMatchedPktCntOnHandler(nonExistedIface, notMatchedPkt) 156 } 157 158 // stop capture 159 stopCaptureOnHandler(ifaceName) 160 161 // expect no matched packet after stop capturing 162 assertThrows(RuntimeException::class.java) { 163 getMatchedPktCntOnHandler(ifaceName, matchedPkt) 164 getMatchedPktCntOnHandler(ifaceName, notMatchedPkt) 165 } 166 } 167 startCaptureOnHandlernull168 private fun startCaptureOnHandler( 169 ifaceName: String, maxCaptureTime: Long = TEST_MAX_CAPTURE_TIME_MS 170 ) { 171 visibleOnHandlerThread(rawTracker.handler) { 172 rawTracker.startCapture(ifaceName, maxCaptureTime) 173 } 174 } 175 stopCaptureOnHandlernull176 private fun stopCaptureOnHandler(ifaceName: String) { 177 visibleOnHandlerThread(rawTracker.handler) { 178 rawTracker.stopCapture(ifaceName) 179 } 180 } 181 getMatchedPktCntOnHandlernull182 private fun getMatchedPktCntOnHandler(ifaceName: String, packetPattern: String): Int { 183 return visibleOnHandlerThread(rawTracker.handler, ThrowingSupplier { 184 rawTracker.getMatchedPacketCount(ifaceName, packetPattern) 185 }) 186 } 187 verifySetCapturenull188 private fun verifySetCapture( 189 isCapture: Boolean, 190 receiveCnt: Int 191 ) { 192 verify(tracker, timeout(TEST_TIMEOUT_MS).times(receiveCnt)).setCapture(eq(isCapture)) 193 clearInvocations<Any>(tracker) 194 } 195 }