/*
 * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

package io.ktor.client.tests.utils

import ch.qos.logback.classic.*
import ch.qos.logback.classic.Logger
import io.ktor.server.engine.*
import kotlinx.coroutines.debug.junit4.*
import org.junit.*
import org.slf4j.*
import java.net.*
import java.util.concurrent.*

@Suppress("KDocMissingDocumentation")
abstract class TestWithKtor {
    protected val serverPort: Int = ServerSocket(0).use { it.localPort }

    @get:Rule
    open val timeout = CoroutinesTimeout.seconds(3 * 60)

    abstract val server: ApplicationEngine

    init {
        (LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as? Logger)?.level = Level.ERROR
    }

    @Before
    fun startServer() {
        server.start()
        ensureServerRunning()
    }

    @After
    fun stopServer() {
        server.stop(0, 0, TimeUnit.SECONDS)
    }

    private fun ensureServerRunning() {
        do {
            try {
                Socket("localhost", serverPort).close()
                break
            } catch (_: Throwable) {
                Thread.sleep(100)
            }
        } while (true)
    }
}

/*
 * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

package io.ktor.client.tests.utils

import ch.qos.logback.classic.*
import ch.qos.logback.classic.Logger
import io.ktor.server.engine.*
import kotlinx.coroutines.debug.junit4.*
import org.junit.*
import org.slf4j.*
import java.net.*
import java.util.concurrent.*

@Suppress("KDocMissingDocumentation")
abstract class TestWithKtor {
    protected val serverPort: Int = ServerSocket(0).use { it.localPort }

    @get:Rule
    open val timeout = CoroutinesTimeout.seconds(3 * 60)

    abstract val server: ApplicationEngine

    init {
        (LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as? Logger)?.level = Level.ERROR
    }

    @Before
    fun startServer() {
        var attempt = 0

        do {
            attempt++
            try {
                server.start()
                break
            } catch (cause: Throwable) {
                if (attempt >= 10) throw cause
                Thread.sleep(250L * attempt)
            }
        } while (true)

        ensureServerRunning()
    }

    @After
    fun stopServer() {
        server.stop(0, 0, TimeUnit.SECONDS)
    }

    private fun ensureServerRunning() {
        do {
            try {
                Socket("localhost", serverPort).close()
                break
            } catch (_: Throwable) {
                Thread.sleep(100)
            }
        } while (true)
    }
}

Make client tests retry server startup if failed
Make client tests retry server startup if failed

ktorio/ktor,ktorio/ktor,ktorio/ktor,ktorio/ktor