diff options
-rw-r--r-- | tests/unit/src/android/networkstack/util/DnsUtilsTest.kt | 92 | ||||
-rw-r--r-- | tests/unit/src/android/networkstack/util/FakeDns.kt | 94 |
2 files changed, 186 insertions, 0 deletions
diff --git a/tests/unit/src/android/networkstack/util/DnsUtilsTest.kt b/tests/unit/src/android/networkstack/util/DnsUtilsTest.kt new file mode 100644 index 0000000..815fc60 --- /dev/null +++ b/tests/unit/src/android/networkstack/util/DnsUtilsTest.kt @@ -0,0 +1,92 @@ +/* + * 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 diff --git a/tests/unit/src/android/networkstack/util/FakeDns.kt b/tests/unit/src/android/networkstack/util/FakeDns.kt new file mode 100644 index 0000000..f0d44d0 --- /dev/null +++ b/tests/unit/src/android/networkstack/util/FakeDns.kt @@ -0,0 +1,94 @@ +/* + * 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 |