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

Unverified Commit d3c1dbb5 authored by Sunik Kupfer's avatar Sunik Kupfer Committed by GitHub
Browse files

`DnsRecordResolverTest`: Use seeded random number generator for deterministic test result (#1306)

* Use seeded random number generator for a 100% deterministic test result

* Fix typo

* Rename variable

* Mock random numbers and assert corresponding record is found
parent cd554d88
Loading
Loading
Loading
Loading
+17 −17
Original line number Original line Diff line number Diff line
@@ -6,6 +6,8 @@ package at.bitfire.davdroid.network


import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.HiltAndroidTest
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertNull
@@ -18,6 +20,7 @@ import org.xbill.DNS.Name
import org.xbill.DNS.SRVRecord
import org.xbill.DNS.SRVRecord
import org.xbill.DNS.TXTRecord
import org.xbill.DNS.TXTRecord
import javax.inject.Inject
import javax.inject.Inject
import kotlin.random.Random


@HiltAndroidTest
@HiltAndroidTest
class DnsRecordResolverTest {
class DnsRecordResolverTest {
@@ -65,25 +68,22 @@ class DnsRecordResolverTest {
            Name.fromString("_caldavs._tcp.example.com."),
            Name.fromString("_caldavs._tcp.example.com."),
            DClass.IN, 3600, 10, 20, 8443, Name.fromString("dav1020.example.com.")
            DClass.IN, 3600, 10, 20, 8443, Name.fromString("dav1020.example.com.")
        )
        )

        val dns1030 = SRVRecord(
        // entries are selected randomly (for load balancing)
            Name.fromString("_caldavs._tcp.example.com."),
        // run 1000 times to get a good distribution
            DClass.IN, 3600, 10, 30, 8443, Name.fromString("dav1030.example.com.")
        val counts = IntArray(2)
        )
        for (i in 0 until 1000) {
        val records = arrayOf(dns1010, dns1020, dns1030)
            val result = dnsRecordResolver.bestSRVRecord(arrayOf(dns1010, dns1020))


        val randomNumberGenerator = mockk<Random>()
            when (result) {
        for (i in 0..60) {
                dns1010 -> counts[0]++
            every { randomNumberGenerator.nextInt(0, 61) } returns i
                dns1020 -> counts[1]++
            val expected = when (i) {
                in 0..10 -> dns1010
                in 11..30 -> dns1020
                else -> dns1030
            }
            }
            assertEquals(expected, dnsRecordResolver.bestSRVRecord(records, randomNumberGenerator))
        }
        }

        /* We had weights 10 and 20, so the distribution of 1000 tries should be roughly
            weight 10   fraction 1/3   expected count 333   binomial distribution (p=1/3) with 99.99% in [275..393]
            weight 20   fraction 2/3   expected count 667   binomial distribution (p=2/3) with 99.99% in [607..725]
         */
        assertTrue(counts[0] in 275..393)
        assertTrue(counts[1] in 607..725)
    }
    }


    @Test
    @Test
+10 −2
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import java.util.LinkedList
import java.util.TreeMap
import java.util.TreeMap
import java.util.logging.Logger
import java.util.logging.Logger
import javax.inject.Inject
import javax.inject.Inject
import kotlin.random.Random


/**
/**
 * Allows to resolve SRV/TXT records. Chooses the correct resolver, DNS servers etc.
 * Allows to resolve SRV/TXT records. Chooses the correct resolver, DNS servers etc.
@@ -102,7 +103,14 @@ class DnsRecordResolver @Inject constructor(


    // record selection
    // record selection


    fun bestSRVRecord(records: Array<out Record>): SRVRecord? {
    /**
     * Selects the best SRV record from a list of records, based on algorithm from RFC 2782.
     *
     * @param records the records to choose from
     * @param randomGenerator a random number generator to use for random selection
     * @return the best SRV record, or `null` if no SRV record is available
     */
    fun bestSRVRecord(records: Array<out Record>, randomGenerator: Random = Random.Default): SRVRecord? {
        val srvRecords = records.filterIsInstance<SRVRecord>()
        val srvRecords = records.filterIsInstance<SRVRecord>()
        if (srvRecords.size <= 1)
        if (srvRecords.size <= 1)
            return srvRecords.firstOrNull()
            return srvRecords.firstOrNull()
@@ -141,7 +149,7 @@ class DnsRecordResolver @Inject constructor(
            map[runningWeight] = record
            map[runningWeight] = record
        }
        }


        val selector = (0..runningWeight).random()
        val selector = (0..runningWeight).random(randomGenerator)
        return map.ceilingEntry(selector)!!.value
        return map.ceilingEntry(selector)!!.value
    }
    }