Loading tests/unit/src/android/networkstack/util/DnsUtilsTest.kt 0 → 100644 +92 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 android.networkstack.util import android.net.DnsResolver import android.net.DnsResolver.FLAG_EMPTY import android.net.DnsResolver.TYPE_A import android.net.DnsResolver.TYPE_AAAA import android.net.Network import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.networkstack.util.DnsUtils import com.android.networkstack.util.DnsUtils.TYPE_ADDRCONFIG import com.android.server.connectivity.NetworkMonitor.DnsLogFunc import java.net.InetAddress import java.net.UnknownHostException import kotlin.test.assertFailsWith import org.junit.Assert.assertArrayEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations const val DEFAULT_TIMEOUT_MS = 1000 const val SHORT_TIMEOUT_MS = 200 @RunWith(AndroidJUnit4::class) @SmallTest class DnsUtilsTest { val fakeNetwork: Network = Network(1234) @Mock lateinit var mockLogger: DnsLogFunc @Mock lateinit var mockResolver: DnsResolver lateinit var fakeDns: FakeDns @Before fun setup() { MockitoAnnotations.initMocks(this) fakeDns = FakeDns(mockResolver) fakeDns.startMocking() } private fun assertIpAddressArrayEquals(expect: Array<String>, actual: Array<InetAddress>) = assertArrayEquals("Array of IP addresses differs", expect, actual.map { it.getHostAddress() }.toTypedArray()) @Test fun testGetAllByNameWithTypeSuccess() { // Test different query types. verifyGetAllByName("www.google.com", arrayOf("2001:db8::1"), TYPE_AAAA) verifyGetAllByName("www.google.com", arrayOf("192.168.0.1"), TYPE_A) verifyGetAllByName("www.android.com", arrayOf("192.168.0.2", "2001:db8::2"), TYPE_ADDRCONFIG) } private fun verifyGetAllByName(name: String, expected: Array<String>, type: Int) { fakeDns.setAnswer(name, expected, type) DnsUtils.getAllByName(mockResolver, fakeNetwork, name, type, FLAG_EMPTY, DEFAULT_TIMEOUT_MS, mockLogger).let { assertIpAddressArrayEquals(expected, it) } } @Test fun testGetAllByNameWithTypeNoResult() { verifyGetAllByNameFails("www.android.com", TYPE_A) verifyGetAllByNameFails("www.android.com", TYPE_AAAA) verifyGetAllByNameFails("www.android.com", TYPE_ADDRCONFIG) } private fun verifyGetAllByNameFails(name: String, type: Int) { assertFailsWith<UnknownHostException> { DnsUtils.getAllByName(mockResolver, fakeNetwork, name, type, FLAG_EMPTY, SHORT_TIMEOUT_MS, mockLogger) } } // TODO: Add more tests. Verify timeout, logger and error. } No newline at end of file tests/unit/src/android/networkstack/util/FakeDns.kt 0 → 100644 +94 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 android.networkstack.util import android.net.DnsResolver import android.net.InetAddresses import android.os.Looper import android.os.Handler import com.android.internal.annotations.GuardedBy import com.android.networkstack.util.DnsUtils.TYPE_ADDRCONFIG import java.net.InetAddress import java.util.concurrent.Executor import org.mockito.invocation.InvocationOnMock import org.mockito.Mockito.any import org.mockito.Mockito.anyInt import org.mockito.Mockito.doAnswer // TODO: Integrate with NetworkMonitorTest. class FakeDns(val mockResolver: DnsResolver) { class DnsEntry(val hostname: String, val type: Int, val addresses: List<InetAddress>) { fun match(host: String, type: Int) = hostname.equals(host) && type == type } @GuardedBy("answers") val answers = ArrayList<DnsEntry>() fun getAnswer(hostname: String, type: Int): DnsEntry? = synchronized(answers) { return answers.firstOrNull { it.match(hostname, type) } } fun setAnswer(hostname: String, answer: Array<String>, type: Int) = synchronized(answers) { val ans = DnsEntry(hostname, type, generateAnswer(answer)) // Replace or remove the existing one. when (val index = answers.indexOfFirst { it.match(hostname, type) }) { -1 -> answers.add(ans) else -> answers[index] = ans } } private fun generateAnswer(answer: Array<String>) = answer.filterNotNull().map { InetAddresses.parseNumericAddress(it) } fun startMocking() { // Mock DnsResolver.query() w/o type doAnswer { mockAnswer(it, 1, -1, 3, 5) }.`when`(mockResolver).query(any() /* network */, any() /* domain */, anyInt() /* flags */, any() /* executor */, any() /* cancellationSignal */, any() /*callback*/) // Mock DnsResolver.query() w/ type doAnswer { mockAnswer(it, 1, 2, 4, 6) }.`when`(mockResolver).query(any() /* network */, any() /* domain */, anyInt() /* nsType */, anyInt() /* flags */, any() /* executor */, any() /* cancellationSignal */, any() /*callback*/) } private fun mockAnswer( it: InvocationOnMock, posHos: Int, posType: Int, posExecutor: Int, posCallback: Int ) { val hostname = it.arguments[posHos] as String val executor = it.arguments[posExecutor] as Executor val callback = it.arguments[posCallback] as DnsResolver.Callback<List<InetAddress>> var type = if (posType != -1) it.arguments[posType] as Int else TYPE_ADDRCONFIG val answer = getAnswer(hostname, type) if (!answer?.addresses.isNullOrEmpty()) { Handler(Looper.getMainLooper()).post({ executor.execute({ callback.onAnswer(answer?.addresses, 0); }) }) } } /** Clears all entries. */ fun clearAll() = synchronized(answers) { answers.clear() } } No newline at end of file Loading
tests/unit/src/android/networkstack/util/DnsUtilsTest.kt 0 → 100644 +92 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 android.networkstack.util import android.net.DnsResolver import android.net.DnsResolver.FLAG_EMPTY import android.net.DnsResolver.TYPE_A import android.net.DnsResolver.TYPE_AAAA import android.net.Network import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.networkstack.util.DnsUtils import com.android.networkstack.util.DnsUtils.TYPE_ADDRCONFIG import com.android.server.connectivity.NetworkMonitor.DnsLogFunc import java.net.InetAddress import java.net.UnknownHostException import kotlin.test.assertFailsWith import org.junit.Assert.assertArrayEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations const val DEFAULT_TIMEOUT_MS = 1000 const val SHORT_TIMEOUT_MS = 200 @RunWith(AndroidJUnit4::class) @SmallTest class DnsUtilsTest { val fakeNetwork: Network = Network(1234) @Mock lateinit var mockLogger: DnsLogFunc @Mock lateinit var mockResolver: DnsResolver lateinit var fakeDns: FakeDns @Before fun setup() { MockitoAnnotations.initMocks(this) fakeDns = FakeDns(mockResolver) fakeDns.startMocking() } private fun assertIpAddressArrayEquals(expect: Array<String>, actual: Array<InetAddress>) = assertArrayEquals("Array of IP addresses differs", expect, actual.map { it.getHostAddress() }.toTypedArray()) @Test fun testGetAllByNameWithTypeSuccess() { // Test different query types. verifyGetAllByName("www.google.com", arrayOf("2001:db8::1"), TYPE_AAAA) verifyGetAllByName("www.google.com", arrayOf("192.168.0.1"), TYPE_A) verifyGetAllByName("www.android.com", arrayOf("192.168.0.2", "2001:db8::2"), TYPE_ADDRCONFIG) } private fun verifyGetAllByName(name: String, expected: Array<String>, type: Int) { fakeDns.setAnswer(name, expected, type) DnsUtils.getAllByName(mockResolver, fakeNetwork, name, type, FLAG_EMPTY, DEFAULT_TIMEOUT_MS, mockLogger).let { assertIpAddressArrayEquals(expected, it) } } @Test fun testGetAllByNameWithTypeNoResult() { verifyGetAllByNameFails("www.android.com", TYPE_A) verifyGetAllByNameFails("www.android.com", TYPE_AAAA) verifyGetAllByNameFails("www.android.com", TYPE_ADDRCONFIG) } private fun verifyGetAllByNameFails(name: String, type: Int) { assertFailsWith<UnknownHostException> { DnsUtils.getAllByName(mockResolver, fakeNetwork, name, type, FLAG_EMPTY, SHORT_TIMEOUT_MS, mockLogger) } } // TODO: Add more tests. Verify timeout, logger and error. } No newline at end of file
tests/unit/src/android/networkstack/util/FakeDns.kt 0 → 100644 +94 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 android.networkstack.util import android.net.DnsResolver import android.net.InetAddresses import android.os.Looper import android.os.Handler import com.android.internal.annotations.GuardedBy import com.android.networkstack.util.DnsUtils.TYPE_ADDRCONFIG import java.net.InetAddress import java.util.concurrent.Executor import org.mockito.invocation.InvocationOnMock import org.mockito.Mockito.any import org.mockito.Mockito.anyInt import org.mockito.Mockito.doAnswer // TODO: Integrate with NetworkMonitorTest. class FakeDns(val mockResolver: DnsResolver) { class DnsEntry(val hostname: String, val type: Int, val addresses: List<InetAddress>) { fun match(host: String, type: Int) = hostname.equals(host) && type == type } @GuardedBy("answers") val answers = ArrayList<DnsEntry>() fun getAnswer(hostname: String, type: Int): DnsEntry? = synchronized(answers) { return answers.firstOrNull { it.match(hostname, type) } } fun setAnswer(hostname: String, answer: Array<String>, type: Int) = synchronized(answers) { val ans = DnsEntry(hostname, type, generateAnswer(answer)) // Replace or remove the existing one. when (val index = answers.indexOfFirst { it.match(hostname, type) }) { -1 -> answers.add(ans) else -> answers[index] = ans } } private fun generateAnswer(answer: Array<String>) = answer.filterNotNull().map { InetAddresses.parseNumericAddress(it) } fun startMocking() { // Mock DnsResolver.query() w/o type doAnswer { mockAnswer(it, 1, -1, 3, 5) }.`when`(mockResolver).query(any() /* network */, any() /* domain */, anyInt() /* flags */, any() /* executor */, any() /* cancellationSignal */, any() /*callback*/) // Mock DnsResolver.query() w/ type doAnswer { mockAnswer(it, 1, 2, 4, 6) }.`when`(mockResolver).query(any() /* network */, any() /* domain */, anyInt() /* nsType */, anyInt() /* flags */, any() /* executor */, any() /* cancellationSignal */, any() /*callback*/) } private fun mockAnswer( it: InvocationOnMock, posHos: Int, posType: Int, posExecutor: Int, posCallback: Int ) { val hostname = it.arguments[posHos] as String val executor = it.arguments[posExecutor] as Executor val callback = it.arguments[posCallback] as DnsResolver.Callback<List<InetAddress>> var type = if (posType != -1) it.arguments[posType] as Int else TYPE_ADDRCONFIG val answer = getAnswer(hostname, type) if (!answer?.addresses.isNullOrEmpty()) { Handler(Looper.getMainLooper()).post({ executor.execute({ callback.onAnswer(answer?.addresses, 0); }) }) } } /** Clears all entries. */ fun clearAll() = synchronized(answers) { answers.clear() } } No newline at end of file