All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.zegreatrob.testmints.Setup.kt Maven / Gradle / Ivy

package com.zegreatrob.testmints

import com.zegreatrob.testmints.report.MintReporter

class Setup(
    private val contextProvider: (SC) -> C,
    private val reporter: MintReporter,
    private val additionalSetupActions: C.() -> Unit,
    private val wrapper: (TestFunc) -> Unit,
) {
    infix fun  exercise(codeUnderTest: C.() -> R) = Exercise { verifyFunc ->
        { teardownFunc ->
            runTest(codeUnderTest, verifyFunc, teardownFunc)
        }
    }

    private fun  runTest(
        exerciseFunc: ExerciseFunc,
        verifyFunc: VerifyFunc,
        teardownFunc: TeardownFunc,
    ) {
        var verifyFailure: Throwable? = null
        var teardownException: Throwable? = null
        val wrapperException = checkedInvoke(wrapper) { sharedContext ->
            val reportedExerciseFunc = exerciseFunc.makeReporting(reporter)
            val reportedVerifyFunc = verifyFunc.makeReporting(reporter)
            val reportedTeardownFunc = teardownFunc.makeReporting(reporter)

            val context = setup(sharedContext)

            val result = reportedExerciseFunc(context)
            verifyFailure = reportedVerifyFunc(context, result)
            teardownException = reportedTeardownFunc(context, result)
        }

        reporter.teardownFinish()

        reportExceptions(verifyFailure, teardownException, wrapperException)
    }

    private fun reportExceptions(
        verifyFailure: Throwable?,
        teardownException: Throwable?,
        wrapperException: Throwable?,
    ) {
        val problems = exceptionDescriptionMap(teardownException, wrapperException, verifyFailure)

        if (problems.size == 1) {
            throw problems.values.first()
        } else if (problems.isNotEmpty()) {
            throw CompoundMintTestException(problems)
        }
    }

    private fun  TeardownFunc.makeReporting(mintReporter: MintReporter) = { c: C, r: R ->
        mintReporter.teardownStart()
        captureException { this(c, r) }
    }

    private fun setup(sc: SC): C {
        val context = contextProvider(sc)
        additionalSetupActions(context)
        return context
    }
}

private fun  ExerciseFunc.makeReporting(reporter: MintReporter): ExerciseFunc = {
    reporter.exerciseStart(this)
    this@makeReporting(this)
        .also { reporter.exerciseFinish() }
}

private fun  VerifyFunc.makeReporting(mintReporter: MintReporter) = { context: C, result: R ->
    context
        .also { mintReporter.verifyStart(result) }
        .let { captureException { it.(this)(result) } }
        .also { mintReporter.verifyFinish() }
}

private fun exceptionDescriptionMap(
    teardownException: Throwable?,
    templateTeardownException: Throwable?,
    failure: Throwable?,
) =
    mapOf(
        "Failure" to failure,
        "Teardown exception" to teardownException,
        "Template teardown exception" to templateTeardownException,
    )
        .mapNotNull { (descriptor, exception) -> exception?.let { descriptor to exception } }
        .toMap()

private fun  checkedInvoke(wrapper: (TestFunc) -> Unit, test: TestFunc) = captureException {
    var testWasInvoked = false
    wrapper.invoke {
        testWasInvoked = true
        test(it)
    }
    if (!testWasInvoked) throw Exception("Incomplete test template: the wrapper function never called the test function")
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy