mirror of
https://github.com/square/okhttp.git
synced 2025-08-07 12:42:57 +03:00
Create OkHttpClient instances eagerly in tests
We used to do this lazily because OkHttpClient instances were somewhat heavy: each standalone instance held its own ExecutorService for the connection pool. Now that we have TaskRunner each instance is much more lightweight and the drawbacks of creating instances eagerly is negligible.
This commit is contained in:
@@ -22,31 +22,36 @@ import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
import java.net.InetAddress
|
||||
import java.util.concurrent.ConcurrentLinkedDeque
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/** Apply this rule to tests that need an OkHttpClient instance. */
|
||||
class OkHttpClientTestRule : TestRule {
|
||||
private val clientEventsList = mutableListOf<String>()
|
||||
private var prototype: OkHttpClient? = null
|
||||
private var testClient: OkHttpClient? = null
|
||||
|
||||
/**
|
||||
* Returns an OkHttpClient for all tests to use as a starting point.
|
||||
* Returns an OkHttpClient for tests to use as a starting point.
|
||||
*
|
||||
* The shared instance allows all tests to share a single connection pool, which prevents idle
|
||||
* connections from consuming unnecessary resources while connections wait to be evicted.
|
||||
* The returned client installs a default event listener that gathers debug information. This will
|
||||
* be logged if the test fails.
|
||||
*
|
||||
* This client is also configured to be slightly more deterministic, returning a single IP
|
||||
* address for all hosts, regardless of the actual number of IP addresses reported by DNS.
|
||||
*/
|
||||
fun newClient(): OkHttpClient {
|
||||
return newClientBuilder().build()
|
||||
var client = testClient
|
||||
if (client == null) {
|
||||
client = OkHttpClient.Builder()
|
||||
.dns(SINGLE_INET_ADDRESS_DNS) // Prevent unexpected fallback addresses.
|
||||
.eventListener(ClientRuleEventListener { addEvent(it) })
|
||||
.build()
|
||||
testClient = client
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
fun newClientBuilder(): OkHttpClient.Builder {
|
||||
return checkNotNull(prototype) { "don't create clients in test initialization!" }
|
||||
.newBuilder()
|
||||
.eventListener(ClientRuleEventListener { addEvent(it) })
|
||||
return newClient().newBuilder()
|
||||
}
|
||||
|
||||
@Synchronized private fun addEvent(it: String) {
|
||||
@@ -54,7 +59,7 @@ class OkHttpClientTestRule : TestRule {
|
||||
}
|
||||
|
||||
fun ensureAllConnectionsReleased() {
|
||||
prototype?.let {
|
||||
testClient?.let {
|
||||
val connectionPool = it.connectionPool
|
||||
connectionPool.evictAll()
|
||||
assertThat(connectionPool.connectionCount()).isEqualTo(0)
|
||||
@@ -72,7 +77,6 @@ class OkHttpClientTestRule : TestRule {
|
||||
override fun apply(base: Statement, description: Description): Statement {
|
||||
return object : Statement() {
|
||||
override fun evaluate() {
|
||||
acquireClient()
|
||||
try {
|
||||
base.evaluate()
|
||||
logEventsIfFlaky(description)
|
||||
@@ -86,15 +90,8 @@ class OkHttpClientTestRule : TestRule {
|
||||
}
|
||||
}
|
||||
|
||||
private fun acquireClient() {
|
||||
prototype = prototypes.poll() ?: freshClient()
|
||||
}
|
||||
|
||||
private fun releaseClient() {
|
||||
prototype?.let {
|
||||
prototypes.push(it)
|
||||
prototype = null
|
||||
}
|
||||
testClient?.dispatcher?.executorService?.shutdown()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,40 +116,16 @@ class OkHttpClientTestRule : TestRule {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called if a test is known to be leaky.
|
||||
*/
|
||||
fun abandonClient() {
|
||||
prototype?.let {
|
||||
prototype = null
|
||||
it.dispatcher.executorService.shutdownNow()
|
||||
it.connectionPool.evictAll()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Quick and dirty pool of OkHttpClient instances. Each has its own independent dispatcher and
|
||||
* connection pool. This way we can reuse expensive resources while preventing concurrent tests
|
||||
* from interfering with each other.
|
||||
*/
|
||||
internal val prototypes = ConcurrentLinkedDeque<OkHttpClient>()
|
||||
|
||||
/**
|
||||
* A network that resolves only one IP address per host. Use this when testing route selection
|
||||
* fallbacks to prevent the host machine's various IP addresses from interfering.
|
||||
*/
|
||||
internal val SINGLE_INET_ADDRESS_DNS = object : Dns {
|
||||
private val SINGLE_INET_ADDRESS_DNS = object : Dns {
|
||||
override fun lookup(hostname: String): List<InetAddress> {
|
||||
val addresses = Dns.SYSTEM.lookup(hostname)
|
||||
return listOf(addresses[0])
|
||||
}
|
||||
}
|
||||
|
||||
private fun freshClient(): OkHttpClient {
|
||||
return OkHttpClient.Builder()
|
||||
.dns(SINGLE_INET_ADDRESS_DNS) // Prevent unexpected fallback addresses.
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user