-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from kleis-technology/cli/test-runner
Cli/test runner
- Loading branch information
Showing
17 changed files
with
585 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
test should_pass { | ||
given { | ||
1 kWh electricity from electricity_mix | ||
} | ||
assert { | ||
co2 between 0 kg and 10 kg | ||
} | ||
} | ||
|
||
test should_fail { | ||
given { | ||
1 kWh electricity from electricity_mix | ||
} | ||
assert { | ||
co2 between 100 kg and 1000 kg | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package ch.kleis.lcaac.cli.cmd | ||
|
||
import ch.kleis.lcaac.core.math.basic.BasicOperations | ||
import ch.kleis.lcaac.core.testing.BasicTestRunner | ||
import ch.kleis.lcaac.core.testing.GenericFailure | ||
import ch.kleis.lcaac.core.testing.RangeAssertionFailure | ||
import ch.kleis.lcaac.core.testing.RangeAssertionSuccess | ||
import ch.kleis.lcaac.grammar.CoreTestMapper | ||
import ch.kleis.lcaac.grammar.Loader | ||
import ch.kleis.lcaac.grammar.LoaderOption | ||
import ch.kleis.lcaac.grammar.parser.LcaLangParser | ||
import com.github.ajalt.clikt.core.CliktCommand | ||
import com.github.ajalt.clikt.core.ProgramResult | ||
import com.github.ajalt.clikt.parameters.options.default | ||
import com.github.ajalt.clikt.parameters.options.flag | ||
import com.github.ajalt.clikt.parameters.options.help | ||
import com.github.ajalt.clikt.parameters.options.option | ||
import com.github.ajalt.clikt.parameters.types.file | ||
import java.io.File | ||
|
||
private const val greenTick = "\u2705" | ||
private const val redCross = "\u274C" | ||
|
||
class TestCommand : CliktCommand(name = "test", help = "Run specified tests") { | ||
val root: File by option("-r", "--root").file(canBeFile = false).default(File(".")).help("Root folder") | ||
val data: File? by option("-d", "--data").file(canBeDir = false).help("CSV file with parameter values") | ||
val showSuccess: Boolean by option("--show-success").flag(default = false).help("Show successful assertions") | ||
|
||
override fun run() { | ||
val files = lcaFiles(root) | ||
val symbolTable = Loader(BasicOperations).load(files, listOf(LoaderOption.WITH_PRELUDE)) | ||
val mapper = CoreTestMapper() | ||
val cases = files | ||
.flatMap { it.testDefinition() } | ||
.map { mapper.test(it) } | ||
val runner = BasicTestRunner<LcaLangParser.TestDefinitionContext>(symbolTable) | ||
val results = cases.map { runner.run(it) } | ||
|
||
results.forEach { result -> | ||
result.results.forEachIndexed { id, assertion -> | ||
val isSuccess: Boolean = assertion is RangeAssertionSuccess | ||
val tick = if (isSuccess) greenTick else redCross | ||
|
||
val message = when (assertion) { | ||
is GenericFailure -> assertion.message | ||
is RangeAssertionFailure -> "${assertion.name} = ${assertion.actual} is not in between ${assertion.lo} and ${assertion.hi}" | ||
is RangeAssertionSuccess -> "${assertion.name} = ${assertion.actual} is in between ${assertion.lo} and ${assertion.hi}" | ||
} | ||
if ((isSuccess && showSuccess) || !isSuccess) | ||
echo("$tick ${result.name}[$id] $message") | ||
} | ||
} | ||
val nbTests = results.flatMap { it.results }.count() | ||
val nbSuccesses = results.flatMap { it.results }.count { it is RangeAssertionSuccess } | ||
val nbFailures = nbTests - nbSuccesses | ||
echo("Run $nbTests tests, $nbSuccesses passed, $nbFailures failed") | ||
if (nbFailures > 0) throw ProgramResult(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package ch.kleis.lcaac.cli.cmd | ||
|
||
import ch.kleis.lcaac.grammar.parser.LcaLangLexer | ||
import ch.kleis.lcaac.grammar.parser.LcaLangParser | ||
import org.antlr.v4.runtime.CharStreams | ||
import org.antlr.v4.runtime.CommonTokenStream | ||
import java.io.File | ||
import java.io.InputStream | ||
import java.nio.file.Files | ||
import kotlin.io.path.isRegularFile | ||
|
||
fun lcaFiles(root: File): Sequence<LcaLangParser.LcaFileContext> { | ||
return Files.walk(root.toPath()) | ||
.filter { it.isRegularFile() } | ||
.filter { it.toString().endsWith(".lca") } | ||
.map { lcaFile(it.toFile().inputStream()) } | ||
.toList() | ||
.asSequence() | ||
} | ||
|
||
private fun lcaFile(inputStream: InputStream): LcaLangParser.LcaFileContext { | ||
val lexer = LcaLangLexer(CharStreams.fromStream(inputStream)) | ||
val tokens = CommonTokenStream(lexer) | ||
val parser = LcaLangParser(tokens) | ||
return parser.lcaFile() | ||
} |
22 changes: 22 additions & 0 deletions
22
core/src/main/kotlin/ch/kleis/lcaac/core/testing/AssertionResult.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package ch.kleis.lcaac.core.testing | ||
|
||
import ch.kleis.lcaac.core.lang.value.QuantityValue | ||
import ch.kleis.lcaac.core.math.basic.BasicNumber | ||
|
||
sealed interface AssertionResult | ||
|
||
data class RangeAssertionSuccess( | ||
val name: String, | ||
val lo: QuantityValue<BasicNumber>, | ||
val hi: QuantityValue<BasicNumber>, | ||
val actual: QuantityValue<BasicNumber>, | ||
) : AssertionResult | ||
|
||
data class GenericFailure(val message: String) : AssertionResult | ||
|
||
data class RangeAssertionFailure( | ||
val name: String, | ||
val lo: QuantityValue<BasicNumber>, | ||
val hi: QuantityValue<BasicNumber>, | ||
val actual: QuantityValue<BasicNumber>, | ||
) : AssertionResult |
91 changes: 91 additions & 0 deletions
91
core/src/main/kotlin/ch/kleis/lcaac/core/testing/BasicTestRunner.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package ch.kleis.lcaac.core.testing | ||
|
||
import ch.kleis.lcaac.core.assessment.ContributionAnalysisProgram | ||
import ch.kleis.lcaac.core.lang.SymbolTable | ||
import ch.kleis.lcaac.core.lang.evaluator.Evaluator | ||
import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException | ||
import ch.kleis.lcaac.core.lang.evaluator.ToValue | ||
import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer | ||
import ch.kleis.lcaac.core.lang.value.DataValue | ||
import ch.kleis.lcaac.core.lang.value.QuantityValue | ||
import ch.kleis.lcaac.core.lang.value.QuantityValueOperations | ||
import ch.kleis.lcaac.core.math.basic.BasicNumber | ||
import ch.kleis.lcaac.core.math.basic.BasicOperations | ||
|
||
class BasicTestRunner<S>( | ||
symbolTable: SymbolTable<BasicNumber>, | ||
private val evaluator: Evaluator<BasicNumber> = Evaluator(symbolTable, BasicOperations), | ||
private val dataReducer: DataExpressionReducer<BasicNumber> = DataExpressionReducer(symbolTable.data, BasicOperations), | ||
) { | ||
fun run(case: TestCase<S>): TestResult<S> { | ||
try { | ||
val trace = evaluator.with(case.template).trace(case.template) | ||
val program = ContributionAnalysisProgram(trace.getSystemValue(), trace.getEntryPoint()) | ||
val analysis = program.run() | ||
val target = trace.getEntryPoint().products.first().port() | ||
val results = case.assertions.map { assertion -> | ||
val ports = analysis.findAllPortsByShortName(assertion.ref) | ||
if (ports.isEmpty()) { | ||
GenericFailure("unknown reference '${assertion.ref}'") | ||
} else { | ||
val impact = with(QuantityValueOperations(BasicOperations)) { | ||
ports.map { | ||
if (analysis.isControllable(it)) analysis.getPortContribution(target, it) | ||
else analysis.supplyOf(it) | ||
}.reduce { acc, quantityValue -> acc + quantityValue } | ||
} | ||
val lo = with(ToValue(BasicOperations)) { dataReducer.reduce(assertion.lo).toValue() } | ||
val hi = with(ToValue(BasicOperations)) { dataReducer.reduce(assertion.hi).toValue() } | ||
test(assertion.ref, impact, lo, hi) | ||
} | ||
} | ||
return TestResult( | ||
case.source, | ||
case.name, | ||
results, | ||
) | ||
} catch (e: EvaluatorException) { | ||
return TestResult( | ||
case.source, | ||
case.name, | ||
listOf( | ||
GenericFailure(e.message ?: "unknown error"), | ||
) | ||
) | ||
} | ||
} | ||
|
||
private fun test( | ||
name: String, | ||
impact: QuantityValue<BasicNumber>, | ||
lo: DataValue<BasicNumber>, | ||
hi: DataValue<BasicNumber> | ||
): AssertionResult { | ||
with(QuantityValueOperations(BasicOperations)) { | ||
val actual = impact.toDouble() | ||
return when { | ||
lo is QuantityValue<BasicNumber> && hi is QuantityValue<BasicNumber> -> | ||
when { | ||
!allTheSameDimension(impact, lo, hi) -> | ||
GenericFailure("incompatible dimensions: $name (${impact.unit.dimension}) between $lo (${lo.unit.dimension}) and $hi (${hi.unit.dimension})") | ||
|
||
lo.toDouble() <= actual && actual <= hi.toDouble() -> | ||
RangeAssertionSuccess(name, lo, hi, impact) | ||
|
||
else -> RangeAssertionFailure(name, lo, hi, impact) | ||
} | ||
|
||
else -> GenericFailure("invalid range: $lo and $hi") | ||
} | ||
} | ||
} | ||
|
||
private fun allTheSameDimension( | ||
a: QuantityValue<BasicNumber>, | ||
b: QuantityValue<BasicNumber>, | ||
c: QuantityValue<BasicNumber> | ||
): Boolean { | ||
return a.unit.dimension == b.unit.dimension | ||
&& b.unit.dimension == c.unit.dimension | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
core/src/main/kotlin/ch/kleis/lcaac/core/testing/RangeAssertion.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package ch.kleis.lcaac.core.testing | ||
|
||
import ch.kleis.lcaac.core.lang.expression.DataExpression | ||
import ch.kleis.lcaac.core.math.basic.BasicNumber | ||
|
||
data class RangeAssertion( | ||
val ref: String, | ||
val lo: DataExpression<BasicNumber>, | ||
val hi: DataExpression<BasicNumber>, | ||
) |
13 changes: 13 additions & 0 deletions
13
core/src/main/kotlin/ch/kleis/lcaac/core/testing/TestCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package ch.kleis.lcaac.core.testing | ||
|
||
import ch.kleis.lcaac.core.lang.expression.DataExpression | ||
import ch.kleis.lcaac.core.lang.expression.EProcessTemplate | ||
import ch.kleis.lcaac.core.math.basic.BasicNumber | ||
|
||
data class TestCase<S>( | ||
val source: S, | ||
val name: String, | ||
val assertions: List<RangeAssertion>, | ||
val template: EProcessTemplate<BasicNumber>, | ||
val arguments: Map<String, DataExpression<BasicNumber>> = emptyMap(), | ||
) |
7 changes: 7 additions & 0 deletions
7
core/src/main/kotlin/ch/kleis/lcaac/core/testing/TestResult.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package ch.kleis.lcaac.core.testing | ||
|
||
data class TestResult<S>( | ||
val source: S, | ||
val name: String, | ||
val results: List<AssertionResult>, | ||
) |
Oops, something went wrong.