Minor model refactoring and more base implementations.

This commit is contained in:
Pat Garrity 2025-07-31 07:08:00 -05:00
parent 2b905d3fb2
commit c2a155ceab
Signed by: pfm
GPG key ID: 5CA5D21BAB7F3A76
6 changed files with 131 additions and 22 deletions

View file

@ -16,8 +16,6 @@ import scala.concurrent.duration.FiniteDuration
* The number of tests which passed. * The number of tests which passed.
* @param failed * @param failed
* The number of tests which failed. * The number of tests which failed.
* @param testExecutions
* List of test results.
*/ */
final class GroupResult( final class GroupResult(
val name: TestGroupDefinition.Name, val name: TestGroupDefinition.Name,
@ -25,6 +23,5 @@ final class GroupResult(
val duration: FiniteDuration, val duration: FiniteDuration,
val seen: Long, val seen: Long,
val passed: Long, val passed: Long,
val failed: Long, val failed: Long
val testExecutions: List[TestExecution]
) )

View file

@ -0,0 +1,30 @@
package gs.test.v0.reporting
import cats.Applicative
import gs.test.v0.api.GroupResult
import gs.test.v0.api.SuiteExecution
import gs.test.v0.api.TestExecution
/** No-op implementation of [[Reporter]].
*/
final class NoopReporter[F[_]: Applicative] extends Reporter[F]:
/** @inheritDocs
*/
override def beginReporting(): F[Unit] = Applicative[F].unit
/** @inheritDocs
*/
override def reportGroup(
groupResult: GroupResult,
testExecutions: List[TestExecution]
): F[Unit] = Applicative[F].unit
/** @inheritDocs
*/
override def reportSuite(suiteExecution: SuiteExecution): F[Unit] =
Applicative[F].unit
/** @inheritDocs
*/
override def endReporting(): F[Unit] = Applicative[F].unit

View file

@ -0,0 +1,19 @@
package gs.test.v0.reporting
import gs.test.v0.api.GroupResult
import gs.test.v0.api.SuiteExecution
import gs.test.v0.api.TestExecution
final class NoopResultFormatter extends ResultFormatter:
/** @inheritDocs
*/
override def formatGroupResult(groupResult: GroupResult): String = ""
/** @inheritDocs
*/
override def formatTestExecution(testExecution: TestExecution): String = ""
/** @inheritDocs
*/
override def formatSuiteExecution(suiteExecution: SuiteExecution): String = ""

View file

@ -1,7 +1,9 @@
package gs.test.v0.reporting package gs.test.v0.reporting
import cats.Applicative
import gs.test.v0.api.GroupResult import gs.test.v0.api.GroupResult
import gs.test.v0.api.SuiteExecution import gs.test.v0.api.SuiteExecution
import gs.test.v0.api.TestExecution
/** Interface for reporters - implementations that report on test results. /** Interface for reporters - implementations that report on test results.
* *
@ -10,8 +12,10 @@ import gs.test.v0.api.SuiteExecution
* *
* ## Order of Operations * ## Order of Operations
* *
* 1. `beginReporting()` 2. `reportGroup` for each group executed 3. * - `beginReporting()`
* `reportSuite` 4. `endReporting()` * - `reportGroup` for each group executed
* - `reportSuite`
* - `endReporting()`
*/ */
trait Reporter[F[_]]: trait Reporter[F[_]]:
/** Hook for the beginning of the reporting lifecycle. This allows /** Hook for the beginning of the reporting lifecycle. This allows
@ -23,11 +27,16 @@ trait Reporter[F[_]]:
/** Report the results of a single group. /** Report the results of a single group.
* *
* @param groupResult * @param groupResult
* The [[GroupResult]] that describes results. * The [[GroupResult]] that describes the group level summary.
* @param testExecutions
* The list of [[TestExecution]] describing the result of each test.
* @return * @return
* Side-effect that describes the reporting operation. * Side-effect that describes the reporting operation.
*/ */
def reportGroup(groupResult: GroupResult): F[Unit] def reportGroup(
groupResult: GroupResult,
testExecutions: List[TestExecution]
): F[Unit]
/** Report the results of an entire suite. /** Report the results of an entire suite.
* *
@ -43,3 +52,12 @@ trait Reporter[F[_]]:
* footer. * footer.
*/ */
def endReporting(): F[Unit] def endReporting(): F[Unit]
object Reporter:
/** @return
* New instance of the no-op Reporter implementation.
*/
def noop[F[_]: Applicative]: Reporter[F] = new NoopReporter[F]
end Reporter

View file

@ -0,0 +1,39 @@
package gs.test.v0.reporting
import gs.test.v0.api.GroupResult
import gs.test.v0.api.SuiteExecution
import gs.test.v0.api.TestExecution
/** Interface for formatters - implementations that transform test results into
* string representations.
*
* Example implementations include producing plain text or JSON
* representations.
*/
trait ResultFormatter:
/** Format a single [[GroupResult]] as a string.
*
* @param groupResult
* The result to format.
* @return
* The string rendition.
*/
def formatGroupResult(groupResult: GroupResult): String
/** Format a single [[TestExecution]] as a string.
*
* @param testExecution
* The result to format.
* @return
* The string rendition.
*/
def formatTestExecution(testExecution: TestExecution): String
/** Format a single [[SuiteExecution]] as a string.
*
* @param suiteExecution
* The result to format.
* @return
* The string rendition.
*/
def formatSuiteExecution(suiteExecution: SuiteExecution): String

View file

@ -76,17 +76,24 @@ final class TestEngine[F[_]: Async](
// TODO: Just do telemetry for the whole damn thing. // TODO: Just do telemetry for the whole damn thing.
_ <- tests _ <- tests
.mapAsync(configuration.groupConcurrency.toInt())(runGroup) .mapAsync(configuration.groupConcurrency.toInt())(runGroup)
.evalTap(groupResult => .evalTap(
for (
// Update the overall statistics based on this group. groupResult,
_ <- stats.updateForGroup( testExecutions
duration = groupResult.duration, ) =>
testExecutions = groupResult.testExecutions for
) // Update the overall statistics based on this group.
_ <- stats.updateForGroup(
duration = groupResult.duration,
testExecutions = testExecutions
)
// Report group level results for this group. // Report group level results for this group.
_ <- reporter.reportGroup(groupResult) _ <- reporter.reportGroup(
yield () groupResult = groupResult,
testExecutions = testExecutions
)
yield ()
) )
.compile .compile
.drain .drain
@ -103,7 +110,7 @@ final class TestEngine[F[_]: Async](
def runGroup( def runGroup(
group: TestGroupDefinition[F] group: TestGroupDefinition[F]
): F[GroupResult] = ): F[(GroupResult, List[TestExecution])] =
entryPoint.root(EngineConstants.Tracing.RootSpan).use { rootSpan => entryPoint.root(EngineConstants.Tracing.RootSpan).use { rootSpan =>
for for
groupStats <- EngineStats.initialize[F] groupStats <- EngineStats.initialize[F]
@ -150,9 +157,8 @@ final class TestEngine[F[_]: Async](
duration = elapsed.duration, duration = elapsed.duration,
seen = seen, seen = seen,
passed = passed, passed = passed,
failed = failed, failed = failed
testExecutions = testExecutions ) -> testExecutions
)
} }
private def executeGroupTests( private def executeGroupTests(