From c6173ec14b1b3ec02211f9a3f5ee67fe2e78d40e Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Tue, 14 Nov 2023 20:00:08 -0800 Subject: [PATCH] bool tests --- src/ytdl_sub/script/functions/__init__.py | 12 ++++++--- src/ytdl_sub/script/parser.py | 20 +++++++++++++-- tests/unit/script/test_parser.py | 2 +- tests/unit/script/types/test_bool.py | 31 +++++++++++++++++++++-- tests/unit/script/types/test_float.py | 5 +++- tests/unit/script/types/test_integer.py | 5 +++- tests/unit/script/types/test_string.py | 3 ++- 7 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/ytdl_sub/script/functions/__init__.py b/src/ytdl_sub/script/functions/__init__.py index 46d494f4..49c09a5b 100644 --- a/src/ytdl_sub/script/functions/__init__.py +++ b/src/ytdl_sub/script/functions/__init__.py @@ -1,11 +1,17 @@ -from typing import Optional - from ytdl_sub.script.functions.array_functions import ArrayFunctions +from ytdl_sub.script.functions.boolean_functions import BooleanFunctions from ytdl_sub.script.functions.map_functions import MapFunctions from ytdl_sub.script.functions.numeric_functions import NumericFunctions from ytdl_sub.script.functions.special_functions import SpecialFunctions from ytdl_sub.script.functions.string_functions import StringFunctions -class Functions(StringFunctions, NumericFunctions, SpecialFunctions, ArrayFunctions, MapFunctions): +class Functions( + StringFunctions, + NumericFunctions, + SpecialFunctions, + ArrayFunctions, + MapFunctions, + BooleanFunctions, +): pass diff --git a/src/ytdl_sub/script/parser.py b/src/ytdl_sub/script/parser.py index c2a6d200..aee6d016 100644 --- a/src/ytdl_sub/script/parser.py +++ b/src/ytdl_sub/script/parser.py @@ -55,6 +55,10 @@ STRINGS_NOT_CLOSED = InvalidSyntaxException( "Must open and close with the same type of quote (single/double)" ) +BOOLEAN_ONLY_ARGS = InvalidSyntaxException( + "Booleans can only be used as arguments to functions, maps, or arrays" +) + def UNEXPECTED_CHAR_ARGUMENT(parser: ArgumentParser): return InvalidSyntaxException(f"Unexpected character when parsing {parser.value} arguments") @@ -90,6 +94,14 @@ def _is_breakable(char: str) -> bool: return char in ["}", ",", ")", "]"] or char.isspace() +def _is_boolean_true(string: Optional[str]) -> bool: + return string == "True" + + +def _is_boolean_false(string: Optional[str]) -> bool: + return string == "False" + + class _Parser: def __init__(self, text: str): self._text = text @@ -252,10 +264,10 @@ class _Parser: return self._parse_function() if _is_numeric_start(self._read(increment_pos=False)): return self._parse_numeric() - if (self._read(increment_pos=False, length=4) or "").lower() == "true": + if _is_boolean_true(self._read(increment_pos=False, length=4)): self._pos += 4 return Boolean(value=True) - if (self._read(increment_pos=False, length=5) or "").lower() == "false": + if _is_boolean_false(self._read(increment_pos=False, length=5)): self._pos += 5 return Boolean(value=False) if _is_string_start(self._read(increment_pos=False)): @@ -441,6 +453,10 @@ class _Parser: raise NUMERICS_ONLY_ARGS elif _is_string_start(ch1): raise STRINGS_ONLY_ARGS + elif _is_boolean_true( + self._read(increment_pos=False, length=4) + ) or _is_boolean_false(self._read(increment_pos=False, length=5)): + raise BOOLEAN_ONLY_ARGS else: raise UNEXPECTED_CHAR_ARGUMENT(parser=ArgumentParser.SCRIPT) elif bracket_counter == 0: diff --git a/tests/unit/script/test_parser.py b/tests/unit/script/test_parser.py index 60efc77c..2ee26a9e 100644 --- a/tests/unit/script/test_parser.py +++ b/tests/unit/script/test_parser.py @@ -155,7 +155,7 @@ class TestParser: input_str = ( f"hello{s}{{{s}%concat({s}'string'{s},{s}%string(1){s},{s}%string(2.4){s}," - f"{s}%string(TRUE){s},{s}%string(variable_name){s},{s}%capitalize({s}'hi'{s}){s})}}" + f"{s}%string(True){s},{s}%string(variable_name){s},{s}%capitalize({s}'hi'{s}){s})}}" f"{s}" ) parsed = parse(input_str) diff --git a/tests/unit/script/types/test_bool.py b/tests/unit/script/types/test_bool.py index 40499be1..ee6a3fd6 100644 --- a/tests/unit/script/types/test_bool.py +++ b/tests/unit/script/types/test_bool.py @@ -3,14 +3,15 @@ from typing import Tuple import pytest +from ytdl_sub.script.parser import BOOLEAN_ONLY_ARGS from ytdl_sub.script.parser import NUMERICS_INVALID_CHAR from ytdl_sub.script.parser import NUMERICS_ONLY_ARGS -from ytdl_sub.script.parser import STRINGS_ONLY_ARGS from ytdl_sub.script.parser import UNEXPECTED_CHAR_ARGUMENT from ytdl_sub.script.parser import UNEXPECTED_COMMA_ARGUMENT from ytdl_sub.script.parser import ArgumentParser from ytdl_sub.script.script import Script from ytdl_sub.script.types.array import ResolvedArray +from ytdl_sub.script.types.resolvable import Boolean from ytdl_sub.script.types.resolvable import Float from ytdl_sub.script.types.resolvable import Integer from ytdl_sub.script.types.resolvable import String @@ -18,4 +19,30 @@ from ytdl_sub.script.utils.exceptions import InvalidSyntaxException class TestBool: - pass + @pytest.mark.parametrize( + "boolean", + [ + "{True}", + "{ True }", + "{False}", + "{ False }", + ], + ) + def test_boolean_not_arg(self, boolean: str): + with pytest.raises(InvalidSyntaxException, match=re.escape(str(BOOLEAN_ONLY_ARGS))): + Script({"boolean": boolean}).resolve() + + @pytest.mark.parametrize( + "boolean, expected_boolean", + [ + ("{%bool(True)}", True), + ("{%bool(False)}", False), + ("{%bool( True )}", True), + ("{%bool( False )}", False), + ], + ) + def test_boolean(self, boolean: bool, expected_boolean: bool): + assert Script({"boolean": boolean, "as_string": "{%string(boolean)}"}).resolve() == { + "boolean": Boolean(expected_boolean), + "as_string": String(str(expected_boolean)), + } diff --git a/tests/unit/script/types/test_float.py b/tests/unit/script/types/test_float.py index d00ae412..acbd0016 100644 --- a/tests/unit/script/types/test_float.py +++ b/tests/unit/script/types/test_float.py @@ -44,7 +44,10 @@ class TestFloat: ], ) def test_float(self, float_: str, expected_float: int): - assert Script({"float": float_}).resolve() == {"float": Float(expected_float)} + assert Script({"float": float_, "as_string": "{%string(float)}"}).resolve() == { + "float": Float(expected_float), + "as_string": String(str(expected_float)), + } @pytest.mark.parametrize( "float_", diff --git a/tests/unit/script/types/test_integer.py b/tests/unit/script/types/test_integer.py index 49907dfe..a7237ede 100644 --- a/tests/unit/script/types/test_integer.py +++ b/tests/unit/script/types/test_integer.py @@ -44,7 +44,10 @@ class TestInteger: ], ) def test_integer(self, integer: str, expected_integer: int): - assert Script({"integer": integer}).resolve() == {"integer": Integer(expected_integer)} + assert Script({"integer": integer, "as_string": "{%string(integer)}"}).resolve() == { + "integer": Integer(expected_integer), + "as_string": String(str(expected_integer)), + } @pytest.mark.parametrize( "integer", diff --git a/tests/unit/script/types/test_string.py b/tests/unit/script/types/test_string.py index 41b3f48e..b4413038 100644 --- a/tests/unit/script/types/test_string.py +++ b/tests/unit/script/types/test_string.py @@ -3,8 +3,9 @@ from typing import Tuple import pytest -from ytdl_sub.script.parser import NUMERICS_INVALID_CHAR, STRINGS_NOT_CLOSED +from ytdl_sub.script.parser import NUMERICS_INVALID_CHAR from ytdl_sub.script.parser import NUMERICS_ONLY_ARGS +from ytdl_sub.script.parser import STRINGS_NOT_CLOSED from ytdl_sub.script.parser import STRINGS_ONLY_ARGS from ytdl_sub.script.parser import UNEXPECTED_CHAR_ARGUMENT from ytdl_sub.script.parser import UNEXPECTED_COMMA_ARGUMENT