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.
* @param failed
* The number of tests which failed.
* @param testExecutions
* List of test results.
*/
final class GroupResult(
val name: TestGroupDefinition.Name,
@ -25,6 +23,5 @@ final class GroupResult(
val duration: FiniteDuration,
val seen: Long,
val passed: Long,
val failed: Long,
val testExecutions: List[TestExecution]
val failed: Long
)

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
import cats.Applicative
import gs.test.v0.api.GroupResult
import gs.test.v0.api.SuiteExecution
import gs.test.v0.api.TestExecution
/** Interface for reporters - implementations that report on test results.
*
@ -10,8 +12,10 @@ import gs.test.v0.api.SuiteExecution
*
* ## Order of Operations
*
* 1. `beginReporting()` 2. `reportGroup` for each group executed 3.
* `reportSuite` 4. `endReporting()`
* - `beginReporting()`
* - `reportGroup` for each group executed
* - `reportSuite`
* - `endReporting()`
*/
trait Reporter[F[_]]:
/** Hook for the beginning of the reporting lifecycle. This allows
@ -23,11 +27,16 @@ trait Reporter[F[_]]:
/** Report the results of a single group.
*
* @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
* 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.
*
@ -43,3 +52,12 @@ trait Reporter[F[_]]:
* footer.
*/
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,16 +76,23 @@ final class TestEngine[F[_]: Async](
// TODO: Just do telemetry for the whole damn thing.
_ <- tests
.mapAsync(configuration.groupConcurrency.toInt())(runGroup)
.evalTap(groupResult =>
.evalTap(
(
groupResult,
testExecutions
) =>
for
// Update the overall statistics based on this group.
_ <- stats.updateForGroup(
duration = groupResult.duration,
testExecutions = groupResult.testExecutions
testExecutions = testExecutions
)
// Report group level results for this group.
_ <- reporter.reportGroup(groupResult)
_ <- reporter.reportGroup(
groupResult = groupResult,
testExecutions = testExecutions
)
yield ()
)
.compile
@ -103,7 +110,7 @@ final class TestEngine[F[_]: Async](
def runGroup(
group: TestGroupDefinition[F]
): F[GroupResult] =
): F[(GroupResult, List[TestExecution])] =
entryPoint.root(EngineConstants.Tracing.RootSpan).use { rootSpan =>
for
groupStats <- EngineStats.initialize[F]
@ -150,9 +157,8 @@ final class TestEngine[F[_]: Async](
duration = elapsed.duration,
seen = seen,
passed = passed,
failed = failed,
testExecutions = testExecutions
)
failed = failed
) -> testExecutions
}
private def executeGroupTests(