From 8e20f2b97ec84bb0ae005f02cc85f10c1dc57a69 Mon Sep 17 00:00:00 2001 From: Jesse Bannon Date: Tue, 19 Dec 2023 18:05:17 -0800 Subject: [PATCH] unit tests for functions --- .../script/functions/array_functions.py | 14 +++- .../script/functions/test_array_functions.py | 47 +++++++++++ .../functions/test_boolean_functions.py | 14 ++++ .../functions/test_conditional_functions.py | 10 +-- .../script/functions/test_error_functions.py | 80 +++++++++++++++++++ .../script/functions/test_string_functions.py | 7 ++ 6 files changed, 165 insertions(+), 7 deletions(-) diff --git a/src/ytdl_sub/script/functions/array_functions.py b/src/ytdl_sub/script/functions/array_functions.py index 9dbbe4cf..5dc0e653 100644 --- a/src/ytdl_sub/script/functions/array_functions.py +++ b/src/ytdl_sub/script/functions/array_functions.py @@ -65,7 +65,11 @@ class ArrayFunctions: for idx, overlap_value in enumerate(overlap.value): if overlap_only_missing and idx < len(array.value): continue - output.insert(idx, overlap_value) + + if idx < len(array.value): + output[idx] = overlap_value + else: + output.append(overlap_value) return Array(output) @@ -163,12 +167,18 @@ class ArrayFunctions: @staticmethod def array_apply_fixed( - array: Array, fixed_argument: AnyArgument, lambda2_function: LambdaTwo + array: Array, + fixed_argument: AnyArgument, + lambda2_function: LambdaTwo, + reverse_args: Optional[Boolean] = None, ) -> Array: """ Apply a lambda function on every element in the Array, with ``fixed_argument`` passed as a second argument to every invocation. """ + if reverse_args and reverse_args.value: + return Array([Array([fixed_argument, val]) for val in array.value]) + return Array([Array([val, fixed_argument]) for val in array.value]) @staticmethod diff --git a/tests/unit/script/functions/test_array_functions.py b/tests/unit/script/functions/test_array_functions.py index 7ad81b5d..d9515d2d 100644 --- a/tests/unit/script/functions/test_array_functions.py +++ b/tests/unit/script/functions/test_array_functions.py @@ -96,3 +96,50 @@ class TestArrayFunctions: FunctionRuntimeException, match="Tried and failed to cast Integer as an Array" ): single_variable_output("{%array(1)}") + + def test_array_overlay(self): + output = single_variable_output("{%array_overlay([1, 2, 3], [4, 5])}") + assert output == [4, 5, 3] + + output = single_variable_output("{%array_overlay([1, 2, 3], [4, 5, 6, 7, 8])}") + assert output == [4, 5, 6, 7, 8] + + output = single_variable_output("{%array_overlay([1, 2, 3], [4, 5, 6, 7, 8], True)}") + assert output == [1, 2, 3, 7, 8] + + def test_array_first(self): + output = single_variable_output( + "{%array_first(['', false, null, [], {}, 0, 'hi', 'no'], 'fallback')}" + ) + assert output == "hi" + + output = single_variable_output("{%array_first(['', false, null, [], {}, 0], 'fallback')}") + assert output == "fallback" + + def test_array_apply_fixed(self): + output = ( + Script( + { + "map_test": "{ {'key1': 7, 'key2': 8, 'key3': 9} }", + "output": """{ + %array_apply_fixed( ['key1', 'key2', 'key3'], map_test, %map_get, True) + }""", + } + ) + .resolve(update=True) + .get("output") + .native + ) + assert output == [7, 8, 9] + + output = ( + Script( + { + "output": "{%array_apply_fixed( ['key1', 'key2', 'key3'], '3', %contains)}", + } + ) + .resolve(update=True) + .get("output") + .native + ) + assert output == [False, False, True] diff --git a/tests/unit/script/functions/test_boolean_functions.py b/tests/unit/script/functions/test_boolean_functions.py index aab6afdf..dfdd79c6 100644 --- a/tests/unit/script/functions/test_boolean_functions.py +++ b/tests/unit/script/functions/test_boolean_functions.py @@ -126,3 +126,17 @@ class TestBooleanFunctions: def test_not(self, value: str, expected_output: bool): output = single_variable_output(f"{{%not({value})}}") assert output == expected_output + + @pytest.mark.parametrize( + "value, expected_output", + [ + ("null", True), + ("''", True), + ("0", False), + ("{}", False), + ("'h'", False), + ], + ) + def test_is_null(self, value: str, expected_output: bool): + output = single_variable_output(f"{{%is_null({value})}}") + assert output == expected_output diff --git a/tests/unit/script/functions/test_conditional_functions.py b/tests/unit/script/functions/test_conditional_functions.py index 259c0a33..f7217915 100644 --- a/tests/unit/script/functions/test_conditional_functions.py +++ b/tests/unit/script/functions/test_conditional_functions.py @@ -1,6 +1,5 @@ import pytest - -from ytdl_sub.script.script import Script +from unit.script.conftest import single_variable_output class TestConditionalFunction: @@ -12,11 +11,12 @@ class TestConditionalFunction: ], ) def test_if_function(self, function_str: str, expected_output: bool): - output = Script({"output": function_str}).resolve(update=True).get("output").native + output = single_variable_output(function_str) assert output == expected_output def test_nested_if_function(self): - function_str = """{ + output = single_variable_output( + """{ %if( True, %if( @@ -31,5 +31,5 @@ class TestConditionalFunction: True ) }""" - output = Script({"output": function_str}).resolve(update=True).get("output").native + ) assert output == "winner" diff --git a/tests/unit/script/functions/test_error_functions.py b/tests/unit/script/functions/test_error_functions.py index 9f1092b4..be2d90c2 100644 --- a/tests/unit/script/functions/test_error_functions.py +++ b/tests/unit/script/functions/test_error_functions.py @@ -23,3 +23,83 @@ class TestErrorFunctions: def test_user_assert_passthrough_as_arg(self): output = single_variable_output("{%int(%assert('123', 'test this error message'))}") assert output == 123 + + def test_user_assert_eq(self): + output = single_variable_output( + """{ + %int( + %assert_eq( + '123', + %array_at(['123'], 0), + 'test this error message' + ) + ) + }""" + ) + assert output == 123 + + def test_user_assert_eq_raises(self): + with pytest.raises(UserThrownRuntimeError, match=re.escape("test this error message")): + single_variable_output( + """{ + %int( + %assert_eq( + '123', + %array_at(['no'], 0), + 'test this error message' + ) + ) + }""" + ) + + def test_user_assert_ne(self): + output = single_variable_output( + """{ + %int( + %assert_ne( + '123', + %array_at(['nope'], 0), + 'test this error message' + ) + ) + }""" + ) + assert output == 123 + + def test_user_assert_ne_raises(self): + with pytest.raises(UserThrownRuntimeError, match=re.escape("test this error message")): + single_variable_output( + """{ + %int( + %assert_ne( + '123', + %array_at(['123'], 0), + 'test this error message' + ) + ) + }""" + ) + + def test_user_assert_then(self): + output = single_variable_output( + """{ + %assert_then( + '123', + %array_at(['nope'], 0), + 'test this error message' + ) + }""" + ) + assert output == "nope" + + def test_user_assert_then_raises(self): + with pytest.raises(UserThrownRuntimeError, match=re.escape("test this error message")): + single_variable_output( + """{ + %assert_then( + {}, + %array_at(['nope'], 0), + 'test this error message' + ) + }""" + ) diff --git a/tests/unit/script/functions/test_string_functions.py b/tests/unit/script/functions/test_string_functions.py index 4533d5bf..9a48079a 100644 --- a/tests/unit/script/functions/test_string_functions.py +++ b/tests/unit/script/functions/test_string_functions.py @@ -107,3 +107,10 @@ class TestNumericFunctions: def test_slice(self, values, expected_output): output = single_variable_output(f"{{%slice({values})}}") assert output == expected_output + + @pytest.mark.parametrize( + "value, expected_output", [("a", True), ("nope", False), ("dog", True)] + ) + def test_contains(self, value, expected_output): + output = single_variable_output(f"{{%contains('a brown dog', '{value}')}}") + assert output == expected_output