From f8007f1ade2ceb2fe352b0765d3ce18762065c6a Mon Sep 17 00:00:00 2001 From: Niko Strijbol Date: Wed, 9 Aug 2023 14:17:31 +0200 Subject: [PATCH] Fix crash and add test In adding the types, we used `zip_longest`, which resulted in a crash if there were more values than expected in the test suite. This has been fixed to properly handle this. --- tested/evaluators/value.py | 11 ++--- tested/serialisation.py | 3 +- tests/test_evaluators.py | 92 +++++++++++++++++++++++++++++++++++++- 3 files changed, 95 insertions(+), 11 deletions(-) diff --git a/tested/evaluators/value.py b/tested/evaluators/value.py index a1425c7d..f2e3929b 100644 --- a/tested/evaluators/value.py +++ b/tested/evaluators/value.py @@ -1,7 +1,6 @@ """ Value evaluator. """ -import itertools import logging from typing import Optional, Tuple, Union, cast @@ -198,9 +197,7 @@ def _check_data_type( prepared_actual.data if isinstance(prepared_actual.data, list) else [] ) prepared_elements = [] - for expected_element, actual_element in itertools.zip_longest( - expected_elements, actual_elements - ): + for expected_element, actual_element in zip(expected_elements, actual_elements): assert expected_element is None or isinstance(expected_element, Value) assert actual_element is None or isinstance(actual_element, Value) element_valid, prepared_element = _check_data_type( @@ -215,13 +212,11 @@ def _check_data_type( prepared_actual.data if isinstance(prepared_actual.data, list) else [] ) prepared_elements = [] - for expected_element, actual_element in itertools.zip_longest( - expected_elements, actual_elements - ): + for expected_element, actual_element in zip(expected_elements, actual_elements): assert isinstance(actual_element, ObjectKeyValuePair) actual_key, actual_value = actual_element.key, actual_element.value assert isinstance(actual_key, Value) and isinstance(actual_value, Value) - expected_key, expected_value = actual_element.key, actual_element.value + expected_key, expected_value = expected_element.key, expected_element.value assert isinstance(expected_key, Value) and isinstance(expected_value, Value) key_valid, prepared_key = _check_data_type(bundle, expected_key, actual_key) value_valid, prepared_value = _check_data_type( diff --git a/tested/serialisation.py b/tested/serialisation.py index d4af748c..669a5678 100644 --- a/tested/serialisation.py +++ b/tested/serialisation.py @@ -177,7 +177,8 @@ def check_number_types(cls, values): f"real numbers." ) - if resolve_to_basic(values.get("type")) == BasicNumericTypes.INTEGER: + type_ = values.get("type") + if type_ and resolve_to_basic(type_) == BasicNumericTypes.INTEGER: values["data"] = values["data"].to_integral_value() return values diff --git a/tests/test_evaluators.py b/tests/test_evaluators.py index aac94489..041bfd3c 100644 --- a/tests/test_evaluators.py +++ b/tests/test_evaluators.py @@ -7,13 +7,19 @@ import tested from tested.configs import create_bundle -from tested.datatypes import BasicSequenceTypes, BasicStringTypes +from tested.datatypes import BasicObjectTypes, BasicSequenceTypes, BasicStringTypes from tested.dodona import Status from tested.evaluators.common import EvaluationResult, EvaluatorConfig from tested.evaluators.exception import evaluate as evaluate_exception from tested.evaluators.text import evaluate_file, evaluate_text from tested.evaluators.value import evaluate as evaluate_value -from tested.serialisation import ExceptionValue, SequenceType, StringType +from tested.serialisation import ( + ExceptionValue, + ObjectKeyValuePair, + ObjectType, + SequenceType, + StringType, +) from tested.testsuite import ( ExceptionOutputChannel, ExpectedException, @@ -490,3 +496,85 @@ def test_nested_sets_type_check_works_if_correct(tmp_path: Path, pytestconfig): ), ) assert result.result.enum == Status.CORRECT + + +def test_too_many_sequence_values_dont_crash(tmp_path: Path, pytestconfig): + expected_value = SequenceType( + type=BasicSequenceTypes.SEQUENCE, + data=[ + SequenceType( + type=BasicSequenceTypes.SET, + data=[ + StringType(type=BasicStringTypes.TEXT, data="a"), + StringType(type=BasicStringTypes.TEXT, data="b"), + ], + ), + ], + ) + actual_value = SequenceType( + type=BasicSequenceTypes.SEQUENCE, + data=[ + SequenceType( + type=BasicSequenceTypes.SET, + data=[ + StringType(type=BasicStringTypes.TEXT, data="a"), + StringType(type=BasicStringTypes.TEXT, data="a"), + ], + ), + StringType(type=BasicStringTypes.TEXT, data="c"), + SequenceType( + type=BasicSequenceTypes.SET, + data=[ + StringType(type=BasicStringTypes.TEXT, data="b"), + StringType(type=BasicStringTypes.TEXT, data="a"), + ], + ), + ], + ) + channel = ValueOutputChannel(value=expected_value) + config = evaluator_config(tmp_path, pytestconfig, language="python") + result = evaluate_value( + config, + channel, + json.dumps( + actual_value, + default=pydantic_encoder, + ), + ) + assert result.result.enum == Status.WRONG + + +def test_too_many_object_values_dont_crash(tmp_path: Path, pytestconfig): + expected_value = ObjectType( + type=BasicObjectTypes.MAP, + data=[ + ObjectKeyValuePair( + key=StringType(type=BasicStringTypes.TEXT, data="a"), + value=StringType(type=BasicStringTypes.TEXT, data="b"), + ) + ], + ) + actual_value = ObjectType( + type=BasicObjectTypes.MAP, + data=[ + ObjectKeyValuePair( + key=StringType(type=BasicStringTypes.TEXT, data="a"), + value=StringType(type=BasicStringTypes.TEXT, data="b"), + ), + ObjectKeyValuePair( + key=StringType(type=BasicStringTypes.TEXT, data="c"), + value=StringType(type=BasicStringTypes.TEXT, data="d"), + ), + ], + ) + channel = ValueOutputChannel(value=expected_value) + config = evaluator_config(tmp_path, pytestconfig, language="python") + result = evaluate_value( + config, + channel, + json.dumps( + actual_value, + default=pydantic_encoder, + ), + ) + assert result.result.enum == Status.WRONG