Move MdnsTestUtils to the net-tests-utils library
This is a no-op refactoring to move MdnsTestUtils to the net-tests-utils library. Bug: 343311941 Test: build pass Change-Id: I009f421da47771cf4d4d3673db1a807e555ce86f
This commit is contained in:
parent
7fecf30941
commit
be4e552787
3 changed files with 28 additions and 14 deletions
|
|
@ -0,0 +1,322 @@
|
|||
/*
|
||||
* 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.testutils
|
||||
|
||||
import android.net.DnsResolver
|
||||
import android.net.Network
|
||||
import android.net.nsd.NsdManager
|
||||
import android.net.nsd.NsdServiceInfo
|
||||
import android.os.Process
|
||||
import com.android.net.module.util.ArrayTrackRecord
|
||||
import com.android.net.module.util.DnsPacket
|
||||
import com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN
|
||||
import com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_LEN
|
||||
import com.android.net.module.util.NetworkStackConstants.IPV6_DST_ADDR_OFFSET
|
||||
import com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN
|
||||
import com.android.net.module.util.NetworkStackConstants.UDP_HEADER_LEN
|
||||
import com.android.net.module.util.TrackRecord
|
||||
import com.android.testutils.IPv6UdpFilter
|
||||
import com.android.testutils.TapPacketReader
|
||||
import java.net.Inet6Address
|
||||
import java.net.InetAddress
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.fail
|
||||
|
||||
private const val MDNS_REGISTRATION_TIMEOUT_MS = 10_000L
|
||||
private const val MDNS_PORT = 5353.toShort()
|
||||
const val MDNS_CALLBACK_TIMEOUT = 2000L
|
||||
const val MDNS_NO_CALLBACK_TIMEOUT_MS = 200L
|
||||
|
||||
interface NsdEvent
|
||||
open class NsdRecord<T : NsdEvent> private constructor(
|
||||
private val history: ArrayTrackRecord<T>,
|
||||
private val expectedThreadId: Int? = null
|
||||
) : TrackRecord<T> by history {
|
||||
constructor(expectedThreadId: Int? = null) : this(ArrayTrackRecord(), expectedThreadId)
|
||||
|
||||
val nextEvents = history.newReadHead()
|
||||
|
||||
override fun add(e: T): Boolean {
|
||||
if (expectedThreadId != null) {
|
||||
assertEquals(
|
||||
expectedThreadId, Process.myTid(),
|
||||
"Callback is running on the wrong thread"
|
||||
)
|
||||
}
|
||||
return history.add(e)
|
||||
}
|
||||
|
||||
inline fun <reified V : NsdEvent> expectCallbackEventually(
|
||||
timeoutMs: Long = MDNS_CALLBACK_TIMEOUT,
|
||||
crossinline predicate: (V) -> Boolean = { true }
|
||||
): V = nextEvents.poll(timeoutMs) { e -> e is V && predicate(e) } as V?
|
||||
?: fail("Callback for ${V::class.java.simpleName} not seen after $timeoutMs ms")
|
||||
|
||||
inline fun <reified V : NsdEvent> expectCallback(timeoutMs: Long = MDNS_CALLBACK_TIMEOUT): V {
|
||||
val nextEvent = nextEvents.poll(timeoutMs)
|
||||
assertNotNull(
|
||||
nextEvent, "No callback received after $timeoutMs ms, expected " +
|
||||
"${V::class.java.simpleName}"
|
||||
)
|
||||
assertTrue(
|
||||
nextEvent is V, "Expected ${V::class.java.simpleName} but got " +
|
||||
nextEvent.javaClass.simpleName
|
||||
)
|
||||
return nextEvent
|
||||
}
|
||||
|
||||
inline fun assertNoCallback(timeoutMs: Long = MDNS_NO_CALLBACK_TIMEOUT_MS) {
|
||||
val cb = nextEvents.poll(timeoutMs)
|
||||
assertNull(cb, "Expected no callback but got $cb")
|
||||
}
|
||||
}
|
||||
|
||||
class NsdDiscoveryRecord(expectedThreadId: Int? = null) :
|
||||
NsdManager.DiscoveryListener, NsdRecord<NsdDiscoveryRecord.DiscoveryEvent>(expectedThreadId) {
|
||||
sealed class DiscoveryEvent : NsdEvent {
|
||||
data class StartDiscoveryFailed(val serviceType: String, val errorCode: Int) :
|
||||
DiscoveryEvent()
|
||||
|
||||
data class StopDiscoveryFailed(val serviceType: String, val errorCode: Int) :
|
||||
DiscoveryEvent()
|
||||
|
||||
data class DiscoveryStarted(val serviceType: String) : DiscoveryEvent()
|
||||
data class DiscoveryStopped(val serviceType: String) : DiscoveryEvent()
|
||||
data class ServiceFound(val serviceInfo: NsdServiceInfo) : DiscoveryEvent()
|
||||
data class ServiceLost(val serviceInfo: NsdServiceInfo) : DiscoveryEvent()
|
||||
}
|
||||
|
||||
override fun onStartDiscoveryFailed(serviceType: String, err: Int) {
|
||||
add(DiscoveryEvent.StartDiscoveryFailed(serviceType, err))
|
||||
}
|
||||
|
||||
override fun onStopDiscoveryFailed(serviceType: String, err: Int) {
|
||||
add(DiscoveryEvent.StopDiscoveryFailed(serviceType, err))
|
||||
}
|
||||
|
||||
override fun onDiscoveryStarted(serviceType: String) {
|
||||
add(DiscoveryEvent.DiscoveryStarted(serviceType))
|
||||
}
|
||||
|
||||
override fun onDiscoveryStopped(serviceType: String) {
|
||||
add(DiscoveryEvent.DiscoveryStopped(serviceType))
|
||||
}
|
||||
|
||||
override fun onServiceFound(si: NsdServiceInfo) {
|
||||
add(DiscoveryEvent.ServiceFound(si))
|
||||
}
|
||||
|
||||
override fun onServiceLost(si: NsdServiceInfo) {
|
||||
add(DiscoveryEvent.ServiceLost(si))
|
||||
}
|
||||
|
||||
fun waitForServiceDiscovered(
|
||||
serviceName: String,
|
||||
serviceType: String,
|
||||
expectedNetwork: Network? = null
|
||||
): NsdServiceInfo {
|
||||
val serviceFound = expectCallbackEventually<DiscoveryEvent.ServiceFound> {
|
||||
it.serviceInfo.serviceName == serviceName &&
|
||||
(expectedNetwork == null ||
|
||||
expectedNetwork == it.serviceInfo.network)
|
||||
}.serviceInfo
|
||||
// Discovered service types have a dot at the end
|
||||
assertEquals("$serviceType.", serviceFound.serviceType)
|
||||
return serviceFound
|
||||
}
|
||||
}
|
||||
|
||||
class NsdRegistrationRecord(expectedThreadId: Int? = null) : NsdManager.RegistrationListener,
|
||||
NsdRecord<NsdRegistrationRecord.RegistrationEvent>(expectedThreadId) {
|
||||
sealed class RegistrationEvent : NsdEvent {
|
||||
abstract val serviceInfo: NsdServiceInfo
|
||||
|
||||
data class RegistrationFailed(
|
||||
override val serviceInfo: NsdServiceInfo,
|
||||
val errorCode: Int
|
||||
) : RegistrationEvent()
|
||||
|
||||
data class UnregistrationFailed(
|
||||
override val serviceInfo: NsdServiceInfo,
|
||||
val errorCode: Int
|
||||
) : RegistrationEvent()
|
||||
|
||||
data class ServiceRegistered(override val serviceInfo: NsdServiceInfo) :
|
||||
RegistrationEvent()
|
||||
|
||||
data class ServiceUnregistered(override val serviceInfo: NsdServiceInfo) :
|
||||
RegistrationEvent()
|
||||
}
|
||||
|
||||
override fun onRegistrationFailed(si: NsdServiceInfo, err: Int) {
|
||||
add(RegistrationEvent.RegistrationFailed(si, err))
|
||||
}
|
||||
|
||||
override fun onUnregistrationFailed(si: NsdServiceInfo, err: Int) {
|
||||
add(RegistrationEvent.UnregistrationFailed(si, err))
|
||||
}
|
||||
|
||||
override fun onServiceRegistered(si: NsdServiceInfo) {
|
||||
add(RegistrationEvent.ServiceRegistered(si))
|
||||
}
|
||||
|
||||
override fun onServiceUnregistered(si: NsdServiceInfo) {
|
||||
add(RegistrationEvent.ServiceUnregistered(si))
|
||||
}
|
||||
}
|
||||
|
||||
class NsdResolveRecord : NsdManager.ResolveListener,
|
||||
NsdRecord<NsdResolveRecord.ResolveEvent>() {
|
||||
sealed class ResolveEvent : NsdEvent {
|
||||
data class ResolveFailed(val serviceInfo: NsdServiceInfo, val errorCode: Int) :
|
||||
ResolveEvent()
|
||||
|
||||
data class ServiceResolved(val serviceInfo: NsdServiceInfo) : ResolveEvent()
|
||||
data class ResolutionStopped(val serviceInfo: NsdServiceInfo) : ResolveEvent()
|
||||
data class StopResolutionFailed(val serviceInfo: NsdServiceInfo, val errorCode: Int) :
|
||||
ResolveEvent()
|
||||
}
|
||||
|
||||
override fun onResolveFailed(si: NsdServiceInfo, err: Int) {
|
||||
add(ResolveEvent.ResolveFailed(si, err))
|
||||
}
|
||||
|
||||
override fun onServiceResolved(si: NsdServiceInfo) {
|
||||
add(ResolveEvent.ServiceResolved(si))
|
||||
}
|
||||
|
||||
override fun onResolutionStopped(si: NsdServiceInfo) {
|
||||
add(ResolveEvent.ResolutionStopped(si))
|
||||
}
|
||||
|
||||
override fun onStopResolutionFailed(si: NsdServiceInfo, err: Int) {
|
||||
super.onStopResolutionFailed(si, err)
|
||||
add(ResolveEvent.StopResolutionFailed(si, err))
|
||||
}
|
||||
}
|
||||
|
||||
class NsdServiceInfoCallbackRecord : NsdManager.ServiceInfoCallback,
|
||||
NsdRecord<NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent>() {
|
||||
sealed class ServiceInfoCallbackEvent : NsdEvent {
|
||||
data class RegisterCallbackFailed(val errorCode: Int) : ServiceInfoCallbackEvent()
|
||||
data class ServiceUpdated(val serviceInfo: NsdServiceInfo) : ServiceInfoCallbackEvent()
|
||||
object ServiceUpdatedLost : ServiceInfoCallbackEvent()
|
||||
object UnregisterCallbackSucceeded : ServiceInfoCallbackEvent()
|
||||
}
|
||||
|
||||
override fun onServiceInfoCallbackRegistrationFailed(err: Int) {
|
||||
add(ServiceInfoCallbackEvent.RegisterCallbackFailed(err))
|
||||
}
|
||||
|
||||
override fun onServiceUpdated(si: NsdServiceInfo) {
|
||||
add(ServiceInfoCallbackEvent.ServiceUpdated(si))
|
||||
}
|
||||
|
||||
override fun onServiceLost() {
|
||||
add(ServiceInfoCallbackEvent.ServiceUpdatedLost)
|
||||
}
|
||||
|
||||
override fun onServiceInfoCallbackUnregistered() {
|
||||
add(ServiceInfoCallbackEvent.UnregisterCallbackSucceeded)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMdnsPayload(packet: ByteArray) = packet.copyOfRange(
|
||||
ETHER_HEADER_LEN + IPV6_HEADER_LEN + UDP_HEADER_LEN, packet.size)
|
||||
|
||||
private fun getDstAddr(packet: ByteArray): Inet6Address {
|
||||
val v6AddrPos = ETHER_HEADER_LEN + IPV6_DST_ADDR_OFFSET
|
||||
return Inet6Address.getByAddress(packet.copyOfRange(v6AddrPos, v6AddrPos + IPV6_ADDR_LEN))
|
||||
as Inet6Address
|
||||
}
|
||||
|
||||
fun TapPacketReader.pollForMdnsPacket(
|
||||
timeoutMs: Long = MDNS_REGISTRATION_TIMEOUT_MS,
|
||||
predicate: (TestDnsPacket) -> Boolean
|
||||
): TestDnsPacket? {
|
||||
val mdnsProbeFilter = IPv6UdpFilter(srcPort = MDNS_PORT, dstPort = MDNS_PORT).and {
|
||||
val dst = getDstAddr(it)
|
||||
val mdnsPayload = getMdnsPayload(it)
|
||||
try {
|
||||
predicate(TestDnsPacket(mdnsPayload, dst))
|
||||
} catch (e: DnsPacket.ParseException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
return poll(timeoutMs, mdnsProbeFilter)?.let {
|
||||
TestDnsPacket(getMdnsPayload(it), getDstAddr(it))
|
||||
}
|
||||
}
|
||||
|
||||
fun TapPacketReader.pollForProbe(
|
||||
serviceName: String,
|
||||
serviceType: String,
|
||||
timeoutMs: Long = MDNS_REGISTRATION_TIMEOUT_MS
|
||||
): TestDnsPacket? = pollForMdnsPacket(timeoutMs) {
|
||||
it.isProbeFor("$serviceName.$serviceType.local")
|
||||
}
|
||||
|
||||
fun TapPacketReader.pollForAdvertisement(
|
||||
serviceName: String,
|
||||
serviceType: String,
|
||||
timeoutMs: Long = MDNS_REGISTRATION_TIMEOUT_MS
|
||||
): TestDnsPacket? = pollForMdnsPacket(timeoutMs) {
|
||||
it.isReplyFor("$serviceName.$serviceType.local")
|
||||
}
|
||||
|
||||
fun TapPacketReader.pollForQuery(
|
||||
recordName: String,
|
||||
vararg requiredTypes: Int,
|
||||
timeoutMs: Long = MDNS_REGISTRATION_TIMEOUT_MS
|
||||
): TestDnsPacket? = pollForMdnsPacket(timeoutMs) { it.isQueryFor(recordName, *requiredTypes) }
|
||||
|
||||
fun TapPacketReader.pollForReply(
|
||||
recordName: String,
|
||||
type: Int,
|
||||
timeoutMs: Long = MDNS_REGISTRATION_TIMEOUT_MS
|
||||
): TestDnsPacket? = pollForMdnsPacket(timeoutMs) { it.isReplyFor(recordName, type) }
|
||||
|
||||
fun TapPacketReader.pollForReply(
|
||||
serviceName: String,
|
||||
serviceType: String,
|
||||
timeoutMs: Long = MDNS_REGISTRATION_TIMEOUT_MS
|
||||
): TestDnsPacket? = pollForMdnsPacket(timeoutMs) {
|
||||
it.isReplyFor("$serviceName.$serviceType.local")
|
||||
}
|
||||
|
||||
class TestDnsPacket(data: ByteArray, val dstAddr: InetAddress) : DnsPacket(data) {
|
||||
val header: DnsHeader
|
||||
get() = mHeader
|
||||
val records: Array<List<DnsRecord>>
|
||||
get() = mRecords
|
||||
fun isProbeFor(name: String): Boolean = mRecords[QDSECTION].any {
|
||||
it.dName == name && it.nsType == DnsResolver.TYPE_ANY
|
||||
}
|
||||
|
||||
fun isReplyFor(name: String, type: Int = DnsResolver.TYPE_SRV): Boolean =
|
||||
mRecords[ANSECTION].any {
|
||||
it.dName == name && it.nsType == type
|
||||
}
|
||||
|
||||
fun isQueryFor(name: String, vararg requiredTypes: Int): Boolean = requiredTypes.all { type ->
|
||||
mRecords[QDSECTION].any {
|
||||
it.dName == name && it.nsType == type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,9 @@ import androidx.test.filters.SmallTest
|
|||
import com.android.testutils.ConnectivityModuleTest
|
||||
import com.android.testutils.DevSdkIgnoreRule
|
||||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
import com.android.testutils.NsdDiscoveryRecord
|
||||
import com.android.testutils.TapPacketReader
|
||||
import com.android.testutils.pollForQuery
|
||||
import com.android.testutils.tryTest
|
||||
import java.util.Random
|
||||
import kotlin.test.assertEquals
|
||||
|
|
|
|||
|
|
@ -39,19 +39,6 @@ import android.net.TestNetworkInterface
|
|||
import android.net.TestNetworkManager
|
||||
import android.net.TestNetworkSpecifier
|
||||
import android.net.connectivity.ConnectivityCompatChanges
|
||||
import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStarted
|
||||
import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStopped
|
||||
import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.ServiceFound
|
||||
import android.net.cts.NsdDiscoveryRecord.DiscoveryEvent.ServiceLost
|
||||
import android.net.cts.NsdRegistrationRecord.RegistrationEvent.RegistrationFailed
|
||||
import android.net.cts.NsdRegistrationRecord.RegistrationEvent.ServiceRegistered
|
||||
import android.net.cts.NsdRegistrationRecord.RegistrationEvent.ServiceUnregistered
|
||||
import android.net.cts.NsdResolveRecord.ResolveEvent.ResolutionStopped
|
||||
import android.net.cts.NsdResolveRecord.ResolveEvent.ServiceResolved
|
||||
import android.net.cts.NsdResolveRecord.ResolveEvent.StopResolutionFailed
|
||||
import android.net.cts.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.ServiceUpdated
|
||||
import android.net.cts.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.ServiceUpdatedLost
|
||||
import android.net.cts.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.UnregisterCallbackSucceeded
|
||||
import android.net.cts.util.CtsNetUtils
|
||||
import android.net.nsd.DiscoveryRequest
|
||||
import android.net.nsd.NsdManager
|
||||
|
|
@ -92,9 +79,29 @@ import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
|
|||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
import com.android.testutils.DeviceConfigRule
|
||||
import com.android.testutils.NSResponder
|
||||
import com.android.testutils.NsdDiscoveryRecord
|
||||
import com.android.testutils.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStarted
|
||||
import com.android.testutils.NsdDiscoveryRecord.DiscoveryEvent.DiscoveryStopped
|
||||
import com.android.testutils.NsdDiscoveryRecord.DiscoveryEvent.ServiceFound
|
||||
import com.android.testutils.NsdDiscoveryRecord.DiscoveryEvent.ServiceLost
|
||||
import com.android.testutils.NsdEvent
|
||||
import com.android.testutils.NsdRecord
|
||||
import com.android.testutils.NsdRegistrationRecord
|
||||
import com.android.testutils.NsdRegistrationRecord.RegistrationEvent.RegistrationFailed
|
||||
import com.android.testutils.NsdRegistrationRecord.RegistrationEvent.ServiceRegistered
|
||||
import com.android.testutils.NsdRegistrationRecord.RegistrationEvent.ServiceUnregistered
|
||||
import com.android.testutils.NsdResolveRecord
|
||||
import com.android.testutils.NsdResolveRecord.ResolveEvent.ResolutionStopped
|
||||
import com.android.testutils.NsdResolveRecord.ResolveEvent.ServiceResolved
|
||||
import com.android.testutils.NsdResolveRecord.ResolveEvent.StopResolutionFailed
|
||||
import com.android.testutils.NsdServiceInfoCallbackRecord
|
||||
import com.android.testutils.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.ServiceUpdated
|
||||
import com.android.testutils.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.ServiceUpdatedLost
|
||||
import com.android.testutils.NsdServiceInfoCallbackRecord.ServiceInfoCallbackEvent.UnregisterCallbackSucceeded
|
||||
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
|
||||
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
|
||||
import com.android.testutils.TapPacketReader
|
||||
import com.android.testutils.TestDnsPacket
|
||||
import com.android.testutils.TestableNetworkAgent
|
||||
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
|
||||
import com.android.testutils.TestableNetworkCallback
|
||||
|
|
@ -102,6 +109,11 @@ import com.android.testutils.assertContainsExactly
|
|||
import com.android.testutils.assertEmpty
|
||||
import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk30
|
||||
import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk33
|
||||
import com.android.testutils.pollForAdvertisement
|
||||
import com.android.testutils.pollForMdnsPacket
|
||||
import com.android.testutils.pollForProbe
|
||||
import com.android.testutils.pollForQuery
|
||||
import com.android.testutils.pollForReply
|
||||
import com.android.testutils.runAsShell
|
||||
import com.android.testutils.tryTest
|
||||
import com.android.testutils.waitForIdle
|
||||
|
|
|
|||
Loading…
Reference in a new issue