introduce ComponentFactoryContainer and AtriumErrorAdjusters in core

In order that one is able to exchange a component which is part of
another component, we need to following to change:
- introduce a ComponentFactoryContainer which stores all factories
  and provide a way to re-define factories
- add val components to AssertionContainer which returns the specified
  ComponentFactoryContainer
- remove withReporter from RootExpectBuilder.OptionsChooser and instead
  add a more generic withComponent (and withChainedComponent)
- use AtriumErrorAdjustor not from Reporter (deprecated the property)
  but retrieve it from the ComponentFactoryContainer of the
  corresponding AssertionContainer

moreover:
- introduce @ExperimentalComponentFactoryContainer
- deprecated the AtriumErrorAdjuster creating functions in CoreFactory
  - replace usages accordingly
This commit is contained in:
Robert Stoll
2021-02-08 22:50:43 +01:00
parent 7867962412
commit 241d41a825
78 changed files with 1129 additions and 133 deletions

View File

@@ -123,6 +123,6 @@ fun <T, R> FeatureExpect<T, R>.withOptions(configuration: FeatureExpectOptionsCh
*/
@ExperimentalWithOptions
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
fun <T, R> FeatureExpect<T, R>.withOptions(options: FeatureExpectOptions<R>): Expect<R> =
FeatureExpect(this, options)

View File

@@ -118,6 +118,6 @@ infix fun <T, R> FeatureExpect<T, R>.withOptions(configuration: FeatureExpectOpt
*/
@ExperimentalWithOptions
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
infix fun <T, R> FeatureExpect<T, R>.withOptions(options: FeatureExpectOptions<R>): Expect<R> =
FeatureExpect(this, options)

View File

@@ -316,6 +316,10 @@ interface CoreFactoryCommon {
*
* @return The newly created adjuster.
*/
@Deprecated(
"Use NoOpAtriumErrorAdjuster instead; will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.NoOpAtriumErrorAdjuster")
)
fun newNoOpAtriumErrorAdjuster(): AtriumErrorAdjuster
/**
@@ -323,6 +327,9 @@ interface CoreFactoryCommon {
*
* @return The newly created adjuster.
*/
@Deprecated(
"Retrieve a RemoveRunnerAtriumErrorAdjuster from a ComponentContainer via getComponent; will be removed with 0.17.0"
)
fun newRemoveRunnerAtriumErrorAdjuster(): AtriumErrorAdjuster
/**
@@ -330,6 +337,9 @@ interface CoreFactoryCommon {
*
* @return The newly created adjuster.
*/
@Deprecated(
"Retrieve a RemoveAtriumFromAtriumErrorAdjuster from a ComponentContainer via getComponent; will be removed with 0.17.0"
)
fun newRemoveAtriumFromAtriumErrorAdjuster(): AtriumErrorAdjuster
/**
@@ -342,6 +352,13 @@ interface CoreFactoryCommon {
*
* @return The newly created adjuster.
*/
@Deprecated(
"Use MultiAtriumErrorAdjuster instead; will be removed with 0.17.0",
ReplaceWith(
"MultiAtriumErrorAdjuster(firstAdjuster, secondAdjuster, otherAdjusters)",
"ch.tutteli.atrium.reporting.erroradjusters.MultiAtriumErrorAdjuster"
)
)
fun newMultiAtriumErrorAdjuster(
firstAdjuster: AtriumErrorAdjuster,
secondAdjuster: AtriumErrorAdjuster,

View File

@@ -17,6 +17,7 @@ import kotlin.reflect.KClass
*
* @param T The type of the subject of `this` expectation.
*/
//TODO 0.16.0 introduce ProofContainer
interface AssertionContainer<T> : @kotlin.Suppress("DEPRECATION") SubjectProvider<T> {
/**
* Either [Some] wrapping the subject of an [Assertion] or [None] in case a previous subject transformation
@@ -27,9 +28,16 @@ interface AssertionContainer<T> : @kotlin.Suppress("DEPRECATION") SubjectProvide
/**
* Do not use yet, this is experimental
*/
//TODO 0.16.0 replace by components
@ExperimentalNewExpectTypes
fun <I : Any> getImpl(kClass: KClass<I>, defaultFactory: () -> I): I
/**
* Do not use yet, this is experimental
*/
@ExperimentalComponentFactoryContainer
val components: ComponentFactoryContainer
// /**
// * Appends the given [assertion] to this container and returns an [Expect] which includes it.
// *

View File

@@ -4,6 +4,7 @@ import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.Option
import ch.tutteli.atrium.creating.impl.CollectingExpectImpl
import ch.tutteli.atrium.creating.impl.DefaultComponentFactoryContainer
/**
* Represents a container for [Assertion] which is intended to serve as receiver object for lambdas which create
@@ -23,9 +24,21 @@ interface CollectingExpect<T> : Expect<T> {
override fun addAssertionsCreatedBy(assertionCreator: Expect<T>.() -> Unit): CollectingExpect<T>
companion object {
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@Suppress(
"DEPRECATION",
"DeprecatedCallableAddReplaceWith" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */
)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
@Deprecated("Use the overload which expects a ComponentFactoryContainer; will be removed with 0.17.0")
operator fun <T> invoke(maybeSubject: Option<T>): CollectingExpect<T> =
CollectingExpectImpl(maybeSubject)
CollectingExpectImpl(maybeSubject, DefaultComponentFactoryContainer)
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
operator fun <T> invoke(
maybeSubject: Option<T>,
componentFactoryContainer: ComponentFactoryContainer
): CollectingExpect<T> =
CollectingExpectImpl(maybeSubject, componentFactoryContainer)
}
}

View File

@@ -0,0 +1,105 @@
package ch.tutteli.atrium.creating
import ch.tutteli.atrium.core.polyfills.cast
import ch.tutteli.atrium.core.polyfills.fullName
import ch.tutteli.atrium.creating.impl.ComponentFactoryContainerImpl
import kotlin.reflect.KClass
import ch.tutteli.atrium.reporting.Reporter
@Suppress("DEPRECATION" /* RequiresOptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@Experimental
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.LOCAL_VARIABLE)
annotation class ExperimentalComponentFactoryContainer
@ExperimentalComponentFactoryContainer
interface ComponentFactoryContainer {
/**
* Returns the component of type [I] using a corresponding factory or `null` in case no factory was found
* which is able to build a component of the given type.
*
* Delegates to [getFactoryOrNull] per default and applies a cast to the result (if not null)
* to the specified [kClass].
*
* @throws ClassCastException in case
*/
fun <I : Any> buildOrNull(kClass: KClass<I>): I? =
getFactoryOrNull(kClass)?.let { factory -> safeBuild(factory, kClass) }
/**
* Returns a chain of components of type [I] using a corresponding factory or `null` in case no factory was found
* which is able to build a chain of components of the given type.
*
* Delegates to [getFactoryForChainedOrNull] per default and applies a cast to the result (if not null)
* to the specified [kClass].
*/
fun <I : Any> buildChainedOrNull(kClass: KClass<I>): Sequence<I>? =
getFactoryForChainedOrNull(kClass)?.map { factory -> safeBuild(factory, kClass) }
private fun <I : Any> safeBuild(factory: (ComponentFactoryContainer) -> Any, kClass: KClass<I>): I {
val component = factory(this)
return kClass.cast(component)
}
/**
* Returns a factory which is able to build a component for the given [kClass]
* or `null` in case no factory was found which is able to build a component of the given type.
*/
fun getFactoryOrNull(kClass: KClass<*>): ((ComponentFactoryContainer) -> Any)?
/**
* Returns a factory which is able to build a chain of components of the specified [kClass]
* or `null` in case no factory was found which is able to build a chain of components of the given type.
*/
fun getFactoryForChainedOrNull(kClass: KClass<*>): (Sequence<(ComponentFactoryContainer) -> Any>)?
/**
* Merges the given [componentFactoryContainer] (if not `null`) with `this` [ComponentFactoryContainer]
* creating a new [ComponentFactoryContainer] where defined dependencies in [componentFactoryContainer]
* will have precedence over dependencies defined in this instance.
*
* For instance, this object has defined a [Reporter] and
* the given [componentFactoryContainer] as well, then the resulting [ComponentFactoryContainer] will return the [Reporter]
* of [componentFactoryContainer] when asked for it.
*/
fun merge(componentFactoryContainer: ComponentFactoryContainer?): ComponentFactoryContainer
companion object {
// should create an immutable ComponentContainer in one go
fun createIfNotEmpty(
components: Map<out KClass<*>, (ComponentFactoryContainer) -> Any>,
chainedComponents: Map<out KClass<*>, Sequence<(ComponentFactoryContainer) -> Any>>
): ComponentFactoryContainer? =
if (components.isEmpty() && chainedComponents.isEmpty()) null
else ComponentFactoryContainerImpl(
if (components.isEmpty()) emptyMap() else HashMap(components),
if (chainedComponents.isEmpty()) emptyMap() else HashMap(chainedComponents)
)
}
}
/**
* Returns the component of type [I] using a corresponding factory or throws an [IllegalStateException]
* in case no factory was found which is able to build a component of the given type.
*
* @throws IllegalStateException in case [ComponentFactoryContainer.buildOrNull] returns `null`
* because not suitable factory was found.
*/
@ExperimentalComponentFactoryContainer
inline fun <reified I : Any> ComponentFactoryContainer.build(): I = buildOrNull(I::class)
?: throw IllegalStateException("No factory is registered in this ComponentContainer which is able to build a ${I::class.fullName}")
/**
* Returns a chain of components of type [I] using a corresponding factory or throws an [IllegalStateException]
* in case no factory was found which is able to build a chain of components of the given type.
* @throws IllegalStateException in case [ComponentFactoryContainer.buildChainedOrNull] returns `null`
* because no suitable factory was found.
*/
@ExperimentalComponentFactoryContainer
inline fun <reified I : Any> ComponentFactoryContainer.buildChained(): Sequence<I> = buildChainedOrNull(I::class)
?: throw IllegalStateException("No factory is registered in this ComponentContainer which is able to build a chain of ${I::class.fullName}")

View File

@@ -11,7 +11,7 @@ import ch.tutteli.atrium.creating.impl.DelegatingExpectImpl
interface DelegatingExpect<T> : Expect<T> {
companion object {
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
operator fun <T> invoke(expect: AssertionContainer<*>, maybeSubject: Option<T>): Expect<T> =
DelegatingExpectImpl(expect, maybeSubject)
}

View File

@@ -14,6 +14,7 @@ interface FeatureExpect<T, R> : Expect<R> {
companion object {
@ExperimentalComponentFactoryContainer
@ExperimentalNewExpectTypes
operator fun <T, R> invoke(
previousExpect: Expect<T>,
@@ -28,6 +29,7 @@ interface FeatureExpect<T, R> : Expect<R> {
* Use this overload if you want to modify the options of a [FeatureExpect].
*/
@ExperimentalNewExpectTypes
@ExperimentalComponentFactoryContainer
operator fun <T, R> invoke(
featureExpect: FeatureExpect<T, R>,
featureExpectOptions: FeatureExpectOptions<R>

View File

@@ -1,7 +1,6 @@
package ch.tutteli.atrium.creating
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.translating.Translatable
/**
@@ -12,19 +11,21 @@ import ch.tutteli.atrium.reporting.translating.Translatable
*
* @property expectationVerb Defines a custom assertion verb if not null.
* @property representationInsteadOfSubject Defines a custom representation based on a present subject if not null.
* @property reporter Defines a custom reporter if not null.
* @property componentFactoryContainer Defines a custom components.
*/
@ExperimentalNewExpectTypes
@Suppress("DEPRECATION" /* RequiresOptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalComponentFactoryContainer::class)
data class RootExpectOptions<T>(
val expectationVerb: Translatable?,
val representationInsteadOfSubject: ((T) -> Any)?,
val reporter: Reporter?
val componentFactoryContainer: ComponentFactoryContainer?
) {
@Deprecated("Use expectationVerb; will be removed latest with 1.0.0 (maybe earlier)")
val assertionVerb = expectationVerb
/**
* Merges the given [options] with this object creating a new [RootExpectOptions]
* Merges the given [options] with `this` [RootExpectOptions] object creating a new [RootExpectOptions]
* where defined properties in [options] will have precedence over properties defined in this instance.
*
* For instance, this object has defined [representationInsteadOfSubject] (meaning it is not `null`) and
@@ -35,6 +36,6 @@ data class RootExpectOptions<T>(
RootExpectOptions(
options.expectationVerb ?: expectationVerb,
options.representationInsteadOfSubject ?: representationInsteadOfSubject,
options.reporter ?: reporter
options.componentFactoryContainer?.let { c -> componentFactoryContainer?.merge(c) ?: c }
)
}

View File

@@ -12,6 +12,7 @@ import ch.tutteli.atrium.reporting.Text
import kotlin.reflect.KClass
@ExperimentalNewExpectTypes
@ExperimentalComponentFactoryContainer
abstract class BaseExpectImpl<T>(
override val maybeSubject: Option<T>
) : ExpectInternal<T> {
@@ -40,7 +41,7 @@ abstract class BaseExpectImpl<T>(
}
override fun addAssertionsCreatedBy(assertionCreator: Expect<T>.() -> Unit): Expect<T> {
val assertions = CollectingExpect(maybeSubject)
val assertions = CollectingExpect(maybeSubject, components)
.addAssertionsCreatedBy(assertionCreator)
.getAssertions()
return addAssertions(assertions)

View File

@@ -7,15 +7,17 @@ import ch.tutteli.atrium.core.Option
import ch.tutteli.atrium.creating.*
@ExperimentalNewExpectTypes
internal class CollectingExpectImpl<T>(maybeSubject: Option<T>) : BaseExpectImpl<T>(maybeSubject), CollectingExpect<T> {
@ExperimentalComponentFactoryContainer
internal class CollectingExpectImpl<T>(
maybeSubject: Option<T>,
override val components: ComponentFactoryContainer
) : BaseExpectImpl<T>(maybeSubject), CollectingExpect<T> {
private val assertions = mutableListOf<Assertion>()
override fun getAssertions(): List<Assertion> = assertions.toList()
override fun addAssertion(assertion: Assertion): Expect<T> {
assertions.add(assertion)
return this
}
override fun addAssertion(assertion: Assertion): Expect<T> =
apply { assertions.add(assertion) }
override fun addAssertionsCreatedBy(assertionCreator: Expect<T>.() -> Unit): CollectingExpect<T> {
// in case we run into performance problems, the code below is certainly not ideal

View File

@@ -0,0 +1,87 @@
package ch.tutteli.atrium.creating.impl
import ch.tutteli.atrium.creating.ComponentFactoryContainer
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.creating.build
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.erroradjusters.MultiAtriumErrorAdjuster
import ch.tutteli.atrium.reporting.erroradjusters.RemoveAtriumFromAtriumError
import ch.tutteli.atrium.reporting.erroradjusters.RemoveRunnerAtriumError
import ch.tutteli.atrium.reporting.erroradjusters.impl.RemoveAtriumFromAtriumErrorImpl
import ch.tutteli.atrium.reporting.erroradjusters.impl.RemoveRunnerAtriumErrorImpl
import ch.tutteli.atrium.reporting.reporter
import kotlin.reflect.KClass
@ExperimentalComponentFactoryContainer
internal abstract class ComponentFactoryContainerImpl : ComponentFactoryContainer {
override fun merge(componentFactoryContainer: ComponentFactoryContainer?): ComponentFactoryContainer =
if (componentFactoryContainer == null) this
else RedefiningComponentFactoryContainer(this, componentFactoryContainer)
@ExperimentalComponentFactoryContainer
private class RootComponentFactoryContainer(
private val components: Map<out KClass<*>, (ComponentFactoryContainer) -> Any>,
private val chainedComponents: Map<out KClass<*>, Sequence<(ComponentFactoryContainer) -> Any>>
) : ComponentFactoryContainerImpl() {
override fun getFactoryOrNull(kClass: KClass<*>): ((ComponentFactoryContainer) -> Any)? =
components[kClass]
override fun getFactoryForChainedOrNull(
kClass: KClass<*>
): (Sequence<(ComponentFactoryContainer) -> Any>)? = chainedComponents[kClass]
}
private class RedefiningComponentFactoryContainer(
private val previousFactoryContainer: ComponentFactoryContainer,
private val redefiningFactoryContainer: ComponentFactoryContainer
) : ComponentFactoryContainerImpl() {
override fun getFactoryOrNull(kClass: KClass<*>): ((ComponentFactoryContainer) -> Any)? =
redefiningFactoryContainer.getFactoryOrNull(kClass) ?: previousFactoryContainer.getFactoryOrNull(kClass)
override fun getFactoryForChainedOrNull(kClass: KClass<*>): Sequence<(ComponentFactoryContainer) -> Any>? {
//TODO 1.0.0 rewrite to sequence { ... }
val previousSequence = previousFactoryContainer.getFactoryForChainedOrNull(kClass)
return redefiningFactoryContainer.getFactoryForChainedOrNull(kClass)?.let { redefinedSequence ->
if (previousSequence != null) redefinedSequence.plus(previousSequence)
else redefinedSequence
} ?: previousSequence
}
}
companion object {
operator fun invoke(
components: Map<out KClass<*>, (ComponentFactoryContainer) -> Any>,
chainedComponents: Map<out KClass<*>, Sequence<(ComponentFactoryContainer) -> Any>>
): ComponentFactoryContainer = RootComponentFactoryContainer(components, chainedComponents)
}
}
// TODO 0.17.0 I guess it would make sense to cache instance here and only re-create them if this component was merged
// with another because most Atrium users won't change the components and it would most likely just be a waste of
// resources to re-create all over and over again. On the other hand they would be very short-lived an most likely
// just be swept away when GC kicks in (not survive the young generation)
@ExperimentalComponentFactoryContainer
//TODO 0.17.0 or 0.18.0 make internal
object DefaultComponentFactoryContainer : ComponentFactoryContainer by ComponentFactoryContainerImpl(
mapOf(
Reporter::class to { _ ->
//TODO 0.16.0 exchange with new Reportable based Reporter
reporter
},
RemoveAtriumFromAtriumError::class to { _ -> RemoveAtriumFromAtriumErrorImpl() },
RemoveRunnerAtriumError::class to { _ -> RemoveRunnerAtriumErrorImpl() },
AtriumErrorAdjuster::class to { c ->
MultiAtriumErrorAdjuster(
c.build<RemoveAtriumFromAtriumError>(),
c.build<RemoveRunnerAtriumError>(),
emptyList()
)
}
),
//TODO 0.16.0 define default chainable types like TextFormatter
emptyMap()
)

View File

@@ -3,16 +3,16 @@ package ch.tutteli.atrium.creating.impl
import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.Option
import ch.tutteli.atrium.creating.AssertionContainer
import ch.tutteli.atrium.creating.DelegatingExpect
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.*
@ExperimentalComponentFactoryContainer
@ExperimentalNewExpectTypes
internal class DelegatingExpectImpl<T>(private val container: AssertionContainer<*>, maybeSubject: Option<T>) :
BaseExpectImpl<T>(maybeSubject), DelegatingExpect<T> {
override fun addAssertion(assertion: Assertion): Expect<T> {
container.addAssertion(assertion)
return this
}
override val components: ComponentFactoryContainer
get() = container.components
override fun addAssertion(assertion: Assertion): Expect<T> =
apply { container.addAssertion(assertion) }
}

View File

@@ -4,12 +4,11 @@ import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.assertions.builders.assertionBuilder
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.Option
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.FeatureExpect
import ch.tutteli.atrium.creating.FeatureExpectOptions
import ch.tutteli.atrium.creating.*
import ch.tutteli.atrium.reporting.translating.Translatable
@ExperimentalNewExpectTypes
@ExperimentalComponentFactoryContainer
internal class FeatureExpectImpl<T, R>(
private val previousExpect: Expect<T>,
maybeSubject: Option<R>,
@@ -53,6 +52,11 @@ internal class FeatureExpectImpl<T, R>(
addAssertions(assertions)
}
override val components: ComponentFactoryContainer
// TODO 0.16.0 the function to turn an Expect into a ProofContainer should be located in core
get() = (previousExpect as AssertionContainer<*>).components
override fun addAssertion(assertion: Assertion): Expect<R> {
assertions.add(assertion)
//Would be nice if we don't have to add it immediately to the previousExpect but only:

View File

@@ -5,33 +5,34 @@ import ch.tutteli.atrium.assertions.builders.assertionBuilder
import ch.tutteli.atrium.assertions.builders.root
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.Option
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.RootExpect
import ch.tutteli.atrium.creating.RootExpectOptions
import ch.tutteli.atrium.creating.*
import ch.tutteli.atrium.reporting.AtriumError
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.translating.Translatable
@ExperimentalNewExpectTypes
@Suppress("DEPRECATION" /* RequiresOptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalComponentFactoryContainer::class)
internal class RootExpectImpl<T>(
maybeSubject: Option<T>,
private val expectationVerb: Translatable,
private val representation: Any?,
private val reporter: Reporter
override val components: ComponentFactoryContainer
) : BaseExpectImpl<T>(maybeSubject), RootExpect<T> {
constructor(
maybeSubject: Option<T>,
assertionVerb: Translatable,
expectationVerb: Translatable,
options: RootExpectOptions<T>?
) : this(
maybeSubject,
options?.expectationVerb ?: assertionVerb,
options?.expectationVerb ?: expectationVerb,
determineRepresentation(
options?.representationInsteadOfSubject,
maybeSubject
),
options?.reporter ?: ch.tutteli.atrium.reporting.reporter
DefaultComponentFactoryContainer.merge(options?.componentFactoryContainer)
)
constructor(previous: RootExpectImpl<T>, options: RootExpectOptions<T>) : this(
@@ -55,8 +56,10 @@ internal class RootExpectImpl<T>(
.build()
val sb = StringBuilder()
reporter.format(assertionGroup, sb)
throw AtriumError.create(sb.toString(), reporter.atriumErrorAdjuster)
components.build<Reporter>().format(assertionGroup, sb)
@Suppress("RemoveExplicitTypeArguments")
throw AtriumError.create(sb.toString(), components.build<AtriumErrorAdjuster>())
}
return this
}

View File

@@ -1,6 +1,6 @@
package ch.tutteli.atrium.reporting
//TODO move to package erroradjusters with 1.0.0
/**
* Responsible to adjust a given [Throwable] (usually an [AtriumError]) for improved error reporting.
*
@@ -10,6 +10,7 @@ package ch.tutteli.atrium.reporting
expect interface AtriumErrorAdjuster : AtriumErrorAdjusterCommon
//TODO move to package erroradjusters with 1.0.0
/**
* Defines the general contract for [AtriumError] adjusters which all platforms have to fulfil.
*/

View File

@@ -11,6 +11,7 @@ interface Reporter {
/**
* The [AtriumErrorAdjuster] which shall be used to modify stack traces in reporting.
*/
@Deprecated("Do no longer use this but build the AtriumErrorAdjuster based on the configured ComponentFactoryContainer of the Expect/ProofContainer, so that you use the latest configured AtriumErrorAdjuster; will be removed with 0.17.0")
val atriumErrorAdjuster: AtriumErrorAdjuster
/**

View File

@@ -0,0 +1,9 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
expect class MultiAtriumErrorAdjuster(
firstAdjuster: AtriumErrorAdjuster,
secondAdjuster: AtriumErrorAdjuster,
otherAdjusters: List<AtriumErrorAdjuster>
) : AtriumErrorAdjuster

View File

@@ -0,0 +1,17 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
/**
* An implementation of an [AtriumErrorAdjuster] which adjusts nothing.
*/
expect object NoOpAtriumErrorAdjuster : AtriumErrorAdjuster
/**
* An implementation of an [AtriumErrorAdjuster] which adjusts nothing and can be used by the platforms to provide the
* actual type of [NoOpAtriumErrorAdjuster].
*/
abstract class NoOpAtriumErrorAdjusterCommon : AtriumErrorAdjuster {
override fun adjustOtherThanStacks(throwable: Throwable) {}
override fun adjust(throwable: Throwable) {}
}

View File

@@ -0,0 +1,13 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.atrium.reporting.AtriumError
import ch.tutteli.atrium.creating.ComponentFactoryContainer
/**
* Responsible to remove the stacktrace of Atrium from an [AtriumError].
*
* It is a marker interface so that one can [ComponentFactoryContainer.buildOrNull] an implementation of
* a [AtriumErrorAdjuster] with the desired behaviour.
*/
interface RemoveAtriumFromAtriumError : AtriumErrorAdjuster

View File

@@ -0,0 +1,13 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.atrium.reporting.AtriumError
import ch.tutteli.atrium.creating.ComponentFactoryContainer
/**
* Responsible to remove the stacktrace of the test runner(s) from an [AtriumError].
*
* It is a marker interface so that one can [ComponentFactoryContainer.buildOrNull] an implementation of
* a [AtriumErrorAdjuster] with the desired behaviour.
*/
interface RemoveRunnerAtriumError : AtriumErrorAdjuster

View File

@@ -0,0 +1,5 @@
package ch.tutteli.atrium.reporting.erroradjusters.impl
import ch.tutteli.atrium.reporting.erroradjusters.RemoveAtriumFromAtriumError
expect class RemoveAtriumFromAtriumErrorImpl() : RemoveAtriumFromAtriumError

View File

@@ -0,0 +1,5 @@
package ch.tutteli.atrium.reporting.erroradjusters.impl
import ch.tutteli.atrium.reporting.erroradjusters.RemoveRunnerAtriumError
expect class RemoveRunnerAtriumErrorImpl() : RemoveRunnerAtriumError

View File

@@ -2,14 +2,14 @@ package ch.tutteli.atrium.reporting
import ch.tutteli.atrium.api.infix.en_GB.*
import ch.tutteli.atrium.api.verbs.internal.expect
import ch.tutteli.atrium.core.coreFactory
import ch.tutteli.atrium.reporting.erroradjusters.NoOpAtriumErrorAdjuster
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
class AtriumErrorSpec : Spek({
describe("creating an AtriumError") {
it("has `null` as cause - regression for #303") {
val e = AtriumError.create("hello world", coreFactory.newNoOpAtriumErrorAdjuster())
val e = AtriumError.create("hello world", NoOpAtriumErrorAdjuster)
expect(e).feature(Throwable::cause).toBe(null)
}
}

View File

@@ -0,0 +1,11 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.atrium.reporting.Reporter
//TODO 0.16.0, remove again, only temporary
internal class DelegatingReporter(
reporter: Reporter,
override val atriumErrorAdjuster: AtriumErrorAdjuster
) : Reporter by reporter

View File

@@ -0,0 +1,19 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.core.polyfills.stackBacktrace
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
abstract class FilterAtriumErrorAdjuster : AtriumErrorAdjuster {
final override fun adjust(throwable: Throwable) {
val filteredStack = adjustStack(throwable.stackBacktrace.asSequence())
val prefix = " at "
throwable.asDynamic().stack = filteredStack.joinToString("\n$prefix", prefix)
throwable.cause?.let { adjust(it) }
}
/**
* Does nothing (no adjustments) - override in subclass if you want a different behaviour.
*/
override fun adjustOtherThanStacks(throwable: Throwable) {}
}

View File

@@ -0,0 +1,19 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
actual class MultiAtriumErrorAdjuster actual constructor(
private val firstAdjuster: AtriumErrorAdjuster,
private val secondAdjuster: AtriumErrorAdjuster,
private val otherAdjusters: List<AtriumErrorAdjuster>
) : FilterAtriumErrorAdjuster(), AtriumErrorAdjuster {
override fun adjustStack(stackTrace: Sequence<String>): Sequence<String> =
firstAdjuster.adjustStack(stackTrace)
.let { secondAdjuster.adjustStack(it) }
.let {
otherAdjusters.fold(it) { filteredStack, adjuster ->
adjuster.adjustStack(filteredStack)
}
}
}

View File

@@ -0,0 +1,7 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
actual object NoOpAtriumErrorAdjuster : NoOpAtriumErrorAdjusterCommon(), AtriumErrorAdjuster {
override fun adjustStack(stackTrace: Sequence<String>): Sequence<String> = stackTrace
}

View File

@@ -0,0 +1,14 @@
package ch.tutteli.atrium.reporting.erroradjusters.impl
import ch.tutteli.atrium.reporting.erroradjusters.FilterAtriumErrorAdjuster
import ch.tutteli.atrium.reporting.erroradjusters.RemoveAtriumFromAtriumError
actual class RemoveAtriumFromAtriumErrorImpl : FilterAtriumErrorAdjuster(),
RemoveAtriumFromAtriumError {
override fun adjustStack(stackTrace: Sequence<String>): Sequence<String> =
stackTrace.filter { !atriumRegex.containsMatchIn(it) }
companion object {
val atriumRegex = Regex("[\\\\|/]atrium-[a-zA-Z-]+.js")
}
}

View File

@@ -0,0 +1,13 @@
package ch.tutteli.atrium.reporting.erroradjusters.impl
import ch.tutteli.atrium.reporting.erroradjusters.FilterAtriumErrorAdjuster
import ch.tutteli.atrium.reporting.erroradjusters.RemoveRunnerAtriumError
actual class RemoveRunnerAtriumErrorImpl : FilterAtriumErrorAdjuster(), RemoveRunnerAtriumError {
override fun adjustStack(stackTrace: Sequence<String>): Sequence<String> =
stackTrace.takeWhile { !runnerRegex.containsMatchIn(it) }
companion object {
val runnerRegex: Regex = Regex("[\\\\|/]node_modules[\\\\|/](mocha|jasmine)[\\\\|/]")
}
}

View File

@@ -0,0 +1,108 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.api.infix.en_GB.*
import ch.tutteli.atrium.api.verbs.internal.AssertionVerb
import ch.tutteli.atrium.api.verbs.internal.expect
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.polyfills.stackBacktrace
import ch.tutteli.atrium.creating.ComponentFactoryContainer
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.creating.build
import ch.tutteli.atrium.logic._logic
import ch.tutteli.atrium.logic.creating.RootExpectBuilder
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.reporter
import kotlin.test.Test
class AdjustStackTest {
@Test
fun noOp_containsMochaAndAtrium() {
expect {
assertNoOp(1).toBe(2)
}.toThrow<AssertionError> {
feature(AssertionError::stackBacktrace) contains entries(
{ contains("mocha") },
{ contains("atrium-core-api-js.js") }
)
}
}
@Test
fun removeRunner_containsAtriumButNotMocha() {
expect {
assertRemoveRunner(1).toBe(2)
}.toThrow<AssertionError> {
it feature of(AssertionError::stackBacktrace) {
it containsNot o entry { it contains "mocha" }
it contains { it contains "atrium-core-api-js.js" }
}
}
}
@ExperimentalComponentFactoryContainer
@Test
fun removeRunner_containsAtriumButNotMochaInCause() {
val adjuster = assertRemoveRunner(1)._logic.components.build<AtriumErrorAdjuster>()
expect(adjuster).isA<RemoveRunnerAtriumError>()
val throwable = IllegalArgumentException("hello", UnsupportedOperationException("world"))
adjuster.adjust(throwable)
expect(throwable.cause!!.stackBacktrace) {
it containsNot o entry { it contains "mocha" }
it contains { it contains "atrium-core-api-js" }
}
}
@Test
fun removeAtrium_containsMochaButNotAtrium() {
expect {
assertRemoveAtrium(1).toBe(2)
}.toThrow<AssertionError> {
it feature of(AssertionError::stackBacktrace) {
it contains { it contains "mocha" }
it containsNot o entry { it contains "atrium-core-api-js.js" }
}
}
}
@ExperimentalComponentFactoryContainer
@Test
fun removeAtrium_containsMochaButNotAtriumInCause() {
val adjuster = assertRemoveAtrium(1)._logic.components.build<AtriumErrorAdjuster>()
expect(adjuster).isA<RemoveAtriumFromAtriumError>()
val throwable = IllegalArgumentException("hello", UnsupportedOperationException("world"))
adjuster.adjust(throwable)
expect(throwable.cause!!.stackBacktrace) {
it contains { it contains "mocha" }
it containsNot o entry { it contains "atrium-core-api-js" }
}
}
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
private fun <T : Any> assertNoOp(subject: T) = createExpect(subject) { _ -> NoOpAtriumErrorAdjuster }
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
private fun <T : Any> assertRemoveRunner(subject: T) =
createExpect(subject) { c -> c.build<RemoveRunnerAtriumError>() }
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
private fun <T : Any> assertRemoveAtrium(subject: T) =
createExpect(subject) { c -> c.build<RemoveAtriumFromAtriumError>() }
@ExperimentalNewExpectTypes
@ExperimentalComponentFactoryContainer
private fun <T : Any> createExpect(subject: T, factory: (ComponentFactoryContainer) -> AtriumErrorAdjuster) =
RootExpectBuilder.forSubject(subject)
.withVerb(AssertionVerb.EXPECT)
.withOptions {
withComponent(AtriumErrorAdjuster::class, factory)
//TODO 0.16.0 should not be necessary once CollectingExpect uses components
withComponent(Reporter::class) { c -> DelegatingReporter(reporter, factory(c))}
}
.build()
}

View File

@@ -0,0 +1,20 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
abstract class FilterAtriumErrorAdjuster : AtriumErrorAdjuster {
final override fun adjust(throwable: Throwable) {
val filteredStackTrace = adjustStackTrace(throwable.stackTrace.asSequence())
val arr = filteredStackTrace.toList().toTypedArray()
throwable.stackTrace = arr
throwable.cause?.let { adjust(it) }
throwable.suppressed.forEach { adjust(it) }
adjustOtherThanStacks(throwable)
}
/**
* Does nothing (no adjustments) - override in subclass if you want a different behaviour.
*/
override fun adjustOtherThanStacks(throwable: Throwable) {}
}

View File

@@ -0,0 +1,25 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.kbox.joinToString
actual class MultiAtriumErrorAdjuster actual constructor(
private val firstAdjuster: AtriumErrorAdjuster,
private val secondAdjuster: AtriumErrorAdjuster,
private val otherAdjusters: List<AtriumErrorAdjuster>
) : FilterAtriumErrorAdjuster(), AtriumErrorAdjuster {
override fun adjustStackTrace(stackTrace: Sequence<StackTraceElement>): Sequence<StackTraceElement> =
firstAdjuster.adjustStackTrace(stackTrace)
.let { secondAdjuster.adjustStackTrace(it) }
.let {
otherAdjusters.fold(it) { filteredStack, adjuster ->
adjuster.adjustStackTrace(filteredStack)
}
}
override fun toString(): String =
"MultiAtriumErrorAdjuster[$firstAdjuster, $secondAdjuster, ${
otherAdjusters.joinToString(", ", " and ") { it, sb -> sb.append(it) }
}"
}

View File

@@ -0,0 +1,12 @@
@file:Suppress(
// TODO remove once https://youtrack.jetbrains.com/issue/KT-35343 is fixed
"JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE"
)
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
actual object NoOpAtriumErrorAdjuster : NoOpAtriumErrorAdjusterCommon(), AtriumErrorAdjuster {
override fun adjustStackTrace(stackTrace: Sequence<StackTraceElement>): Sequence<StackTraceElement> = stackTrace
}

View File

@@ -0,0 +1,15 @@
@file:Suppress(
// TODO remove once https://youtrack.jetbrains.com/issue/KT-35343 is fixed
"JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE"
)
package ch.tutteli.atrium.reporting.erroradjusters.impl
import ch.tutteli.atrium.reporting.erroradjusters.FilterAtriumErrorAdjuster
import ch.tutteli.atrium.reporting.erroradjusters.RemoveAtriumFromAtriumError
actual class RemoveAtriumFromAtriumErrorImpl : FilterAtriumErrorAdjuster(),
RemoveAtriumFromAtriumError {
override fun adjustStackTrace(stackTrace: Sequence<StackTraceElement>): Sequence<StackTraceElement> =
stackTrace.filter { !it.className.startsWith("ch.tutteli.atrium") }
}

View File

@@ -0,0 +1,19 @@
@file:Suppress(
// TODO remove once https://youtrack.jetbrains.com/issue/KT-35343 is fixed
"JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE"
)
package ch.tutteli.atrium.reporting.erroradjusters.impl
import ch.tutteli.atrium.reporting.erroradjusters.FilterAtriumErrorAdjuster
import ch.tutteli.atrium.reporting.erroradjusters.RemoveRunnerAtriumError
actual class RemoveRunnerAtriumErrorImpl : FilterAtriumErrorAdjuster(), RemoveRunnerAtriumError {
override fun adjustStackTrace(stackTrace: Sequence<StackTraceElement>): Sequence<StackTraceElement> =
stackTrace.takeWhile {
!it.className.startsWith("org.junit") &&
!it.className.startsWith("org.jetbrains.spek") &&
!it.className.startsWith("org.spekframework.spek2") &&
!it.className.startsWith("io.kotlintest")
}
}

View File

@@ -11,5 +11,11 @@ module ch.tutteli.atrium.core.api {
exports ch.tutteli.atrium.core.polyfills;
exports ch.tutteli.atrium.creating;
exports ch.tutteli.atrium.reporting;
exports ch.tutteli.atrium.reporting.erroradjusters;
exports ch.tutteli.atrium.reporting.translating;
// TODO 0.17.0 or 0.18.0 remove once DefaultComponentFactoryContainer is internal
exports ch.tutteli.atrium.creating.impl to
ch.tutteli.atrium.logic,
ch.tutteli.atrium.domain.robstoll.lib;
}

View File

@@ -0,0 +1,214 @@
package ch.tutteli.atrium.reporting.erroradjusters
import ch.tutteli.atrium.api.infix.en_GB.*
import ch.tutteli.atrium.api.verbs.internal.AssertionVerb
import ch.tutteli.atrium.api.verbs.internal.expect
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.polyfills.stackBacktrace
import ch.tutteli.atrium.creating.ComponentFactoryContainer
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.creating.build
import ch.tutteli.atrium.logic._logic
import ch.tutteli.atrium.logic.creating.RootExpectBuilder
import ch.tutteli.atrium.logic.utils.expectLambda
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.reporter
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
@ExperimentalNewExpectTypes
@ExperimentalComponentFactoryContainer
class AdjustStackSpec : Spek({
describe("no-op adjuster") {
fun <T : Any> assertNoOp(subject: T) =
createExpect(subject) { _ -> NoOpAtriumErrorAdjuster }
it("contains spek, junit, atrium.creating and atrium.reporting") {
expect {
assertNoOp(1).toBe(2)
}.toThrow<AssertionError> {
feature { f(it::stackBacktrace) } contains entries(
{ it startsWith "org.spekframework.spek2" },
{ it startsWith "ch.tutteli.atrium.creating" },
{ it startsWith "ch.tutteli.atrium.reporting" }
)
}
}
}
fun mapStartsWith(list: List<String>): Pair<Expect<String>.() -> Unit, Array<out Expect<String>.() -> Unit>> {
val asserts = list.map { c -> expectLambda<String> { startsWith(c) } }
return asserts.first() to asserts.drop(1).toTypedArray()
}
mapOf<String, Triple<(ComponentFactoryContainer) -> AtriumErrorAdjuster, List<String>, List<String>>>(
"remove test runner adjuster" to Triple(
{ c -> c.build<RemoveRunnerAtriumError>() },
listOf("org.spekframework.spek2", "kotlin.coroutines", "kotlinx.coroutines"),
listOf("ch.tutteli.atrium")
),
"remove atrium adjuster" to Triple(
{ c -> c.build<RemoveAtriumFromAtriumError>() },
listOf("ch.tutteli.atrium"),
listOf("org.spekframework.spek2")
)
).forEach { (description, triple) ->
val (factory, containsNot, contains) = triple
val (containsNotFirst, containsNotRest) = mapStartsWith(containsNot)
val (containsFirst, containsRest) = mapStartsWith(contains)
describe(description) {
it("does not contain $containsNot in stackBacktrace but $contains") {
expect {
createExpect(1, factory).toBe(2)
}.toThrow<AssertionError> {
feature { f(it::stackBacktrace) } and {
it containsNot o the entries(containsNotFirst, *containsNotRest)
it contains entries(containsFirst, *containsRest)
}
}
}
it("does not contain $containsNot in stackBacktrace of cause, but $contains") {
val throwable = IllegalArgumentException("hello", UnsupportedOperationException("world"))
val adjuster = createExpect(1, factory)._logic.components.build<AtriumErrorAdjuster>()
adjuster.adjust(throwable)
expect(throwable.cause!!.stackBacktrace) {
it containsNot o the entries(containsNotFirst, *containsNotRest)
it contains entries(containsFirst, *containsRest)
}
}
it("does not contain $containsNot in stackBacktrace of cause of cause, but $contains") {
val throwable = IllegalArgumentException(
"hello",
UnsupportedOperationException("world", IllegalStateException("and good night"))
)
val adjuster = createExpect(1, factory)._logic.components.build<AtriumErrorAdjuster>()
adjuster.adjust(throwable)
expect(throwable.cause!!.cause!!.stackBacktrace) {
it containsNot o the entries(containsNotFirst, *containsNotRest)
it contains entries(containsFirst, *containsRest)
}
}
it("does not contain $containsNot in stackBacktrace of suppressed exception, but $contains") {
val throwable1 = IllegalArgumentException("hello", UnsupportedOperationException("world"))
val throwable2 = IllegalArgumentException("hello", UnsupportedOperationException("world"))
val throwable = IllegalStateException("with suppressed")
throwable.addSuppressed(throwable1)
throwable.addSuppressed(throwable2)
val adjuster = createExpect(1, factory)._logic.components.build<AtriumErrorAdjuster>()
adjuster.adjust(throwable)
expect(throwable.suppressed) asList o all {
feature { f(it::stackBacktrace) } and {
it containsNot o the entries(containsNotFirst, *containsNotRest)
it contains entries(containsFirst, *containsRest)
}
}
}
it("does not contain $containsNot in stackBacktrace of cause of suppressed exception, but $contains") {
val throwable1 = IllegalArgumentException("hello", UnsupportedOperationException("world"))
val throwable2 = IllegalArgumentException("hello", UnsupportedOperationException("world"))
val throwable = IllegalStateException("with suppressed")
throwable.addSuppressed(throwable1)
throwable.addSuppressed(throwable2)
val adjuster = createExpect(1, factory)._logic.components.build<AtriumErrorAdjuster>()
adjuster.adjust(throwable)
expect(throwable.suppressed) asList o all {
cause<UnsupportedOperationException> {
feature { f(it::stackBacktrace) } and {
it containsNot o the entries(containsNotFirst, *containsNotRest)
it contains entries(containsFirst, *containsRest)
}
}
}
}
}
}
mapOf<String, (ComponentFactoryContainer) -> AtriumErrorAdjuster>(
"combine remove runner adjuster and remove atrium adjuster" to
{ c ->
MultiAtriumErrorAdjuster(
c.build<RemoveRunnerAtriumError>(),
c.build<RemoveAtriumFromAtriumError>(),
listOf()
)
},
"combine remove atrium adjuster and remove runner adjuster" to
{ c ->
MultiAtriumErrorAdjuster(
c.build<RemoveAtriumFromAtriumError>(),
c.build<RemoveRunnerAtriumError>(),
listOf()
)
},
"combine noop adjust, remove atrium adjuster and remove runner adjuster" to
{ c ->
MultiAtriumErrorAdjuster(
NoOpAtriumErrorAdjuster,
c.build<RemoveAtriumFromAtriumError>(),
listOf(c.build<RemoveRunnerAtriumError>())
)
},
"combine remove atrium adjuster, remove runner adjuster and noop adjuster several times" to
{ c ->
MultiAtriumErrorAdjuster(
c.build<RemoveAtriumFromAtriumError>(),
c.build<RemoveRunnerAtriumError>(),
listOf(
NoOpAtriumErrorAdjuster,
c.build<RemoveRunnerAtriumError>(),
NoOpAtriumErrorAdjuster
)
)
}
).forEach { (description, factory) ->
describe(description) {
it("stackBacktrace is empty as we filter out everything") {
expect {
createExpect(1, factory).toBe(2)
}.toThrow<AssertionError> {
it feature { f(it::stackBacktrace) } toBe empty
}
}
it("stackBacktrace of cause is empty as we filter out everything") {
val throwable = IllegalArgumentException("hello", UnsupportedOperationException("world"))
val adjuster = createExpect(1, factory)._logic.components.build<AtriumErrorAdjuster>()
adjuster.adjust(throwable)
expect(throwable.cause!!.stackBacktrace) toBe empty
}
it("stackBacktrace of suppressed is empty as we filter out everything") {
val throwable1 = IllegalArgumentException("hello", UnsupportedOperationException("world"))
val throwable2 = IllegalArgumentException("hello", UnsupportedOperationException("world"))
val throwable = IllegalStateException("with suppressed")
throwable.addSuppressed(throwable1)
throwable.addSuppressed(throwable2)
val adjuster = createExpect(1, factory)._logic.components.build<AtriumErrorAdjuster>()
adjuster.adjust(throwable)
expect(throwable.suppressed) asList o all {
it feature { f(it::stackBacktrace) } toBe empty
}
}
}
}
})
@ExperimentalNewExpectTypes
@ExperimentalComponentFactoryContainer
private fun <T : Any> createExpect(subject: T, factory: (ComponentFactoryContainer) -> AtriumErrorAdjuster) =
RootExpectBuilder.forSubject(subject)
.withVerb(AssertionVerb.EXPECT)
.withOptions {
withComponent(AtriumErrorAdjuster::class, factory)
//TODO 0.16.0 should not be necessary once CollectingExpect also uses ComponentFactoryContainer
withComponent(Reporter::class) { c -> DelegatingReporter(reporter, factory(c))}
}
.build()

View File

@@ -1,7 +1,14 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use MultiAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.MultiAtriumErrorAdjuster")
)
expect class MultiAtriumErrorAdjusterImpl(
firstAdjuster: AtriumErrorAdjuster,
secondAdjuster: AtriumErrorAdjuster,

View File

@@ -1,3 +1,6 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@@ -5,12 +8,20 @@ import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
/**
* An implementation of an [AtriumErrorAdjuster] which adjust nothings.
*/
@Deprecated(
"Use NoOpAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.NoOpAtriumErrorAdjuster")
)
expect object NoOpAtriumErrorAdjuster : AtriumErrorAdjuster
/**
* An implementation of an [AtriumErrorAdjuster] which adjust nothings and can be used by the platforms to provide the
* actual type of [NoOpAtriumErrorAdjuster].
*/
@Deprecated(
"Use NoOpAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.NoOpAtriumErrorAdjusterCommon")
)
abstract class NoOpAtriumErrorAdjusterCommon : AtriumErrorAdjuster {
override fun adjustOtherThanStacks(throwable: Throwable) {}
override fun adjust(throwable: Throwable) {}

View File

@@ -1,3 +1,6 @@
//TODO remove file with 0.17.0
@file:Suppress("OverridingDeprecatedMember")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.assertions.Assertion
@@ -15,6 +18,7 @@ import ch.tutteli.atrium.reporting.Reporter
* @param assertionFormatterFacade The formatter used to format [Assertion]s.
* @param atriumErrorAdjuster The adjuster which should be used to adjust the resulting [AtriumError].
*/
//TODO deprecate with 0.16.0
class OnlyFailureReporter(
private val assertionFormatterFacade: AssertionFormatterFacade,
override val atriumErrorAdjuster: AtriumErrorAdjuster

View File

@@ -1,5 +1,12 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use RemoveAtriumFromAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.impl.RemoveAtriumFromAtriumErrorAdjuster")
)
expect class RemoveAtriumFromAtriumErrorAdjuster() : AtriumErrorAdjuster

View File

@@ -1,5 +1,12 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use RemoveRunnerAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.impl.RemoveRunnerAtriumErrorAdjuster")
)
expect class RemoveRunnerAtriumErrorAdjuster() : AtriumErrorAdjuster

View File

@@ -1,8 +1,15 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.core.polyfills.stackBacktrace
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use FilterAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.FilterAtriumErrorAdjuster")
)
abstract class FilterAtriumErrorAdjuster : AtriumErrorAdjuster {
final override fun adjust(throwable: Throwable) {

View File

@@ -1,7 +1,14 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use MultiAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.MultiAtriumErrorAdjuster")
)
actual class MultiAtriumErrorAdjusterImpl actual constructor(
private val firstAdjuster: AtriumErrorAdjuster,
private val secondAdjuster: AtriumErrorAdjuster,

View File

@@ -1,7 +1,14 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use NoOpAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.NoOpAtriumErrorAdjuster")
)
actual object NoOpAtriumErrorAdjuster : NoOpAtriumErrorAdjusterCommon(), AtriumErrorAdjuster {
override fun adjustStack(stackTrace: Sequence<String>): Sequence<String> = stackTrace
}

View File

@@ -1,7 +1,14 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use RemoveAtriumFromAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.impl.RemoveAtriumFromAtriumErrorAdjuster")
)
actual class RemoveAtriumFromAtriumErrorAdjuster : FilterAtriumErrorAdjuster(), AtriumErrorAdjuster {
override fun adjustStack(stackTrace: Sequence<String>): Sequence<String> = stackTrace.filter {
!atriumRegex.containsMatchIn(it)

View File

@@ -1,7 +1,14 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use RemoveRunnerAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.impl.RemoveRunnerAtriumErrorAdjuster")
)
actual class RemoveRunnerAtriumErrorAdjuster : FilterAtriumErrorAdjuster(), AtriumErrorAdjuster {
override fun adjustStack(stackTrace: Sequence<String>): Sequence<String> = stackTrace.takeWhile {
!runnerRegex.containsMatchIn(it)

View File

@@ -1,3 +1,6 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.api.fluent.en_GB.*
@@ -6,9 +9,11 @@ import ch.tutteli.atrium.api.verbs.internal.expect
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.coreFactory
import ch.tutteli.atrium.core.polyfills.stackBacktrace
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.logic.creating.RootExpectBuilder
import ch.tutteli.atrium.logic.creating.RootExpectOptions
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.reporter
import kotlin.test.Test
@@ -81,10 +86,13 @@ class AdjustStackTest {
)
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
private fun <T : Any> createExpect(subject: T, adjuster: AtriumErrorAdjuster) =
RootExpectBuilder.forSubject(subject)
.withVerb(AssertionVerb.EXPECT)
.withOptions(RootExpectOptions(reporter = DelegatingReporter(reporter, adjuster)))
.withOptions {
withComponent(AtriumErrorAdjuster::class) { _ -> adjuster }
withComponent(Reporter::class) { _ -> DelegatingReporter(reporter, adjuster) }
}
.build()
}

View File

@@ -1,7 +1,14 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use FilterAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.FilterAtriumErrorAdjuster")
)
abstract class FilterAtriumErrorAdjuster : AtriumErrorAdjuster {
final override fun adjust(throwable: Throwable) {

View File

@@ -1,7 +1,14 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use MultiAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.MultiAtriumErrorAdjuster")
)
actual class MultiAtriumErrorAdjusterImpl actual constructor(
private val firstAdjuster: AtriumErrorAdjuster,
private val secondAdjuster: AtriumErrorAdjuster,

View File

@@ -1,4 +1,5 @@
@file:Suppress(
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION",
// TODO remove once https://youtrack.jetbrains.com/issue/KT-35343 is fixed
"JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE"
)
@@ -7,6 +8,10 @@ package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use NoOpAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.NoOpAtriumErrorAdjuster")
)
actual object NoOpAtriumErrorAdjuster : NoOpAtriumErrorAdjusterCommon(), AtriumErrorAdjuster {
override fun adjustStackTrace(stackTrace: Sequence<StackTraceElement>): Sequence<StackTraceElement> = stackTrace
}

View File

@@ -1,7 +1,14 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use RemoveAtriumFromAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.impl.RemoveAtriumFromAtriumErrorAdjuster")
)
actual class RemoveAtriumFromAtriumErrorAdjuster : FilterAtriumErrorAdjuster(), AtriumErrorAdjuster {
override fun adjustStackTrace(stackTrace: Sequence<StackTraceElement>): Sequence<StackTraceElement> =

View File

@@ -1,7 +1,14 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
@Deprecated(
"Use RemoveRunnerAtriumErrorAdjuster from atrium-core, will be removed with 0.17.0",
ReplaceWith("ch.tutteli.atrium.reporting.erroradjusters.impl.RemoveRunnerAtriumErrorAdjuster")
)
actual class RemoveRunnerAtriumErrorAdjuster : FilterAtriumErrorAdjuster(), AtriumErrorAdjuster {
override fun adjustStackTrace(stackTrace: Sequence<StackTraceElement>): Sequence<StackTraceElement> =

View File

@@ -1,3 +1,6 @@
//TODO remove with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll.lib.reporting
import ch.tutteli.atrium.api.fluent.en_GB.*
@@ -7,10 +10,12 @@ import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.coreFactory
import ch.tutteli.atrium.core.polyfills.stackBacktrace
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.logic.creating.RootExpectBuilder
import ch.tutteli.atrium.logic.creating.RootExpectOptions
import ch.tutteli.atrium.logic.utils.expectLambda
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.reporter
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
@@ -179,9 +184,12 @@ class AdjustStackSpec : Spek({
})
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
private fun <T : Any> createExpect(subject: T, adjuster: AtriumErrorAdjuster) =
RootExpectBuilder.forSubject(subject)
.withVerb(AssertionVerb.EXPECT)
.withOptions(RootExpectOptions(reporter = DelegatingReporter(reporter, adjuster)))
.withOptions {
withComponent(AtriumErrorAdjuster::class) { _ -> adjuster }
withComponent(Reporter::class) { _ -> DelegatingReporter(reporter, adjuster) }
}
.build()

View File

@@ -1,3 +1,6 @@
//TODO remove with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.core.robstoll
import ch.tutteli.atrium.assertions.BulletPointIdentifier

View File

@@ -1,4 +1,6 @@
//TODO remove file with 0.17.0
@file:Suppress(
"DEPRECATION",
// TODO remove once https://youtrack.jetbrains.com/issue/KT-35343 is fixed
"JAVA_MODULE_DOES_NOT_READ_UNNAMED_MODULE"
)

View File

@@ -1,15 +1,13 @@
package ch.tutteli.atrium.logic.creating
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.RootExpect
import ch.tutteli.atrium.creating.RootExpectOptions
import ch.tutteli.atrium.creating.*
import ch.tutteli.atrium.logic.creating.impl.*
import ch.tutteli.atrium.logic.reporting.ReporterBuilder
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.Text
import ch.tutteli.atrium.reporting.translating.Translatable
import ch.tutteli.atrium.reporting.translating.Untranslatable
import kotlin.reflect.KClass
/**
* Defines the contract to create custom expectation verbs, `RootExpect<T>` respectively.
@@ -63,7 +61,6 @@ interface RootExpectBuilder {
*/
val expectationVerb: Translatable
/**
* Allows to define the [RootExpectOptions] via an [OptionsChooser]-lambda which provides convenience functions.
*
@@ -140,12 +137,21 @@ interface RootExpectBuilder {
fun withRepresentation(representationProvider: (T) -> Any)
/**
* Uses the given [reporter] instead of the default [Reporter].
* Expects a factory which creates the component of type [I] which is used instead of the currently specified
* factory.
*/
fun withReporter(reporter: Reporter)
@ExperimentalComponentFactoryContainer
fun <I : Any> withComponent(kClass: KClass<I>, factory: (ComponentFactoryContainer) -> I)
//TODO 0.16.0 maybe we should change the name to prependChainedComponents
// because we also use the once we defined so far?
@ExperimentalComponentFactoryContainer
fun <I : Any> withChainedComponents(kClass: KClass<I>, factories: Sequence<(ComponentFactoryContainer) -> I>)
companion object {
@ExperimentalNewExpectTypes
@Suppress("DEPRECATION" /* RequiresOptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalComponentFactoryContainer::class)
operator fun <T> invoke(configuration: OptionsChooser<T>.() -> Unit): RootExpectOptions<T> =
RootExpectOptionsChooserImpl<T>().apply(configuration).build()
}

View File

@@ -1,9 +1,10 @@
package ch.tutteli.atrium.logic.creating
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.creating.ComponentFactoryContainer
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.creating.RootExpectOptions
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.translating.Translatable
import kotlin.reflect.KClass
/**
* Helper function to create a [RootExpectOptions] via
@@ -14,18 +15,9 @@ fun <T> RootExpectOptions(configuration: RootExpectBuilder.OptionsChooser<T>.()
RootExpectBuilder.OptionsChooser(configuration)
/**
* Helper function to create a [RootExpectOptions] by specifying components via named parameters.
*
* You can use it as follows: `RootExpectOptions(reporter = myReporter)`
* Convenience function which infers the [KClass] usually required for [RootExpectBuilder.OptionsChooser.withComponent].
*/
@ExperimentalNewExpectTypes
@Suppress("FunctionName")
fun <T> RootExpectOptions(
expectationVerb: Translatable? = null,
representationInsteadOfSubject: ((T) -> Any)? = null,
reporter: Reporter? = null
): RootExpectOptions<T> = RootExpectOptions(
expectationVerb,
representationInsteadOfSubject,
reporter
)
@ExperimentalComponentFactoryContainer
inline fun <reified I : Any, T> RootExpectBuilder.OptionsChooser<T>.withComponent(
noinline factory: (ComponentFactoryContainer) -> I
) = withComponent(I::class, factory)

View File

@@ -7,6 +7,8 @@ import ch.tutteli.atrium.core.Option
import ch.tutteli.atrium.core.Some
import ch.tutteli.atrium.creating.Expect
//TODO 0.16.0 I am not sure this is actually needed, couldn't they just be helper methods?
// moreover, we need to create it via ComponentFactoryContainer as the CollectingExpect which is used internally needs to use the components
interface AssertionCollector {
/**

View File

@@ -6,10 +6,12 @@ import ch.tutteli.atrium.assertions.builders.invisibleGroup
import ch.tutteli.atrium.core.Option
import ch.tutteli.atrium.creating.CollectingExpect
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.creating.impl.DefaultComponentFactoryContainer
import ch.tutteli.atrium.logic.creating.collectors.AssertionCollector
class DefaultAssertionCollector : AssertionCollector {
override fun <T> collect(maybeSubject: Option<T>, assertionCreator: Expect<T>.() -> Unit): Assertion {
val collectedAssertions = collectForComposition(maybeSubject, assertionCreator)
return if (collectedAssertions.size > 1) {
@@ -19,10 +21,13 @@ class DefaultAssertionCollector : AssertionCollector {
}
}
@Suppress("DEPRECATION" /* RequiresOptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalComponentFactoryContainer::class)
override fun <T> collectForComposition(
maybeSubject: Option<T>,
assertionCreator: Expect<T>.() -> Unit
): List<Assertion> = CollectingExpect(maybeSubject)
//TODO 0.16.0 don't use DefaultComponentFactoryContainer but the one from the container
): List<Assertion> = CollectingExpect(maybeSubject, DefaultComponentFactoryContainer)
.addAssertionsCreatedBy(assertionCreator)
.getAssertions()
}

View File

@@ -7,4 +7,5 @@ import ch.tutteli.atrium.logic.creating.collectors.impl.DefaultAssertionCollecto
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
val <T> AssertionContainer<T>.assertionCollector: AssertionCollector
//TODO 0.16.0 should be via ComponentFactoryContainer, hence move ProofCollector to core
get() = getImpl(AssertionCollector::class) { DefaultAssertionCollector() }

View File

@@ -1,16 +1,20 @@
package ch.tutteli.atrium.logic.creating.impl
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.creating.ComponentFactoryContainer
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.creating.RootExpectOptions
import ch.tutteli.atrium.logic.creating.RootExpectBuilder
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.translating.Translatable
import kotlin.reflect.KClass
@ExperimentalComponentFactoryContainer
@ExperimentalNewExpectTypes
class RootExpectOptionsChooserImpl<T> : RootExpectBuilder.OptionsChooser<T> {
private var description: Translatable? = null
private var representationInsteadOfSubject: ((T) -> Any)? = null
private var reporter: Reporter? = null
private var components = mutableMapOf<KClass<*>, (ComponentFactoryContainer) -> Any>()
private var chainedComponents = mutableMapOf<KClass<*>, Sequence<(ComponentFactoryContainer) -> Any>>()
override fun withVerb(verb: Translatable) {
this.description = verb
@@ -20,9 +24,16 @@ class RootExpectOptionsChooserImpl<T> : RootExpectBuilder.OptionsChooser<T> {
this.representationInsteadOfSubject = representationProvider
}
override fun withReporter(reporter: Reporter) {
this.reporter = reporter
@ExperimentalComponentFactoryContainer
override fun <I : Any> withComponent(kClass: KClass<I>, factory: (ComponentFactoryContainer) -> I) {
components[kClass] = factory
}
fun build(): RootExpectOptions<T> = RootExpectOptions(description, representationInsteadOfSubject, reporter)
@ExperimentalComponentFactoryContainer
override fun <I : Any> withChainedComponents(kClass: KClass<I>, factories: Sequence<(ComponentFactoryContainer) -> I>) {
chainedComponents[kClass] = factories
}
fun build(): RootExpectOptions<T> =
RootExpectOptions(description, representationInsteadOfSubject, ComponentFactoryContainer.createIfNotEmpty(components, chainedComponents))
}

View File

@@ -24,7 +24,11 @@ class DefaultIterableLikeContainsInAnyOrderAssertions : IterableLikeContainsInAn
expected: List<SC>,
factory: ((T) -> Iterable<E>, S, List<IterableLikeContains.Checker>) -> IterableLikeContains.Creator<T, SC>
): AssertionGroup {
val creator = factory(checkerStepLogic.entryPointStepLogic.converter, checkerStepLogic.entryPointStepLogic.searchBehaviour, checkerStepLogic.checkers)
val creator = factory(
checkerStepLogic.entryPointStepLogic.converter,
checkerStepLogic.entryPointStepLogic.searchBehaviour,
checkerStepLogic.checkers
)
return creator.createAssertionGroup(checkerStepLogic.entryPointStepLogic.container, expected)
}
}

View File

@@ -10,6 +10,8 @@ import ch.tutteli.atrium.core.getOrElse
import ch.tutteli.atrium.creating.AssertionContainer
import ch.tutteli.atrium.creating.CollectingExpect
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.creating.impl.DefaultComponentFactoryContainer
import ch.tutteli.atrium.logic.collectForDifferentSubject
import ch.tutteli.atrium.logic.creating.basic.contains.creators.impl.ContainsAssertionCreator
import ch.tutteli.atrium.logic.creating.iterable.contains.IterableLikeContains
@@ -94,6 +96,8 @@ class InAnyOrderEntriesAssertionCreator<E : Any, T : IterableLike>(
//TODO 0.17.0 check if this is still state of the art to add a hint that no assertion was created
// in the assertionCreator-lambda, maybe it is a special case and needs to be handled like this,
// maybe it would be enough to collect
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalComponentFactoryContainer::class)
private fun addEmptyAssertionCreatorLambdaIfNecessary(
container: AssertionContainer<*>,
assertions: MutableList<Assertion>,
@@ -101,7 +105,8 @@ class InAnyOrderEntriesAssertionCreator<E : Any, T : IterableLike>(
count: Int
) {
if (searchCriterion != null && count == 0) {
val collectingExpect = CollectingExpect<E>(None)
// TODO if it is still state of the art, then use components from defined AssertionContainer
val collectingExpect = CollectingExpect<E>(None, DefaultComponentFactoryContainer)
// not using addAssertionsCreatedBy on purpose so that we don't append a failing assertion
collectingExpect.searchCriterion()
val collectedAssertions = collectingExpect.getAssertions()

View File

@@ -5,10 +5,7 @@ import ch.tutteli.atrium.assertions.builders.assertionBuilder
import ch.tutteli.atrium.assertions.builders.fixedClaimGroup
import ch.tutteli.atrium.core.*
import ch.tutteli.atrium.core.polyfills.fullName
import ch.tutteli.atrium.creating.AssertionContainer
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.FeatureExpect
import ch.tutteli.atrium.creating.FeatureExpectOptions
import ch.tutteli.atrium.creating.*
import ch.tutteli.atrium.logic.creating.collectors.assertionCollector
import ch.tutteli.atrium.logic.creating.collectors.collectAssertions
import ch.tutteli.atrium.logic.creating.transformers.FeatureExtractor
@@ -20,7 +17,7 @@ import ch.tutteli.atrium.translations.DescriptionFunLikeAssertion
class DefaultFeatureExtractor : FeatureExtractor {
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
override fun <T, R> extract(
container: AssertionContainer<T>,
description: Translatable,
@@ -42,7 +39,8 @@ class DefaultFeatureExtractor : FeatureExtractor {
try {
featureExtraction(subject).fold({ Left(None) }, { Right(it) })
} catch (throwable: Throwable) {
//TODO 0.16.0 should be taken from `container`
// container.components.build<AtriumErrorAdjuster>().adjust(throwable)
//TODO 0.16.0 use the above instead of this line as soon as CollectingExpect is also using the ComponentFactoryContainer
reporter.atriumErrorAdjuster.adjust(throwable)
Left(Some(throwable))
}

View File

@@ -4,9 +4,7 @@ import ch.tutteli.atrium.core.Either
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.Left
import ch.tutteli.atrium.core.Right
import ch.tutteli.atrium.creating.AssertionContainer
import ch.tutteli.atrium.creating.FeatureExpect
import ch.tutteli.atrium.creating.FeatureExpectOptions
import ch.tutteli.atrium.creating.*
import ch.tutteli.atrium.logic.Fun0Assertions
import ch.tutteli.atrium.logic.changeSubject
import ch.tutteli.atrium.logic.creating.transformers.FeatureExtractorBuilder
@@ -20,8 +18,6 @@ import kotlin.reflect.KClass
class DefaultFun0Assertions : Fun0Assertions {
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
override fun <TExpected : Throwable> toThrow(
container: AssertionContainer<out () -> Any?>,
expectedType: KClass<TExpected>
@@ -29,9 +25,11 @@ class DefaultFun0Assertions : Fun0Assertions {
// we use manualFeature and not extractFeature since we never want to fail the feature extraction
// because we want to show the planned downCast in the error message
return container.manualFeature(THROWN_EXCEPTION_WHEN_CALLED) {
catchAndAdjustThrowable(this)
catchAndAdjustThrowable(container, this)
.fold({ it }, { /* use null as subject in case no exception occurred */ null })
}.transform().let { previousExpect ->
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
FeatureExpect(
previousExpect,
FeatureExpectOptions(representationInsteadOfFeature = { it ?: NO_EXCEPTION_OCCURRED })
@@ -42,11 +40,17 @@ class DefaultFun0Assertions : Fun0Assertions {
}
}
private inline fun <R> catchAndAdjustThrowable(act: () -> R): Either<Throwable, R> =
private inline fun <R> catchAndAdjustThrowable(
container: AssertionContainer<*>,
act: () -> R
): Either<Throwable, R> =
try {
Right(act())
} catch (throwable: Throwable) {
//TODO should be taken from current expect once it is configured this way
// @Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
// @UseExperimental(ExperimentalComponentFactoryContainer::class)
// container.components.build<AtriumErrorAdjuster>().adjust(throwable)
//TODO 0.16.0 use the above instead of this line as soon as CollectingExpect is also using the ComponentFactoryContainer
reporter.atriumErrorAdjuster.adjust(throwable)
Left(throwable)
}

View File

@@ -2,6 +2,7 @@ package ch.tutteli.atrium.logic.impl
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.creating.AssertionContainer
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.creating.FeatureExpect
import ch.tutteli.atrium.creating.FeatureExpectOptions
import ch.tutteli.atrium.logic.*
@@ -14,7 +15,7 @@ import kotlin.reflect.KClass
class DefaultThrowableAssertions : ThrowableAssertions {
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
override fun <TExpected : Throwable> causeIsA(
container: AssertionContainer<out Throwable>,
expectedType: KClass<TExpected>

View File

@@ -51,7 +51,6 @@ val <T> AssertionContainer<T>.changeSubject: SubjectChangerBuilder.KindStep<T>
val <T> AssertionContainer<T>.extractFeature: FeatureExtractorBuilder.DescriptionStep<T>
get() = FeatureExtractorBuilder(this)
/**
* Use this function if you want to make [Assertion]s about a feature or you perform a type transformation or any
* other action which results in an [Expect] being created for a different subject and

View File

@@ -3,6 +3,7 @@ package ch.tutteli.atrium.logic.kotlin_1_3.impl
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.Option
import ch.tutteli.atrium.creating.AssertionContainer
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.creating.FeatureExpect
import ch.tutteli.atrium.creating.FeatureExpectOptions
import ch.tutteli.atrium.logic.creating.transformers.SubjectChangerBuilder
@@ -28,7 +29,7 @@ class DefaultResultAssertions : ResultAssertions {
.build()
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
override fun <TExpected : Throwable> isFailureOfType(
container: AssertionContainer<out Result<*>>,
expectedType: KClass<TExpected>

View File

@@ -7,9 +7,7 @@ import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.None
import ch.tutteli.atrium.core.Option
import ch.tutteli.atrium.core.Some
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.RootExpect
import ch.tutteli.atrium.creating.RootExpectOptions
import ch.tutteli.atrium.creating.*
import ch.tutteli.atrium.domain.builders.reporting.impl.verb.AssertionVerbStepImpl
import ch.tutteli.atrium.domain.builders.reporting.impl.verb.FinalStepImpl
import ch.tutteli.atrium.domain.builders.reporting.impl.verb.OptionsChooserImpl
@@ -260,8 +258,18 @@ data class ExpectOptions<T>(
)
@ExperimentalNewExpectTypes
@Suppress("DEPRECATION" /* RequiresOptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalComponentFactoryContainer::class)
fun toRootExpectOptions(): RootExpectOptions<T> =
RootExpectOptions(assertionVerb, representationInsteadOfSubject, reporter)
RootExpectOptions(
assertionVerb,
representationInsteadOfSubject,
reporter?.let { r ->
ComponentFactoryContainer.createIfNotEmpty(
mapOf(Reporter::class to { _ -> r }),
emptyMap()
)
})
}
@Suppress("FunctionName")

View File

@@ -1,3 +1,6 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.domain.builders.reporting.impl
import ch.tutteli.atrium.core.coreFactory

View File

@@ -1,3 +1,6 @@
//TODO remove file with 0.17.0
@file:Suppress("DEPRECATION")
package ch.tutteli.atrium.domain.builders.reporting.impl
import ch.tutteli.atrium.core.coreFactory

View File

@@ -4,19 +4,23 @@ import ch.tutteli.atrium.assertions.Assertion
import ch.tutteli.atrium.assertions.AssertionGroup
import ch.tutteli.atrium.assertions.builders.assertionBuilder
import ch.tutteli.atrium.assertions.builders.invisibleGroup
import ch.tutteli.atrium.assertions.builders.withExplanatoryAssertion
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.Option
import ch.tutteli.atrium.creating.CollectingExpect
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.creating.impl.DefaultComponentFactoryContainer
import ch.tutteli.atrium.reporting.Text
import ch.tutteli.atrium.translations.DescriptionBasic
import ch.tutteli.atrium.translations.ErrorMessages
fun <T> _collect(
maybeSubject: Option<T>,
assertionCreator: Expect<T>.() -> Unit
): Assertion {
val collectedAssertions = CollectingExpect(maybeSubject)
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
//TODO 0.16.0 don't use DefaultComponentFactoryContainer but the one from the container
val collectedAssertions = CollectingExpect(maybeSubject, DefaultComponentFactoryContainer)
.addAssertionsCreatedBy(assertionCreator)
.getAssertions()
return if (collectedAssertions.size > 1) {
@@ -29,7 +33,7 @@ fun <T> _collect(
fun <T> _collectForComposition(
maybeSubject: Option<T>,
assertionCreatorOrNull: (Expect<T>.() -> Unit)?
): List<Assertion> = collectAssertions(maybeSubject, assertionCreatorOrNull)
): List<Assertion> = collectAssertions(maybeSubject, assertionCreatorOrNull)
//TODO 0.16.0: better replace by silentToBeNullIfNullGiven?
private fun <T> collectAssertions(
@@ -38,7 +42,10 @@ private fun <T> collectAssertions(
): List<Assertion> {
//TODO 0.16.0: almost same as in _containsKeyWithNullableValueAssertions
return if (assertionCreatorOrNull != null) {
CollectingExpect(maybeSubject)
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
//TODO 0.16.0 don't use DefaultComponentFactoryContainer but the one from the container
CollectingExpect(maybeSubject, DefaultComponentFactoryContainer)
.addAssertionsCreatedBy(assertionCreatorOrNull)
.getAssertions()
} else {

View File

@@ -10,11 +10,13 @@ import ch.tutteli.atrium.assertions.ExplanatoryAssertionGroupType
import ch.tutteli.atrium.assertions.builders.assertionBuilder
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.None
import ch.tutteli.atrium.core.coreFactory
import ch.tutteli.atrium.creating.CollectingExpect
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.logic._logic
import ch.tutteli.atrium.logic.creating.RootExpectBuilder
import ch.tutteli.atrium.logic.creating.RootExpectOptions
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.atrium.reporting.erroradjusters.NoOpAtriumErrorAdjuster
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
@@ -26,7 +28,9 @@ abstract class SubjectLessSpec<T>(
describe("${groupPrefix}assertion function can be used in an ${AssertionGroup::class.simpleName} with an ${ExplanatoryAssertionGroupType::class.simpleName} and report without failure") {
assertionCreator.forEach { (name, createAssertion) ->
it("fun `$name`") {
val assertions = CollectingExpect<T>(None)
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
val assertions = CollectingExpect<T>(None, expect(1)._logic.components)
.addAssertionsCreatedBy(createAssertion)
.getAssertions()
@@ -34,17 +38,12 @@ abstract class SubjectLessSpec<T>(
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
val container = RootExpectBuilder.forSubject(1.0)
.withVerb("custom assertion verb")
.withOptions(
RootExpectOptions(
reporter = coreFactory.newOnlyFailureReporter(
coreFactory.newAssertionFormatterFacade(coreFactory.newAssertionFormatterController()),
coreFactory.newNoOpAtriumErrorAdjuster()
)
)
)
.withOptions {
withComponent(AtriumErrorAdjuster::class) { _ -> NoOpAtriumErrorAdjuster }
}
.build()
val explanatoryGroup = assertionBuilder.explanatoryGroup
@@ -59,7 +58,9 @@ abstract class SubjectLessSpec<T>(
describe("${groupPrefix}assertion function does not hold if there is no subject") {
assertionCreator.forEach { (name, createAssertion) ->
it("fun `$name`") {
val assertions = CollectingExpect<T>(None)
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
val assertions = CollectingExpect<T>(None, expect(1)._logic.components)
.addAssertionsCreatedBy(createAssertion)
.getAssertions()
expect(assertions).all { feature(Assertion::holds).toBe(false) }

View File

@@ -12,6 +12,7 @@ import ch.tutteli.atrium.core.coreFactory
import ch.tutteli.atrium.reporting.AssertionFormatterFacade
import ch.tutteli.atrium.reporting.AtriumErrorAdjuster
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.erroradjusters.NoOpAtriumErrorAdjuster
import ch.tutteli.atrium.reporting.translating.UsingDefaultTranslator
import ch.tutteli.atrium.specs.AssertionVerb
import ch.tutteli.atrium.specs.describeFunTemplate
@@ -37,7 +38,7 @@ abstract class OnlyFailureReporterSpec(
coreFactory.newDetailedObjectFormatter(translator), translator
)
}
val testee = testeeFactory(facade, coreFactory.newNoOpAtriumErrorAdjuster())
val testee = testeeFactory(facade, NoOpAtriumErrorAdjuster)
describeFun(testee::format.name) {
val sb = StringBuilder()
@@ -83,9 +84,7 @@ abstract class OnlyFailureReporterSpec(
val assertionFormatterFacade = mockk<AssertionFormatterFacade>()
every { assertionFormatterFacade.format(any(), any(), any()) } just Runs
val testeeWithMockedFacade = testeeFactory(
assertionFormatterFacade, coreFactory.newNoOpAtriumErrorAdjuster()
)
val testeeWithMockedFacade = testeeFactory(assertionFormatterFacade, NoOpAtriumErrorAdjuster)
it("delegates to ${assertionFormatterFacade::class.simpleName}") {
testeeWithMockedFacade.format(basicAssertion, sb)

View File

@@ -4,11 +4,13 @@ import ch.tutteli.atrium.api.fluent.en_GB.*
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.core.polyfills.fullName
import ch.tutteli.atrium.creating.Expect
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.domain.builders.reporting.ReporterBuilder
import ch.tutteli.atrium.logic._logic
import ch.tutteli.atrium.logic.changeSubject
import ch.tutteli.atrium.logic.creating.RootExpectBuilder
import ch.tutteli.atrium.logic.creating.RootExpectOptions
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.specs.AssertionVerb
import ch.tutteli.atrium.specs.prefixedDescribeTemplate
import ch.tutteli.atrium.specs.toBeDescr
@@ -174,12 +176,15 @@ private fun Suite.testNonNullableSubject(assertionVerb: (Int) -> Expect<Int>) {
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
// does not make sense to test the verbs with the verbs themselves. Thus we create our own assertion verb here
private fun <R> assert(act: () -> R): Expect<() -> R> =
RootExpectBuilder.forSubject(act)
.withVerb(AssertionVerb.EXPECT_THROWN)
.withOptions(RootExpectOptions(reporter = AtriumReporterSupplier.REPORTER))
.withOptions {
//TODO we only use the default components here, I guess once we get rid of reporter we can get rid of this as well
withComponent(Reporter::class) { _ -> AtriumReporterSupplier.REPORTER }
}
.build()
private object AtriumReporterSupplier {

View File

@@ -3,8 +3,8 @@ package ch.tutteli.atrium.specs.reporting.translating
import ch.tutteli.atrium.api.fluent.en_GB.*
import ch.tutteli.atrium.api.verbs.internal.expect
import ch.tutteli.atrium.core.ExperimentalNewExpectTypes
import ch.tutteli.atrium.creating.ExperimentalComponentFactoryContainer
import ch.tutteli.atrium.logic.creating.RootExpectBuilder
import ch.tutteli.atrium.logic.creating.RootExpectOptions
import ch.tutteli.atrium.reporting.Reporter
import ch.tutteli.atrium.reporting.translating.Locale
import ch.tutteli.atrium.reporting.translating.StringBasedTranslatable
@@ -104,24 +104,28 @@ abstract class TranslatorIntSpec(
fun prefixedDescribe(description: String, body: Suite.() -> Unit) =
prefixedDescribeTemplate(describePrefix, description, body)
val reporterDeChFallbackFr = reporterFactory(Locale("de", "CH"), arrayOf(Locale("fr")))
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
fun <T : Any> assertWithDeCh_Fr(subject: T) =
RootExpectBuilder.forSubject(subject)
.withVerb(AssertionVerb.ASSERT)
.withOptions(RootExpectOptions(reporter = reporterDeChFallbackFr))
.withOptions {
withComponent(Reporter::class) { _ ->
reporterFactory(Locale("de", "CH"), arrayOf(Locale("fr")))
}
}
.build()
val reporterDeChFallbackFrIt = reporterFactory(Locale("de", "CH"), arrayOf(Locale("fr", "CH"), Locale("it", "CH")))
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
fun <T : Any> assertWithDeCh_Fr_It(subject: T) =
RootExpectBuilder.forSubject(subject)
.withVerb(AssertionVerb.ASSERT)
.withOptions(RootExpectOptions(reporter = reporterDeChFallbackFrIt))
.withOptions {
withComponent(Reporter::class) { _ ->
reporterFactory(Locale("de", "CH"), arrayOf(Locale("fr", "CH"), Locale("it", "CH")))
}
}
.build()
val descriptionAnyAssertion = DescriptionAnyAssertion::class.simpleName
@@ -277,14 +281,17 @@ abstract class TranslatorIntSpec(
val zhWithScript = "zh_$script"
countries.forEach { country ->
val locale = Locale("zh", country)
val reporter = reporterFactory(locale, arrayOf())
@Suppress("DEPRECATION" /* OptIn is only available since 1.3.70 which we cannot use if we want to support 1.2 */)
@UseExperimental(ExperimentalNewExpectTypes::class)
@UseExperimental(ExperimentalNewExpectTypes::class, ExperimentalComponentFactoryContainer::class)
val assert = RootExpectBuilder.forSubject(1)
.withVerb(AssertionVerb.ASSERT)
.withOptions(RootExpectOptions(reporter = reporter))
.withOptions {
withComponent(Reporter::class) { _ ->
reporterFactory(locale, arrayOf())
}
}
.build()
prefixedDescribe("primary locale is 'zh_$country' and no fallback defined") {