diff --git a/src/ytdl_sub/script/functions/numeric_functions.py b/src/ytdl_sub/script/functions/numeric_functions.py index fe157742..02d90c11 100644 --- a/src/ytdl_sub/script/functions/numeric_functions.py +++ b/src/ytdl_sub/script/functions/numeric_functions.py @@ -1,3 +1,4 @@ +import math from typing import Union from ytdl_sub.script.types.resolvable import Boolean @@ -29,25 +30,36 @@ class NumericFunctions: return Integer(value=int(value.value)) @staticmethod - def add(left: Numeric, right: Numeric) -> Numeric: + def add(*values: Numeric) -> Numeric: """ - ``+`` operator. Returns ``left + right``. + ``+`` operator. Returns the sum of all values. """ - return _to_numeric(left.value + right.value) + return _to_numeric(sum(val.value for val in values)) @staticmethod - def sub(left: Numeric, right: Numeric) -> Numeric: + def sub(*values: Numeric) -> Numeric: """ - ``-`` operator. Returns ``left - right``. + ``-`` operator. Subtracts all values from left to right. """ - return _to_numeric(left.value - right.value) + output = values[0].value + for val in values[1:]: + output -= val.value + + return _to_numeric(output) @staticmethod - def mul(left: Numeric, right: Numeric) -> Numeric: + def mul(*values: Numeric) -> Numeric: """ - ``*`` operator. Returns ``left * right``. + ``*`` operator. Returns the product of all values. """ - return _to_numeric(left.value * right.value) + return _to_numeric(math.prod([val.value for val in values])) + + @staticmethod + def pow(base: Numeric, exponent: Numeric) -> Numeric: + """ + ``**`` operator. Returns the exponential of the base and exponent value. + """ + return _to_numeric(math.pow(base.value, exponent.value)) @staticmethod def div(left: Numeric, right: Numeric) -> Numeric: diff --git a/tests/unit/script/conftest.py b/tests/unit/script/conftest.py new file mode 100644 index 00000000..2b3e4f49 --- /dev/null +++ b/tests/unit/script/conftest.py @@ -0,0 +1,15 @@ +from ytdl_sub.script.script import Script + + +def single_variable_output(script: str): + output = ( + Script( + { + "output": script, + } + ) + .resolve(update=True) + .get("output") + .native + ) + return output diff --git a/tests/unit/script/functions/test_array_functions.py b/tests/unit/script/functions/test_array_functions.py index e044c01d..6534076c 100644 --- a/tests/unit/script/functions/test_array_functions.py +++ b/tests/unit/script/functions/test_array_functions.py @@ -1,3 +1,5 @@ +from unit.script.conftest import single_variable_output + from ytdl_sub.script.script import Script @@ -19,59 +21,19 @@ class TestArrayFunctions: assert output == ["a", "b", "c"] def test_array_at(self): - output = ( - Script( - { - "array1": "{['a', 'b', 'c']}", - "output": "{%array_at(array1, 1)}", - } - ) - .resolve(update=True) - .get("output") - .native - ) + output = single_variable_output("{%array_at(['a', 'b', 'c'], 1)}") assert output == "b" def test_array_flatten(self): - output = ( - Script( - { - "array1": "{['a', ['b'], [['c']]]}", - "output": "{%array_flatten(array1)}", - } - ) - .resolve(update=True) - .get("output") - .native - ) + output = single_variable_output("{%array_flatten(['a', ['b'], [['c']]])}") assert output == ["a", "b", "c"] def test_array_reverse(self): - output = ( - Script( - { - "array1": "{['a', 'b', 'c']}", - "output": "{%array_reverse(array1)}", - } - ) - .resolve(update=True) - .get("output") - .native - ) + output = single_variable_output("{%array_reverse(['a', 'b', 'c'])}") assert output == ["c", "b", "a"] def test_array_apply(self): - output = ( - Script( - { - "array1": "{['a', 'b', 'c']}", - "output": "{%array_apply(array1, %capitalize)}", - } - ) - .resolve(update=True) - .get("output") - .native - ) + output = single_variable_output("{%array_apply(['a', 'b', 'c'], %capitalize)}") assert output == ["A", "B", "C"] def test_array_enumerate(self): diff --git a/tests/unit/script/functions/test_boolean_functions.py b/tests/unit/script/functions/test_boolean_functions.py index 90fc60e9..aab6afdf 100644 --- a/tests/unit/script/functions/test_boolean_functions.py +++ b/tests/unit/script/functions/test_boolean_functions.py @@ -1,4 +1,5 @@ import pytest +from unit.script.conftest import single_variable_output from ytdl_sub.script.script import Script @@ -21,16 +22,7 @@ class TestBooleanFunctions: @pytest.mark.parametrize("is_ne", [True, False]) def test_eq_ne(self, lhs: str, rhs: str, expected_output: bool, is_ne: bool): op = "ne" if is_ne else "eq" - output = ( - Script( - { - "output": f"{{%{op}({lhs}, {rhs})}}", - } - ) - .resolve(update=True) - .get("output") - .native - ) + output = single_variable_output(f"{{%{op}({lhs}, {rhs})}}") if is_ne: assert output != expected_output @@ -49,16 +41,7 @@ class TestBooleanFunctions: @pytest.mark.parametrize("is_gt", [True, False]) def test_lte_gt(self, lhs: str, rhs: str, expected_output: bool, is_gt: bool): op = "gt" if is_gt else "lte" - output = ( - Script( - { - "output": f"{{%{op}({lhs}, {rhs})}}", - } - ) - .resolve(update=True) - .get("output") - .native - ) + output = single_variable_output(f"{{%{op}({lhs}, {rhs})}}") if is_gt: assert output != expected_output @@ -78,16 +61,7 @@ class TestBooleanFunctions: @pytest.mark.parametrize("is_lt", [True, False]) def test_gte_lt(self, lhs: str, rhs: str, expected_output: bool, is_lt: bool): op = "lt" if is_lt else "gte" - output = ( - Script( - { - "output": f"{{%{op}({lhs}, {rhs})}}", - } - ) - .resolve(update=True) - .get("output") - .native - ) + output = single_variable_output(f"{{%{op}({lhs}, {rhs})}}") if is_lt: assert output != expected_output @@ -106,16 +80,7 @@ class TestBooleanFunctions: ], ) def test_and(self, values: str, expected_output: bool): - output = ( - Script( - { - "output": f"{{%and({values})}}", - } - ) - .resolve(update=True) - .get("output") - .native - ) + output = single_variable_output(f"{{%and({values})}}") assert output == expected_output @pytest.mark.parametrize( @@ -131,16 +96,7 @@ class TestBooleanFunctions: ], ) def test_or(self, values: str, expected_output: bool): - output = ( - Script( - { - "output": f"{{%or({values})}}", - } - ) - .resolve(update=True) - .get("output") - .native - ) + output = single_variable_output(f"{{%or({values})}}") assert output == expected_output @pytest.mark.parametrize( @@ -157,16 +113,7 @@ class TestBooleanFunctions: ], ) def test_xor(self, values: str, expected_output: bool): - output = ( - Script( - { - "output": f"{{%xor({values})}}", - } - ) - .resolve(update=True) - .get("output") - .native - ) + output = single_variable_output(f"{{%xor({values})}}") assert output == expected_output @pytest.mark.parametrize( @@ -177,14 +124,5 @@ class TestBooleanFunctions: ], ) def test_not(self, value: str, expected_output: bool): - output = ( - Script( - { - "output": f"{{%not({value})}}", - } - ) - .resolve(update=True) - .get("output") - .native - ) + output = single_variable_output(f"{{%not({value})}}") assert output == expected_output diff --git a/tests/unit/script/functions/test_numeric_functions.py b/tests/unit/script/functions/test_numeric_functions.py new file mode 100644 index 00000000..bbd2879d --- /dev/null +++ b/tests/unit/script/functions/test_numeric_functions.py @@ -0,0 +1,68 @@ +import pytest +from unit.script.conftest import single_variable_output + +from ytdl_sub.script.script import Script + + +class TestNumericFunctions: + @pytest.mark.parametrize( + "values, expected_output", [("1, 2, 3", 6), ("1", 1), ("-1, -2, -3", -6), ("1.1, 1.2", 2.3)] + ) + def test_add(self, values: str, expected_output: int): + output = single_variable_output(f"{{%add({values})}}") + assert output == expected_output + + @pytest.mark.parametrize( + "values, expected_output", [("1, 2, 3", -4), ("1", 1), ("-1, -2, -3", 4), ("1.5, 2.5", -1)] + ) + def test_sub(self, values: str, expected_output: int): + output = single_variable_output(f"{{%sub({values})}}") + assert output == expected_output + + @pytest.mark.parametrize( + "values, expected_output", + [("1, 2, 3", 6), ("1", 1), ("-1, -2, -3", -6), ("1.5, 2.5", 3.75)], + ) + def test_mul(self, values: str, expected_output: int): + output = single_variable_output(f"{{%mul({values})}}") + assert output == expected_output + + @pytest.mark.parametrize( + "values, expected_output", [("2, 2", 1), ("10, 5", 2), ("4.5, 0.5", 9), ("-3.5, -2", 1.75)] + ) + def test_div(self, values: str, expected_output: int): + output = single_variable_output(f"{{%div({values})}}") + assert output == expected_output + + @pytest.mark.parametrize( + "values, expected_output", + [ + ("8, 3", 2), + ("1, 1", 0), + ], + ) + def test_mod(self, values: str, expected_output: int): + output = single_variable_output(f"{{%mod({values})}}") + assert output == expected_output + + @pytest.mark.parametrize( + "values, expected_output", + [ + ("8, 3, 0.3, 0.2, 1.4, 99.9", 99.9), + ("1, 1, 0, 1", 1), + ], + ) + def test_max(self, values: str, expected_output: int): + output = single_variable_output(f"{{%max({values})}}") + assert output == expected_output + + @pytest.mark.parametrize( + "values, expected_output", + [ + ("8, 3, 0.3, 0.2, 1.4, 99.9", 0.2), + ("1, 1, 0, 1", 0), + ], + ) + def test_min(self, values: str, expected_output: int): + output = single_variable_output(f"{{%min({values})}}") + assert output == expected_output