From 748cb9e0067168f5d5709e24f7018404177b9d45 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Fri, 22 Sep 2023 00:10:45 +0200 Subject: [PATCH 01/19] Added failing tests --- tests/test_image_app_parameter.py | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/test_image_app_parameter.py diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py new file mode 100644 index 00000000..7b57b5e7 --- /dev/null +++ b/tests/test_image_app_parameter.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +""" +Test image app parameter. +""" +from tests.pyxform_test_case import PyxformTestCase + + +class ImageAppParameterTest(PyxformTestCase): + + def test_valid_android_package_name(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | + """, + xml__xpath_match=[ + "/h:html/h:body/x:upload[@intent='com.jeyluta.timestampcamerafree' and @mediatype='image/*' and @ref='/data/my_image']" + ], + ) + + def test_invalid_android_package_name(self): + self.assertPyxformXform( + name="data", + errored=True, + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | image | my_image | Image | app=something | + """, + error__contains=[ + "Invalid Android package name format" + ], + ) \ No newline at end of file From c145885d7b488870813d1af996976a29508b7856 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Fri, 22 Sep 2023 00:11:31 +0200 Subject: [PATCH 02/19] Added support for the app parameter in image questions --- pyxform/xls2json.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pyxform/xls2json.py b/pyxform/xls2json.py index 56197e90..75c2528b 100644 --- a/pyxform/xls2json.py +++ b/pyxform/xls2json.py @@ -1310,7 +1310,7 @@ def workbook_to_json( if row.get("default"): new_dict["default"] = process_image_default(row["default"]) - parameters_generic.validate(parameters=parameters, allowed=("max-pixels",)) + parameters_generic.validate(parameters=parameters, allowed=("max-pixels","app",)) if "max-pixels" in parameters.keys(): try: int(parameters["max-pixels"]) @@ -1324,6 +1324,18 @@ def workbook_to_json( (ROW_FORMAT_STRING % row_number) + " Use the max-pixels parameter to speed up submission sending and save storage space. Learn more: https://xlsform.org/#image" ) + + if "app" in parameters.keys(): + android_package_regex_pattern = "^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+[0-9a-z_]$" + app_package_name = str(parameters["app"]) + if re.fullmatch(android_package_regex_pattern, app_package_name): + new_dict["control"] = new_dict.get("control", {}) + new_dict["control"].update( + {"intent": app_package_name} + ) + else: + raise PyXFormError("Invalid Android package name format") + parent_children_array.append(new_dict) continue From 57cc0330d4a6df84334baff7c9d40d2f1092f4e9 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Fri, 22 Sep 2023 00:12:00 +0200 Subject: [PATCH 03/19] Fixed test_max_pixels.py --- tests/test_max_pixels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_max_pixels.py b/tests/test_max_pixels.py index 9a37067f..f54a0b95 100644 --- a/tests/test_max_pixels.py +++ b/tests/test_max_pixels.py @@ -46,7 +46,7 @@ def test_string_extra_params(self): | | image | my_image | Image | max-pixels=640 foo=bar | """, error__contains=[ - "Accepted parameters are 'max-pixels'. The following are invalid parameter(s): 'foo'." + "Accepted parameters are 'app, max-pixels'. The following are invalid parameter(s): 'foo'." ], ) From 4471353f27cf3cf98aaa0834e219532a1d2e6db8 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Fri, 22 Sep 2023 00:19:36 +0200 Subject: [PATCH 04/19] Formatted code --- pyxform/xls2json.py | 16 +++++++++++----- tests/test_image_app_parameter.py | 15 ++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/pyxform/xls2json.py b/pyxform/xls2json.py index 75c2528b..1ae15bb1 100644 --- a/pyxform/xls2json.py +++ b/pyxform/xls2json.py @@ -1310,7 +1310,13 @@ def workbook_to_json( if row.get("default"): new_dict["default"] = process_image_default(row["default"]) - parameters_generic.validate(parameters=parameters, allowed=("max-pixels","app",)) + parameters_generic.validate( + parameters=parameters, + allowed=( + "max-pixels", + "app", + ), + ) if "max-pixels" in parameters.keys(): try: int(parameters["max-pixels"]) @@ -1326,13 +1332,13 @@ def workbook_to_json( ) if "app" in parameters.keys(): - android_package_regex_pattern = "^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+[0-9a-z_]$" + android_package_regex_pattern = ( + "^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+[0-9a-z_]$" + ) app_package_name = str(parameters["app"]) if re.fullmatch(android_package_regex_pattern, app_package_name): new_dict["control"] = new_dict.get("control", {}) - new_dict["control"].update( - {"intent": app_package_name} - ) + new_dict["control"].update({"intent": app_package_name}) else: raise PyXFormError("Invalid Android package name format") diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py index 7b57b5e7..8fd25007 100644 --- a/tests/test_image_app_parameter.py +++ b/tests/test_image_app_parameter.py @@ -6,13 +6,12 @@ class ImageAppParameterTest(PyxformTestCase): - def test_valid_android_package_name(self): self.assertPyxformXform( name="data", md=""" - | survey | | | | | - | | type | name | label | parameters | + | survey | | | | | + | | type | name | label | parameters | | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | """, xml__xpath_match=[ @@ -25,11 +24,9 @@ def test_invalid_android_package_name(self): name="data", errored=True, md=""" - | survey | | | | | - | | type | name | label | parameters | + | survey | | | | | + | | type | name | label | parameters | | | image | my_image | Image | app=something | """, - error__contains=[ - "Invalid Android package name format" - ], - ) \ No newline at end of file + error__contains=["Invalid Android package name format"], + ) From 257d33ea376fdfb8bffa52ccfeedd62ffc7d7099 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Fri, 22 Sep 2023 02:05:00 +0200 Subject: [PATCH 05/19] Added tests to check that adding package name to draw/signature/selfie image questions is ignored --- tests/test_image_app_parameter.py | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py index 8fd25007..cc7ba5cd 100644 --- a/tests/test_image_app_parameter.py +++ b/tests/test_image_app_parameter.py @@ -30,3 +30,55 @@ def test_invalid_android_package_name(self): """, error__contains=["Invalid Android package name format"], ) + + def test_adding_android_package_name_in_annotate_image(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | | + | | type | name | label | parameters | appearance | + | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | annotate | + """, + xml__xpath_match=[ + "/h:html/h:body/x:upload[@intent='com.jeyluta.timestampcamerafree' and @mediatype='image/*' and @ref='/data/my_image']" + ], + ) + + def test_ignoring_android_package_name_in_signature_image(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | | + | | type | name | label | parameters | appearance | + | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | signature | + """, + xml__xpath_match=[ + "/h:html/h:body/x:upload[@mediatype='image/*' and @ref='/data/my_image']" + ], + ) + + def test_ignoring_valid_android_package_name_in_draw_image(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | | + | | type | name | label | parameters | appearance | + | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | draw | + """, + xml__xpath_match=[ + "/h:html/h:body/x:upload[@mediatype='image/*' and @ref='/data/my_image']" + ], + ) + + def test_ignoring_valid_android_package_name_in_draw_image(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | | + | | type | name | label | parameters | appearance | + | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | new-front | + """, + xml__xpath_match=[ + "/h:html/h:body/x:upload[@mediatype='image/*' and @ref='/data/my_image']" + ], + ) From 3b7d7c833df451ea504d455bdfb5f0d8864abf81 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Fri, 22 Sep 2023 02:06:08 +0200 Subject: [PATCH 06/19] Ignore adding package name to draw/signature/selfie image questions --- pyxform/xls2json.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/pyxform/xls2json.py b/pyxform/xls2json.py index 1ae15bb1..d107bf0d 100644 --- a/pyxform/xls2json.py +++ b/pyxform/xls2json.py @@ -1332,15 +1332,17 @@ def workbook_to_json( ) if "app" in parameters.keys(): - android_package_regex_pattern = ( - "^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+[0-9a-z_]$" - ) - app_package_name = str(parameters["app"]) - if re.fullmatch(android_package_regex_pattern, app_package_name): - new_dict["control"] = new_dict.get("control", {}) - new_dict["control"].update({"intent": app_package_name}) - else: - raise PyXFormError("Invalid Android package name format") + appearance = row.get("control", {}).get("appearance") + if appearance is None or appearance == "annotate": + android_package_regex_pattern = ( + "^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+[0-9a-z_]$" + ) + app_package_name = str(parameters["app"]) + if re.fullmatch(android_package_regex_pattern, app_package_name): + new_dict["control"] = new_dict.get("control", {}) + new_dict["control"].update({"intent": app_package_name}) + else: + raise PyXFormError("Invalid Android package name format.") parent_children_array.append(new_dict) continue From feb5944c87f9a086d8c76ab1ff071de0b2846f37 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Fri, 22 Sep 2023 13:15:27 +0200 Subject: [PATCH 07/19] Fixed test names --- tests/test_image_app_parameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py index cc7ba5cd..e9fb0ef3 100644 --- a/tests/test_image_app_parameter.py +++ b/tests/test_image_app_parameter.py @@ -57,7 +57,7 @@ def test_ignoring_android_package_name_in_signature_image(self): ], ) - def test_ignoring_valid_android_package_name_in_draw_image(self): + def test_ignoring_android_package_name_in_draw_image(self): self.assertPyxformXform( name="data", md=""" @@ -70,7 +70,7 @@ def test_ignoring_valid_android_package_name_in_draw_image(self): ], ) - def test_ignoring_valid_android_package_name_in_draw_image(self): + def test_ignoring_android_package_name_in_selfie_image(self): self.assertPyxformXform( name="data", md=""" From c83101794287b0b1938f15b499e48b11874b2f45 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Mon, 25 Sep 2023 13:47:07 +0200 Subject: [PATCH 08/19] Improved tests --- tests/test_image_app_parameter.py | 127 ++++++++++++------------------ 1 file changed, 51 insertions(+), 76 deletions(-) diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py index e9fb0ef3..afecfdca 100644 --- a/tests/test_image_app_parameter.py +++ b/tests/test_image_app_parameter.py @@ -4,81 +4,56 @@ """ from tests.pyxform_test_case import PyxformTestCase +class TestImageAppParameter(PyxformTestCase): + def test_adding_valid_android_package_name_in_image_with_supported_appearances(self): + appearances = ("", "annotate") + md = """ + | survey | | | | | | + | | type | name | label | parameters | appearance | + | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | {case} | + """ + for case in appearances: + with self.subTest(msg=case): + self.assertPyxformXform( + name="data", + md=md.format(case=case), + xml__xpath_match=[ + "/h:html/h:body/x:upload[@intent='com.jeyluta.timestampcamerafree' and @mediatype='image/*' and @ref='/data/my_image']" + ], + ) -class ImageAppParameterTest(PyxformTestCase): - def test_valid_android_package_name(self): - self.assertPyxformXform( - name="data", - md=""" - | survey | | | | | - | | type | name | label | parameters | - | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | - """, - xml__xpath_match=[ - "/h:html/h:body/x:upload[@intent='com.jeyluta.timestampcamerafree' and @mediatype='image/*' and @ref='/data/my_image']" - ], - ) + def test_throwing_error_when_invalid_android_package_name_is_used(self): + appearances = ("", "annotate") + md = """ + | survey | | | | | | + | | type | name | label | parameters | appearance | + | | image | my_image | Image | app=something | {case} | + """ + error__contains = (["Invalid Android package name format"],) + for case in appearances: + with self.subTest(msg=case): + self.assertPyxformXform( + name="data", + errored=True, + md=md.format(case=case), + xml__xpath_match=[ + "/h:html/h:body/x:upload[not(@intent) and @mediatype='image/*' and @ref='/data/my_image']" + ], + ) - def test_invalid_android_package_name(self): - self.assertPyxformXform( - name="data", - errored=True, - md=""" - | survey | | | | | - | | type | name | label | parameters | - | | image | my_image | Image | app=something | - """, - error__contains=["Invalid Android package name format"], - ) - - def test_adding_android_package_name_in_annotate_image(self): - self.assertPyxformXform( - name="data", - md=""" - | survey | | | | | | - | | type | name | label | parameters | appearance | - | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | annotate | - """, - xml__xpath_match=[ - "/h:html/h:body/x:upload[@intent='com.jeyluta.timestampcamerafree' and @mediatype='image/*' and @ref='/data/my_image']" - ], - ) - - def test_ignoring_android_package_name_in_signature_image(self): - self.assertPyxformXform( - name="data", - md=""" - | survey | | | | | | - | | type | name | label | parameters | appearance | - | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | signature | - """, - xml__xpath_match=[ - "/h:html/h:body/x:upload[@mediatype='image/*' and @ref='/data/my_image']" - ], - ) - - def test_ignoring_android_package_name_in_draw_image(self): - self.assertPyxformXform( - name="data", - md=""" - | survey | | | | | | - | | type | name | label | parameters | appearance | - | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | draw | - """, - xml__xpath_match=[ - "/h:html/h:body/x:upload[@mediatype='image/*' and @ref='/data/my_image']" - ], - ) - - def test_ignoring_android_package_name_in_selfie_image(self): - self.assertPyxformXform( - name="data", - md=""" - | survey | | | | | | - | | type | name | label | parameters | appearance | - | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | new-front | - """, - xml__xpath_match=[ - "/h:html/h:body/x:upload[@mediatype='image/*' and @ref='/data/my_image']" - ], - ) + def test_ignoring_android_package_name_in_image_with_not_supported_appearances(self): + appearances = ("signature", "draw", "new-front") + md = """ + | survey | | | | | | + | | type | name | label | parameters | appearance | + | | image | my_image | Image | app=com.jeyluta.timestampcamerafree | {case} | + """ + for case in appearances: + with self.subTest(msg=case): + self.assertPyxformXform( + name="data", + md=md.format(case=case), + xml__xpath_match=[ + "/h:html/h:body/x:upload[not(@intent) and @mediatype='image/*' and @ref='/data/my_image']" + ], + ) From af0764cd61060c922f0e86f2d66e3c81dc6684bc Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Mon, 25 Sep 2023 13:57:36 +0200 Subject: [PATCH 09/19] Include the row number in the error message --- pyxform/xls2json.py | 5 ++++- tests/test_image_app_parameter.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pyxform/xls2json.py b/pyxform/xls2json.py index d107bf0d..771f815d 100644 --- a/pyxform/xls2json.py +++ b/pyxform/xls2json.py @@ -1342,7 +1342,10 @@ def workbook_to_json( new_dict["control"] = new_dict.get("control", {}) new_dict["control"].update({"intent": app_package_name}) else: - raise PyXFormError("Invalid Android package name format.") + raise PyXFormError( + (ROW_FORMAT_STRING % row_number) + + " Invalid Android package name format." + ) parent_children_array.append(new_dict) continue diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py index afecfdca..9f87c49c 100644 --- a/tests/test_image_app_parameter.py +++ b/tests/test_image_app_parameter.py @@ -29,7 +29,7 @@ def test_throwing_error_when_invalid_android_package_name_is_used(self): | | type | name | label | parameters | appearance | | | image | my_image | Image | app=something | {case} | """ - error__contains = (["Invalid Android package name format"],) + error__contains = (["[row = 2] Invalid Android package name format"],) for case in appearances: with self.subTest(msg=case): self.assertPyxformXform( From a6e28745c34ba259602e59d5ca744c7a1831bffe Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Tue, 3 Oct 2023 22:55:35 +0200 Subject: [PATCH 10/19] Improved validating android package names --- .../pyxform/android_package_name.py | 31 +++++++++ pyxform/xls2json.py | 10 ++- tests/test_image_app_parameter.py | 26 +++++++- .../pyxform/test_android_package_name.py | 63 +++++++++++++++++++ 4 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 pyxform/validators/pyxform/android_package_name.py create mode 100644 tests/validators/pyxform/test_android_package_name.py diff --git a/pyxform/validators/pyxform/android_package_name.py b/pyxform/validators/pyxform/android_package_name.py new file mode 100644 index 00000000..aa4c8716 --- /dev/null +++ b/pyxform/validators/pyxform/android_package_name.py @@ -0,0 +1,31 @@ +import re + + +def validate_android_package_name(name: str) -> str | None: + prefix = "Invalid Android package name - " + + if not name.strip(): + return f"{prefix}package name is missing." + + if "." not in name: + return f"{prefix}the package name must have at least one '.' separator." + + if name[-1] == ".": + return f"{prefix}the package name cannot end in a '.' separator." + + segments = name.split(".") + if any(segment == "" for segment in segments): + return f"{prefix}package segments must be of non-zero length." + + if any(segment.startswith("_") for segment in segments): + return f"{prefix}the character '_' cannot be the first character in a package name segment." + + if any(segment[0].isdigit() for segment in segments): + return f"{prefix}a digit cannot be the first character in a package name segment." + + pattern = re.compile(r"[^a-zA-Z0-9._]") + for segment in segments: + if pattern.search(segment): + return f"{prefix}the package name contains not allowed characters." + + return None diff --git a/pyxform/xls2json.py b/pyxform/xls2json.py index 771f815d..a3a848a0 100644 --- a/pyxform/xls2json.py +++ b/pyxform/xls2json.py @@ -24,6 +24,7 @@ from pyxform.errors import PyXFormError from pyxform.utils import PYXFORM_REFERENCE_REGEX, default_is_dynamic from pyxform.validators.pyxform import parameters_generic, select_from_file_params +from pyxform.validators.pyxform.android_package_name import validate_android_package_name from pyxform.validators.pyxform.translations_checks import SheetTranslations from pyxform.xls2json_backends import csv_to_dict, xls_to_dict, xlsx_to_dict from pyxform.xlsparseutils import find_sheet_misspellings, is_valid_xml_tag @@ -1334,17 +1335,14 @@ def workbook_to_json( if "app" in parameters.keys(): appearance = row.get("control", {}).get("appearance") if appearance is None or appearance == "annotate": - android_package_regex_pattern = ( - "^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+[0-9a-z_]$" - ) app_package_name = str(parameters["app"]) - if re.fullmatch(android_package_regex_pattern, app_package_name): + validation_result = validate_android_package_name(app_package_name) + if validation_result is None: new_dict["control"] = new_dict.get("control", {}) new_dict["control"].update({"intent": app_package_name}) else: raise PyXFormError( - (ROW_FORMAT_STRING % row_number) - + " Invalid Android package name format." + (ROW_FORMAT_STRING % row_number) + " " + validation_result ) parent_children_array.append(new_dict) diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py index 9f87c49c..a884ff9b 100644 --- a/tests/test_image_app_parameter.py +++ b/tests/test_image_app_parameter.py @@ -4,6 +4,7 @@ """ from tests.pyxform_test_case import PyxformTestCase + class TestImageAppParameter(PyxformTestCase): def test_adding_valid_android_package_name_in_image_with_supported_appearances(self): appearances = ("", "annotate") @@ -22,19 +23,40 @@ def test_adding_valid_android_package_name_in_image_with_supported_appearances(s ], ) - def test_throwing_error_when_invalid_android_package_name_is_used(self): + def test_throwing_error_when_invalid_android_package_name_is_used_with_supported_appearances( + self, + ): appearances = ("", "annotate") md = """ | survey | | | | | | | | type | name | label | parameters | appearance | | | image | my_image | Image | app=something | {case} | """ - error__contains = (["[row = 2] Invalid Android package name format"],) for case in appearances: with self.subTest(msg=case): self.assertPyxformXform( name="data", errored=True, + error__contains="[row : 2] Invalid Android package name - the package name must have at least one '.' separator.", + md=md.format(case=case), + xml__xpath_match=[ + "/h:html/h:body/x:upload[not(@intent) and @mediatype='image/*' and @ref='/data/my_image']" + ], + ) + + def test_ignoring_invalid_android_package_name_is_used_with_not_supported_appearances( + self, + ): + appearances = ("signature", "draw", "new-front") + md = """ + | survey | | | | | | + | | type | name | label | parameters | appearance | + | | image | my_image | Image | app=something | {case} | + """ + for case in appearances: + with self.subTest(msg=case): + self.assertPyxformXform( + name="data", md=md.format(case=case), xml__xpath_match=[ "/h:html/h:body/x:upload[not(@intent) and @mediatype='image/*' and @ref='/data/my_image']" diff --git a/tests/validators/pyxform/test_android_package_name.py b/tests/validators/pyxform/test_android_package_name.py new file mode 100644 index 00000000..0b7e402b --- /dev/null +++ b/tests/validators/pyxform/test_android_package_name.py @@ -0,0 +1,63 @@ +from pyxform.validators.pyxform.android_package_name import validate_android_package_name +from tests.pyxform_test_case import PyxformTestCase + + +class TestAndroidPackageNameValidator(PyxformTestCase): + def test_empty_package_name(self): + result = validate_android_package_name("") + self.assertEqual( + result, "Invalid Android package name - package name is missing." + ) + + def test_blank_package_name(self): + result = validate_android_package_name(" ") + self.assertEqual( + result, "Invalid Android package name - package name is missing." + ) + + def test_missing_separator(self): + result = validate_android_package_name("comexampleapp") + self.assertEqual( + result, + "Invalid Android package name - the package name must have at least one '.' separator.", + ) + + def test_invalid_start_with_underscore(self): + result = validate_android_package_name("_com.example.app") + expected_error = "Invalid Android package name - the character '_' cannot be the first character in a package name segment." + self.assertEqual(result, expected_error) + + def test_invalid_start_with_digit(self): + result = validate_android_package_name("1com.example.app") + expected_error = "Invalid Android package name - a digit cannot be the first character in a package name segment." + self.assertEqual(result, expected_error) + + def test_invalid_character(self): + result = validate_android_package_name("com.example.app$") + expected_error = "Invalid Android package name - the package name contains not allowed characters." + self.assertEqual(result, expected_error) + + def test_package_name_segment_with_zero_length(self): + result = validate_android_package_name("com..app") + expected_error = ( + "Invalid Android package name - package segments must be of non-zero length." + ) + self.assertEqual(result, expected_error) + + def test_separator_as_last_char_in_package_name(self): + result = validate_android_package_name("com.example.app.") + expected_error = "Invalid Android package name - the package name cannot end in a '.' separator." + self.assertEqual(result, expected_error) + + def test_valid_package_name(self): + package_names = ( + "com.zenstudios.zenpinball", + "com.outfit7.talkingtom", + "com.zeptolab.ctr2.f2p.google", + "com.ea.game.pvzfree_row", + "com.rovio.angrybirdsspace.premium", + ) + + for case in package_names: + result = validate_android_package_name(case) + self.assertIsNone(result) From 8e062069b6766f7977f553456940c7a733d2377f Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Wed, 4 Oct 2023 15:25:37 +0200 Subject: [PATCH 11/19] Do not use the new union operator --- pyxform/validators/pyxform/android_package_name.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyxform/validators/pyxform/android_package_name.py b/pyxform/validators/pyxform/android_package_name.py index aa4c8716..931adfba 100644 --- a/pyxform/validators/pyxform/android_package_name.py +++ b/pyxform/validators/pyxform/android_package_name.py @@ -1,7 +1,8 @@ import re +from typing import Optional -def validate_android_package_name(name: str) -> str | None: +def validate_android_package_name(name: str) -> Optional[str]: prefix = "Invalid Android package name - " if not name.strip(): From d101bb5e7470171403a1e5848cf4c16adbb9a3a9 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Sat, 7 Oct 2023 18:43:34 +0200 Subject: [PATCH 12/19] Moved mx-pixels tests to TestImageAppParameter class --- tests/test_image_app_parameter.py | 58 +++++++++++++++++++++++++- tests/test_max_pixels.py | 67 ------------------------------- 2 files changed, 57 insertions(+), 68 deletions(-) delete mode 100644 tests/test_max_pixels.py diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py index a884ff9b..91225d69 100644 --- a/tests/test_image_app_parameter.py +++ b/tests/test_image_app_parameter.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -Test image app parameter. +Test image max-pixels and app parameters. """ from tests.pyxform_test_case import PyxformTestCase @@ -79,3 +79,59 @@ def test_ignoring_android_package_name_in_image_with_not_supported_appearances(s "/h:html/h:body/x:upload[not(@intent) and @mediatype='image/*' and @ref='/data/my_image']" ], ) + + def test_integer_max_pixels(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | image | my_image | Image | max-pixels=640 | + """, + xml__contains=[ + 'xmlns:orx="http://openrosa.org/xforms"', + '', + ], + ) + + def test_string_max_pixels(self): + self.assertPyxformXform( + name="data", + errored=True, + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | image | my_image | Image | max-pixels=foo | + """, + error__contains=["Parameter max-pixels must have an integer value."], + ) + + def test_string_extra_params(self): + self.assertPyxformXform( + name="data", + errored=True, + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | image | my_image | Image | max-pixels=640 foo=bar | + """, + error__contains=[ + "Accepted parameters are 'app, max-pixels'. The following are invalid parameter(s): 'foo'." + ], + ) + + def test_image_with_no_max_pixels_should_warn(self): + warnings = [] + + self.md_to_pyxform_survey( + """ + | survey | | | | + | | type | name | label | + | | image | my_image | Image | + | | image | my_image_1 | Image 1 | + """, + warnings=warnings, + ) + + self.assertTrue(len(warnings) == 2) + self.assertTrue("max-pixels" in warnings[0] and "max-pixels" in warnings[1]) diff --git a/tests/test_max_pixels.py b/tests/test_max_pixels.py deleted file mode 100644 index f54a0b95..00000000 --- a/tests/test_max_pixels.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Test image max-pixel parameters. -""" -from tests.pyxform_test_case import PyxformTestCase - - -class MaxPixelsTest(PyxformTestCase): - """ - Test image max-pixel parameters. - """ - - def test_integer_max_pixels(self): - self.assertPyxformXform( - name="data", - md=""" - | survey | | | | | - | | type | name | label | parameters | - | | image | my_image | Image | max-pixels=640 | - """, - xml__contains=[ - 'xmlns:orx="http://openrosa.org/xforms"', - '', - ], - ) - - def test_string_max_pixels(self): - self.assertPyxformXform( - name="data", - errored=True, - md=""" - | survey | | | | | - | | type | name | label | parameters | - | | image | my_image | Image | max-pixels=foo | - """, - error__contains=["Parameter max-pixels must have an integer value."], - ) - - def test_string_extra_params(self): - self.assertPyxformXform( - name="data", - errored=True, - md=""" - | survey | | | | | - | | type | name | label | parameters | - | | image | my_image | Image | max-pixels=640 foo=bar | - """, - error__contains=[ - "Accepted parameters are 'app, max-pixels'. The following are invalid parameter(s): 'foo'." - ], - ) - - def test_image_with_no_max_pixels_should_warn(self): - warnings = [] - - self.md_to_pyxform_survey( - """ - | survey | | | | - | | type | name | label | - | | image | my_image | Image | - | | image | my_image_1 | Image 1 | - """, - warnings=warnings, - ) - - self.assertTrue(len(warnings) == 2) - self.assertTrue("max-pixels" in warnings[0] and "max-pixels" in warnings[1]) From c7946ca09b8bc3995c278709213589cff253012b Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Sat, 7 Oct 2023 18:44:04 +0200 Subject: [PATCH 13/19] Naming improvements --- tests/test_image_app_parameter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py index 91225d69..86d3a55b 100644 --- a/tests/test_image_app_parameter.py +++ b/tests/test_image_app_parameter.py @@ -5,7 +5,7 @@ from tests.pyxform_test_case import PyxformTestCase -class TestImageAppParameter(PyxformTestCase): +class TestImageParameters(PyxformTestCase): def test_adding_valid_android_package_name_in_image_with_supported_appearances(self): appearances = ("", "annotate") md = """ From 5613b2eb986141181b3e5bee457b84a484891ca9 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Sat, 7 Oct 2023 18:44:57 +0200 Subject: [PATCH 14/19] Test using max-pixels and app parameters together --- tests/test_image_app_parameter.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py index 86d3a55b..e28effb7 100644 --- a/tests/test_image_app_parameter.py +++ b/tests/test_image_app_parameter.py @@ -135,3 +135,20 @@ def test_image_with_no_max_pixels_should_warn(self): self.assertTrue(len(warnings) == 2) self.assertTrue("max-pixels" in warnings[0] and "max-pixels" in warnings[1]) + + def test_max_pixels_and_app(self): + self.assertPyxformXform( + name="data", + md=""" + | survey | | | | | + | | type | name | label | parameters | + | | image | my_image | Image | max-pixels=640 app=com.jeyluta.timestampcamerafree | + """, + xml__contains=[ + 'xmlns:orx="http://openrosa.org/xforms"', + '', + ], + xml__xpath_match=[ + "/h:html/h:body/x:upload[@intent='com.jeyluta.timestampcamerafree' and @mediatype='image/*' and @ref='/data/my_image']" + ], + ) From 89ce8b4c04d96fb0bc637e8c4e665c9575a2f120 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Sun, 29 Oct 2023 01:06:40 +0200 Subject: [PATCH 15/19] Improved the error message prefix --- .../validators/pyxform/android_package_name.py | 2 +- tests/test_image_app_parameter.py | 2 +- .../pyxform/test_android_package_name.py | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pyxform/validators/pyxform/android_package_name.py b/pyxform/validators/pyxform/android_package_name.py index 931adfba..69185b96 100644 --- a/pyxform/validators/pyxform/android_package_name.py +++ b/pyxform/validators/pyxform/android_package_name.py @@ -3,7 +3,7 @@ def validate_android_package_name(name: str) -> Optional[str]: - prefix = "Invalid Android package name - " + prefix = "Parameter 'app' has an invalid Android package name - " if not name.strip(): return f"{prefix}package name is missing." diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py index e28effb7..6ded260e 100644 --- a/tests/test_image_app_parameter.py +++ b/tests/test_image_app_parameter.py @@ -37,7 +37,7 @@ def test_throwing_error_when_invalid_android_package_name_is_used_with_supported self.assertPyxformXform( name="data", errored=True, - error__contains="[row : 2] Invalid Android package name - the package name must have at least one '.' separator.", + error__contains="[row : 2] Parameter 'app' has an invalid Android package name - the package name must have at least one '.' separator.", md=md.format(case=case), xml__xpath_match=[ "/h:html/h:body/x:upload[not(@intent) and @mediatype='image/*' and @ref='/data/my_image']" diff --git a/tests/validators/pyxform/test_android_package_name.py b/tests/validators/pyxform/test_android_package_name.py index 0b7e402b..6e85ce19 100644 --- a/tests/validators/pyxform/test_android_package_name.py +++ b/tests/validators/pyxform/test_android_package_name.py @@ -6,47 +6,47 @@ class TestAndroidPackageNameValidator(PyxformTestCase): def test_empty_package_name(self): result = validate_android_package_name("") self.assertEqual( - result, "Invalid Android package name - package name is missing." + result, "Parameter 'app' has an invalid Android package name - package name is missing." ) def test_blank_package_name(self): result = validate_android_package_name(" ") self.assertEqual( - result, "Invalid Android package name - package name is missing." + result, "Parameter 'app' has an invalid Android package name - package name is missing." ) def test_missing_separator(self): result = validate_android_package_name("comexampleapp") self.assertEqual( result, - "Invalid Android package name - the package name must have at least one '.' separator.", + "Parameter 'app' has an invalid Android package name - the package name must have at least one '.' separator.", ) def test_invalid_start_with_underscore(self): result = validate_android_package_name("_com.example.app") - expected_error = "Invalid Android package name - the character '_' cannot be the first character in a package name segment." + expected_error = "Parameter 'app' has an invalid Android package name - the character '_' cannot be the first character in a package name segment." self.assertEqual(result, expected_error) def test_invalid_start_with_digit(self): result = validate_android_package_name("1com.example.app") - expected_error = "Invalid Android package name - a digit cannot be the first character in a package name segment." + expected_error = "Parameter 'app' has an invalid Android package name - a digit cannot be the first character in a package name segment." self.assertEqual(result, expected_error) def test_invalid_character(self): result = validate_android_package_name("com.example.app$") - expected_error = "Invalid Android package name - the package name contains not allowed characters." + expected_error = "Parameter 'app' has an invalid Android package name - the package name contains not allowed characters." self.assertEqual(result, expected_error) def test_package_name_segment_with_zero_length(self): result = validate_android_package_name("com..app") expected_error = ( - "Invalid Android package name - package segments must be of non-zero length." + "Parameter 'app' has an invalid Android package name - package segments must be of non-zero length." ) self.assertEqual(result, expected_error) def test_separator_as_last_char_in_package_name(self): result = validate_android_package_name("com.example.app.") - expected_error = "Invalid Android package name - the package name cannot end in a '.' separator." + expected_error = "Parameter 'app' has an invalid Android package name - the package name cannot end in a '.' separator." self.assertEqual(result, expected_error) def test_valid_package_name(self): From dab912868edb9ce63a18b698e078530c632ffccf Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Sun, 29 Oct 2023 01:21:12 +0200 Subject: [PATCH 16/19] Improved the error message displayed when there are not allowed characters --- pyxform/validators/pyxform/android_package_name.py | 2 +- tests/validators/pyxform/test_android_package_name.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyxform/validators/pyxform/android_package_name.py b/pyxform/validators/pyxform/android_package_name.py index 69185b96..9507b072 100644 --- a/pyxform/validators/pyxform/android_package_name.py +++ b/pyxform/validators/pyxform/android_package_name.py @@ -27,6 +27,6 @@ def validate_android_package_name(name: str) -> Optional[str]: pattern = re.compile(r"[^a-zA-Z0-9._]") for segment in segments: if pattern.search(segment): - return f"{prefix}the package name contains not allowed characters." + return f"{prefix}the package name can only include letters (a-z, A-Z), numbers (0-9), dots (.), and underscores (_)." return None diff --git a/tests/validators/pyxform/test_android_package_name.py b/tests/validators/pyxform/test_android_package_name.py index 6e85ce19..e348a7c5 100644 --- a/tests/validators/pyxform/test_android_package_name.py +++ b/tests/validators/pyxform/test_android_package_name.py @@ -34,7 +34,7 @@ def test_invalid_start_with_digit(self): def test_invalid_character(self): result = validate_android_package_name("com.example.app$") - expected_error = "Parameter 'app' has an invalid Android package name - the package name contains not allowed characters." + expected_error = "Parameter 'app' has an invalid Android package name - the package name can only include letters (a-z, A-Z), numbers (0-9), dots (.), and underscores (_)." self.assertEqual(result, expected_error) def test_package_name_segment_with_zero_length(self): From ba747cd1fa044483a0fdfb9d93fa8f4ef81bcba7 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Sun, 29 Oct 2023 01:25:48 +0200 Subject: [PATCH 17/19] Compile the regex at module level --- pyxform/validators/pyxform/android_package_name.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyxform/validators/pyxform/android_package_name.py b/pyxform/validators/pyxform/android_package_name.py index 9507b072..b1c0cd03 100644 --- a/pyxform/validators/pyxform/android_package_name.py +++ b/pyxform/validators/pyxform/android_package_name.py @@ -1,6 +1,8 @@ import re from typing import Optional +PACKAGE_NAME_REGEX = re.compile(r"[^a-zA-Z0-9._]") + def validate_android_package_name(name: str) -> Optional[str]: prefix = "Parameter 'app' has an invalid Android package name - " @@ -24,9 +26,8 @@ def validate_android_package_name(name: str) -> Optional[str]: if any(segment[0].isdigit() for segment in segments): return f"{prefix}a digit cannot be the first character in a package name segment." - pattern = re.compile(r"[^a-zA-Z0-9._]") for segment in segments: - if pattern.search(segment): + if PACKAGE_NAME_REGEX.search(segment): return f"{prefix}the package name can only include letters (a-z, A-Z), numbers (0-9), dots (.), and underscores (_)." return None From 23f35ffa542b87dd24666440edd41384c7e8d5f2 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Sun, 29 Oct 2023 01:31:35 +0200 Subject: [PATCH 18/19] Reformated file --- tests/validators/pyxform/test_android_package_name.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/validators/pyxform/test_android_package_name.py b/tests/validators/pyxform/test_android_package_name.py index e348a7c5..e1f7f290 100644 --- a/tests/validators/pyxform/test_android_package_name.py +++ b/tests/validators/pyxform/test_android_package_name.py @@ -6,13 +6,15 @@ class TestAndroidPackageNameValidator(PyxformTestCase): def test_empty_package_name(self): result = validate_android_package_name("") self.assertEqual( - result, "Parameter 'app' has an invalid Android package name - package name is missing." + result, + "Parameter 'app' has an invalid Android package name - package name is missing.", ) def test_blank_package_name(self): result = validate_android_package_name(" ") self.assertEqual( - result, "Parameter 'app' has an invalid Android package name - package name is missing." + result, + "Parameter 'app' has an invalid Android package name - package name is missing.", ) def test_missing_separator(self): @@ -39,9 +41,7 @@ def test_invalid_character(self): def test_package_name_segment_with_zero_length(self): result = validate_android_package_name("com..app") - expected_error = ( - "Parameter 'app' has an invalid Android package name - package segments must be of non-zero length." - ) + expected_error = "Parameter 'app' has an invalid Android package name - package segments must be of non-zero length." self.assertEqual(result, expected_error) def test_separator_as_last_char_in_package_name(self): From cbac91ceb3ddce0c16fb6d5e64637fe74d0d0d98 Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Tue, 31 Oct 2023 13:40:43 +0100 Subject: [PATCH 19/19] Improved tests --- tests/test_image_app_parameter.py | 61 +++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/tests/test_image_app_parameter.py b/tests/test_image_app_parameter.py index 6ded260e..6f6324f5 100644 --- a/tests/test_image_app_parameter.py +++ b/tests/test_image_app_parameter.py @@ -27,24 +27,53 @@ def test_throwing_error_when_invalid_android_package_name_is_used_with_supported self, ): appearances = ("", "annotate") + parameters = ("app=something", "app=_") md = """ - | survey | | | | | | - | | type | name | label | parameters | appearance | - | | image | my_image | Image | app=something | {case} | - """ - for case in appearances: - with self.subTest(msg=case): - self.assertPyxformXform( - name="data", - errored=True, - error__contains="[row : 2] Parameter 'app' has an invalid Android package name - the package name must have at least one '.' separator.", - md=md.format(case=case), - xml__xpath_match=[ - "/h:html/h:body/x:upload[not(@intent) and @mediatype='image/*' and @ref='/data/my_image']" - ], - ) + | survey | | | | | | + | | type | name | label | parameters | appearance | + | | image | my_image | Image | {parameter} | {appearance} | + """ + for appearance in appearances: + for parameter in parameters: + with self.subTest(msg=f"{appearance} - {parameter}"): + self.assertPyxformXform( + name="data", + errored=True, + error__contains=[ + "[row : 2] Parameter 'app' has an invalid Android package name - the package name must have at least one '.' separator." + ], + md=md.format(parameter=parameter, appearance=appearance), + xml__xpath_match=[ + "/h:html/h:body/x:upload[not(@intent) and @mediatype='image/*' and @ref='/data/my_image']" + ], + ) + + def test_throwing_error_when_blank_android_package_name_is_used_with_supported_appearances( + self, + ): + appearances = ("", "annotate") + parameters = ("app=", "app= ") + md = """ + | survey | | | | | | + | | type | name | label | parameters | appearance | + | | image | my_image | Image | {parameter} | {appearance} | + """ + for appearance in appearances: + for parameter in parameters: + with self.subTest(msg=f"{appearance} - {parameter}"): + self.assertPyxformXform( + name="data", + errored=True, + error__contains=[ + "[row : 2] Parameter 'app' has an invalid Android package name - package name is missing." + ], + md=md.format(parameter=parameter, appearance=appearance), + xml__xpath_match=[ + "/h:html/h:body/x:upload[not(@intent) and @mediatype='image/*' and @ref='/data/my_image']" + ], + ) - def test_ignoring_invalid_android_package_name_is_used_with_not_supported_appearances( + def test_ignoring_invalid_android_package_name_with_not_supported_appearances( self, ): appearances = ("signature", "draw", "new-front")