Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a9e12757 authored by Chiachang Wang's avatar Chiachang Wang Committed by android-build-merger
Browse files

Merge "Add DnsUtilsTest" am: f81e4538

am: 37d8e7e6

Change-Id: I126f577ceaf820491a08a2c1183f4bb7a4f50f37
parents 7540852b 37d8e7e6
Loading
Loading
Loading
Loading
+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
+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