Zum Hauptinhalt springen

Evaluatoren

Jeder Evaluator kann eine Menge von Auswertungsschritten (Capabilities) unterstützen. Diese sind derzeit: compile, test, style.

Eigene Evaluatoren mit Python implementieren

Für einen neuen Evaluator ist eine Subklasse von eva.pltform.evaluation.core.eval.Evaluator anzulegen und die Setup-Methode zu überschreiben:

class JavaEvaluator(Evaluator):
# ...

def setup(self, ctx: EvaluationContext):
# Evaluator-spezifische Validierungslogik: Bestimmte Anfragen zurückweisen
check_dynamic_asserts(ctx)

# Eigene Parameter verarbeiten
java_args, javac_args = configure_jvm(ctx)

# Handler definieren, die bei jedem Auswertungsschritt aufgerufen werden sollen:
return ExecutionHandlerConfig(
compile=JavaCompileHandler(javac_args=javac_args),
style=JavaStyleHandler(java_args=java_args),
test=JavaTestHandler(java_args=java_args),
coverage=JavaCoverageHandler(javac_args=javac_args, java_args=java_args)
)

In den Handlern kann dann die Auswertungslogik implementiert werden:

class JavaTestHandler(TestHandler):

def __init__(self, java_args):
self.java_args = java_args

def run(self, evaluator: Evaluator, ctx: EvaluationContext) -> TestResult:
test_fqcns = [to_fqcn(f) for f in ctx.payload.test_files]
r = ctx.env.bash(
[
f'java {self.java_args} -cp $EJUNIT_RUNNER_PATH:$CLASSPATH:classes',
'de.hsrm.sls.ejunit.mono.Runner -o trex_report.xml',
f'-s {ctx.opts.max_output_size}',
*test_fqcns,
],
max_output_size=ctx.opts.max_output_size,
timeout=ctx.opts.test_timeout,
)
r.expect(status=[CODE_SUCCESS, CODE_TIMEOUT])

testsuite = self.trex.parse(ctx.env.read_file('trex_report.xml')).fix_timeouts(
ctx.opts.test_timeout
)
return self.mapper.map(testsuite=testsuite, output=r.out())

ctx.env bietet die Schnittstelle für den Zugriff auf die Umgebung. Darüber können Befehle ausgeführt, Verzeichnisse angelegt oder Dateien gelesen werden. Zudem gibt es Asserts (z.B. r.expect), die sicherstellen, dass der Befehl korrekt ausgeführt wurde. Mit ctx.payload kann auf die Anfrage zugegriffen werden und ctx.opts ermöglicht den Zugriff auf die Konfiguration.

Anschließend müssen Metadaten in der Klasse ergänzt werden:

class JavaEvaluator(Evaluator):
id = 'java'
supported_envs = 'java*'
default_env = 'java19'
capability = EvaluatorCapability(compile=True, style=True, test=True)
  • id: Eindeutige ID
  • supported_envs: Unix glob-Pattern, welches alle Umgebungen matcht, für die der Evaluator verwendet werden kann
  • default_env: Der Selektor für den Evaluation Stack bietet die Möglichkeit, die Umgebung wegzulassen. In dem Fall wird die angegebene Umgebung als Default verwendet
  • capability: Auswertungsschritte, die der Evaluator unterstützt

Damit der Evaluator zur Verfügung steht, muss die Implementierung in der EvaluatorRegistry konfiguriert werden:

eva.pltform/eva2/pltform/registry.py
class EvaluatorRegistry:
# ...

def _load_evaluators(self):
if self._evaluators is None:
self._evaluators = [
# ...
JavaEvaluator,
]

return self._evaluators

Integration-Tests für eigenen Evaluator schreiben

Evaluatoren lassen sich sehr einfach über Integration-Tests debuggen. Gleichzeitig stellt man die Qualität sicher und sorgt dafür, dass der Evaluator automatisch getestet werden kann. Hierfür sind ein (oder mehrere) Testmodule anzulegen:

eva.pltform/tests/integration/evaluator/java.py
@pytest.mark.evaluator('java') # 'java' ist die zuvor vergebene ID des Evaluators
class JavaTest:
def test_passing(self, ctx: TestContext):
payload = EvalPayload(
solution_files=[
EvaFile(path='Heron.java', content=ctx.fixtures.load('java/passing/Heron.java'))
],
test_files=[
EvaFile(
path='HeronTest.java', content=ctx.fixtures.load('java/passing/HeronTest.java')
)
],
include_files=[],
compile=True,
test=True,
)

result = ctx.eval(payload)
assert result.compilation.compiling
assert pytest.approx(result.test.correctness) == 1.0
# ...

Unter eva.pltform/tests/integration/fixtures können die Dateien organisiert werden, die für die einzelnen Testfälle benötigt werden. Der Pfad in ctx.fixtures.load ist relativ zu diesem Verzeichnis.

Zuletzt muss der Evaluator noch mit den Umgebungen für die Tests registriert werden:

eva.pltform/tests/integration/conftest.py
env_map = {
# ...
'java': ['java19', 'java20', 'java21'],
}

Der Eintrag definiert alle Umgebungen (java19, java20, java21), die für Tests mit dem Evaluator java verwendet werden sollen. Alle Testfälle in Klassen, die mit @pytest.mark.evaluator('java') annotiert werden, werden einmal für jede dieser Umgebungen ausgeführt. Weitere Infos zur Ausführung der Tests finden sich unter Integration-Tests.