1
0
mirror of https://github.com/square/okhttp.git synced 2025-08-08 23:42:08 +03:00

Output client events after tagged @Flaky tests or failures (#5217)

Collect additional debugging info for know problem tests or after unplanned failures.
This commit is contained in:
Yuri Schimke
2019-07-16 22:34:32 +01:00
committed by GitHub
parent daf49ff1ce
commit a6c4ff6e44
7 changed files with 221 additions and 18 deletions

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2018 Square, Inc.
*
* 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 okhttp3
import java.io.IOException
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.Proxy
import java.util.concurrent.TimeUnit
class ClientRuleEventListener(var logger: (String) -> Unit) : EventListener(),
EventListener.Factory {
private var startNs: Long = 0
override fun create(call: Call): EventListener = this
override fun callStart(call: Call) {
startNs = System.nanoTime()
logWithTime("callStart: ${call.request()}")
}
override fun dnsStart(call: Call, domainName: String) {
logWithTime("dnsStart: $domainName")
}
override fun dnsEnd(call: Call, domainName: String, inetAddressList: List<InetAddress>) {
logWithTime("dnsEnd: $inetAddressList")
}
override fun connectStart(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy) {
logWithTime("connectStart: $inetSocketAddress $proxy")
}
override fun secureConnectStart(call: Call) {
logWithTime("secureConnectStart")
}
override fun secureConnectEnd(call: Call, handshake: Handshake?) {
logWithTime("secureConnectEnd: $handshake")
}
override fun connectEnd(
call: Call,
inetSocketAddress: InetSocketAddress,
proxy: Proxy,
protocol: Protocol?
) {
logWithTime("connectEnd: $protocol")
}
override fun connectFailed(
call: Call,
inetSocketAddress: InetSocketAddress,
proxy: Proxy,
protocol: Protocol?,
ioe: IOException
) {
logWithTime("connectFailed: $protocol $ioe")
}
override fun connectionAcquired(call: Call, connection: Connection) {
logWithTime("connectionAcquired: $connection")
}
override fun connectionReleased(call: Call, connection: Connection) {
logWithTime("connectionReleased")
}
override fun requestHeadersStart(call: Call) {
logWithTime("requestHeadersStart")
}
override fun requestHeadersEnd(call: Call, request: Request) {
logWithTime("requestHeadersEnd")
}
override fun requestBodyStart(call: Call) {
logWithTime("requestBodyStart")
}
override fun requestBodyEnd(call: Call, byteCount: Long) {
logWithTime("requestBodyEnd: byteCount=$byteCount")
}
override fun requestFailed(call: Call, ioe: IOException) {
logWithTime("requestFailed: $ioe")
}
override fun responseHeadersStart(call: Call) {
logWithTime("responseHeadersStart")
}
override fun responseHeadersEnd(call: Call, response: Response) {
logWithTime("responseHeadersEnd: $response")
}
override fun responseBodyStart(call: Call) {
logWithTime("responseBodyStart")
}
override fun responseBodyEnd(call: Call, byteCount: Long) {
logWithTime("responseBodyEnd: byteCount=$byteCount")
}
override fun responseFailed(call: Call, ioe: IOException) {
logWithTime("responseFailed: $ioe")
}
override fun callEnd(call: Call) {
logWithTime("callEnd")
}
override fun callFailed(call: Call, ioe: IOException) {
logWithTime("callFailed: $ioe")
}
private fun logWithTime(message: String) {
val timeMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
logger.invoke("[$timeMs ms] $message")
}
}

View File

@@ -15,6 +15,7 @@
*/
package okhttp3
import okhttp3.testing.Flaky
import org.assertj.core.api.Assertions.assertThat
import org.junit.rules.TestRule
import org.junit.runner.Description
@@ -24,6 +25,7 @@ import java.util.concurrent.ConcurrentLinkedDeque
/** Apply this rule to tests that need an OkHttpClient instance. */
class OkHttpClientTestRule : TestRule {
private val clientEventsList = mutableListOf<String>()
private var prototype: OkHttpClient? = null
/**
@@ -40,7 +42,13 @@ class OkHttpClientTestRule : TestRule {
}
fun newClientBuilder(): OkHttpClient.Builder {
return checkNotNull(prototype) { "don't create clients in test initialization!" }.newBuilder()
return checkNotNull(prototype) { "don't create clients in test initialization!" }
.newBuilder()
.eventListener(ClientRuleEventListener { addEvent(it) })
}
@Synchronized private fun addEvent(it: String) {
clientEventsList.add(it)
}
fun ensureAllConnectionsReleased() {
@@ -57,6 +65,10 @@ class OkHttpClientTestRule : TestRule {
acquireClient()
try {
base.evaluate()
logEventsIfFlaky(description)
} catch (t: Throwable) {
logEvents()
throw t
} finally {
ensureAllConnectionsReleased()
releaseClient()
@@ -64,9 +76,7 @@ class OkHttpClientTestRule : TestRule {
}
private fun acquireClient() {
prototype = prototypes.poll() ?: OkHttpClient.Builder()
.dns(SINGLE_INET_ADDRESS_DNS) // Prevent unexpected fallback addresses.
.build()
prototype = prototypes.poll() ?: freshClient()
}
private fun releaseClient() {
@@ -78,6 +88,26 @@ class OkHttpClientTestRule : TestRule {
}
}
private fun logEventsIfFlaky(description: Description) {
if (isTestFlaky(description)) {
logEvents()
}
}
private fun isTestFlaky(description: Description): Boolean {
return description.annotations.any { it.annotationClass == Flaky::class } ||
description.testClass.annotations.any { it.annotationClass == Flaky::class }
}
@Synchronized private fun logEvents() {
// Will be ineffective if test overrides the listener
println("Events (${clientEventsList.size})")
for (e in clientEventsList) {
println(e)
}
}
/**
* Called if a test is known to be leaky.
*/
@@ -107,5 +137,11 @@ class OkHttpClientTestRule : TestRule {
return listOf(addresses[0])
}
}
private fun freshClient(): OkHttpClient {
return OkHttpClient.Builder()
.dns(SINGLE_INET_ADDRESS_DNS) // Prevent unexpected fallback addresses.
.build()
}
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2019 Square, Inc.
*
* 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 okhttp3.testing
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
/**
* Annotation marking a test as flaky, and requires extra logging and linking against
* a known github issue. This does not ignore the failure.
*/
annotation class Flaky(val issues: Array<String> = [])