From 71a08bdcba3efd5c072d06b2065c6c25e849c104 Mon Sep 17 00:00:00 2001 From: johnb432 <58661205+johnb432@users.noreply.github.com> Date: Tue, 24 Oct 2023 21:10:19 +0200 Subject: [PATCH] Settings - Fixes for whitespaces and comments, added unit tests (#1622) Co-authored-by: PabstMirror --- addons/settings/fnc_parse.sqf | 13 ++- addons/settings/test.sqf | 19 ++++ addons/settings/test_parse.sqf | 94 +++++++++++++++++++ addons/settings/test_settings_comments.sqf | 35 +++++++ .../settings/test_settings_comments_eof.sqf | 5 + addons/settings/test_settings_multiline.sqf | 28 ++++++ addons/settings/test_settings_regular.sqf | 10 ++ addons/settings/test_settings_strings.sqf | 25 +++++ addons/settings/test_settings_unicode.sqf | 1 + tools/sqf_validator.py | 3 + tools/sqfvmChecker.py | 27 ++---- 11 files changed, 238 insertions(+), 22 deletions(-) create mode 100644 addons/settings/test.sqf create mode 100644 addons/settings/test_parse.sqf create mode 100644 addons/settings/test_settings_comments.sqf create mode 100644 addons/settings/test_settings_comments_eof.sqf create mode 100644 addons/settings/test_settings_multiline.sqf create mode 100644 addons/settings/test_settings_regular.sqf create mode 100644 addons/settings/test_settings_strings.sqf create mode 100644 addons/settings/test_settings_unicode.sqf diff --git a/addons/settings/fnc_parse.sqf b/addons/settings/fnc_parse.sqf index 44fb7b6cf..5c7f0af99 100644 --- a/addons/settings/fnc_parse.sqf +++ b/addons/settings/fnc_parse.sqf @@ -8,6 +8,7 @@ Description: Parameters: _info - Content of file or clipboard to parse. _validate - Check if settings are valid. (optional, default: false) + _source - Can be "client", "mission" or "server" (optional, default: "") Returns: Settings with values and priority states. @@ -22,14 +23,16 @@ params [["_info", "", [""]], ["_validate", false, [false]], ["_source", "", [""] private _fnc_parseAny = { params ["_string"]; - // Remove whitespace so parseSimpleArray can handle arrays. - // Means that strings inside arrays don't support white space chars, but w/e. - // No such setting exists atm anyway. - _string = _string splitString _whitespace joinString ""; - parseSimpleArray (["[", _string, "]"] joinString "") select 0 }; +// If string comes from the "import" button, it is not preprocessed +// Therefore, remove single line comments (//) +_info = _info regexReplace ["/{2}[^\n\r]*((\n|\r)?)", ""]; + +// Remove multiline comments too (/* */) +_info = _info regexReplace ["/\*[^(\*/)]*((\*/)?)", ""]; + // Remove whitespaces at the start and end of each statement, a statement being defined by the ";" at its end private _parsed = []; diff --git a/addons/settings/test.sqf b/addons/settings/test.sqf new file mode 100644 index 000000000..de3ff16bf --- /dev/null +++ b/addons/settings/test.sqf @@ -0,0 +1,19 @@ +// ----------------------------------------------------------------------------- +// Automatically generated by 'functions_config.rb' +// DO NOT MANUALLY EDIT THIS FILE! +// ----------------------------------------------------------------------------- +#define DEBUG_MODE_FULL +#include "script_component.hpp" + +#define TESTS ["parse"] + +SCRIPT(test-settings); + +// ---------------------------------------------------------------------------- + +LOG("=== Testing Settings ==="); + +{ + private _test = execVM format ["\x\cba\addons\settings\test_%1.sqf", _x]; + waitUntil { scriptDone _test }; +} forEach TESTS; diff --git a/addons/settings/test_parse.sqf b/addons/settings/test_parse.sqf new file mode 100644 index 000000000..fbcc49b7f --- /dev/null +++ b/addons/settings/test_parse.sqf @@ -0,0 +1,94 @@ +// ---------------------------------------------------------------------------- +#define DEBUG_MODE_FULL +#include "script_component.hpp" + +SCRIPT(test_settings); + +// ---------------------------------------------------------------------------- + +private ["_funcName", "_settings", "_result"]; + +LOG('Testing Settings'); + +// UNIT TESTS (parse) +_funcName = QFUNC(parse); +TEST_DEFINED(QFUNC(parse),""); + +// Purposely weird formatting, must remain this way because newlines remain part of result +_settings = (preprocessFile "x\cba\addons\settings\test_settings_regular.sqf") call FUNC(parse); +_result = _settings isEqualTo [ + ["ace_advanced_ballistics_ammoTemperatureEnabled", true, 0], + ["ace_advanced_ballistics_barrelLengthInfluenceEnabled", false, 2], + ["ace_advanced_ballistics_bulletTraceEnabled", true, 1], + ["ace_advanced_fatigue_enabled", false, 0], + ["ace_advanced_fatigue_enableStaminaBar", true, 0], + ["ace_advanced_fatigue_performanceFactor", 1, 1], + ["ace_advanced_fatigue_terrainGradientFactor", 1, 2] +]; +TEST_TRUE(_result,_funcName); + +_settings = (preprocessFile "x\cba\addons\settings\test_settings_multiline.sqf") call FUNC(parse); +_result = _settings isEqualTo [ + ["test1", "[ + "" item_1 "", + "" item_2 "" +]", 0], + ["test2", "[ + ' item_1 ', + ' item_2 ' +]", 1], + ["test3", "[ + ' item_1 ' , "" item_2 "" +]", 2], + ["test4", " + +[ +'"" item_1 ""', +' item_2 ' +] + +", 0] +]; +TEST_TRUE(_result,_funcName); + +_settings = (preprocessFile "x\cba\addons\settings\test_settings_unicode.sqf") call FUNC(parse); +_result = _settings isEqualTo [["test1", "[Āā, Ăă, Ҙ, привет]", 1]]; +TEST_TRUE(_result,_funcName); + +_settings = (preprocessFile "x\cba\addons\settings\test_settings_strings.sqf") call FUNC(parse); +_result = _settings isEqualTo [ + ["test1", "", 0], + ["test2", "", 0], + ["test3", " T E S T " , 0], + ["test4", " T E S T ", 0], + ["test5", "[ ' t e s t ' , "" T E S T "" ]", 0], + ["test6", "[ "" t e s t "" , + +"""" T E S T """" ]", 0], + ["test7", "[ true, false ]", 0], + ["test8", "[ "" item_1 "" , "" item_2 "" ]", 0], + ["test9", "[ ' item_1 ' , ' item_2 ' ]", 0], + ["test10", "[ ' item_1 ' , "" item_2 "" ]", 0], + ["test11", "[ '"" item_1 ""' , ' item_2 ' ]", 0], + ["test12", "[ ' item_1 ' , ""' item_2 '"" ]", 0] +]; +TEST_TRUE(_result,_funcName); + +// Don't preprocess for testing comments +_settings = (loadFile "x\cba\addons\settings\test_settings_comments.sqf") call FUNC(parse); +_result = _settings isEqualTo [ + ["test2", "[true,false]", 1], + ["test4", "[ ' t e s t ' , "" T E S T "" ]", 0], + ["ace_advanced_ballistics_ammoTemperatureEnabled", true, 0], + ["ace_advanced_ballistics_barrelLengthInfluenceEnabled", true, 2], + ["ace_advanced_ballistics_bulletTraceEnabled", true, 1] +]; +TEST_TRUE(_result,_funcName); + +_settings = (loadFile "x\cba\addons\settings\test_settings_comments_eof.sqf") call FUNC(parse); +_result = _settings isEqualTo [ + ["test1", "[""item_1"",""item_2""]", 1] +]; +TEST_TRUE(_result,_funcName); + +nil diff --git a/addons/settings/test_settings_comments.sqf b/addons/settings/test_settings_comments.sqf new file mode 100644 index 000000000..6d573ccd5 --- /dev/null +++ b/addons/settings/test_settings_comments.sqf @@ -0,0 +1,35 @@ +/* +force test1 = "[""item_1"",""item_2""]"; +*/ + +force test2 = "[true,false]"; +/* +//forceforceforce = true;//"[""ACE_DAGR"",""ACE_microDAGR""]"; +*/ + +//test3 = "[Āā,Ăă,Ҙ,привет]"; +test4 = "[ ' t e s t ' , "" T E S T "" ]"; +/* +test5 = '[ " t e s t ", + + "" T E S T "" ]'; +test6 = ''; // "settings,"; +test7 = " T S T "; + */ + + +// ACE Advanced Ballistics +ace_advanced_ballistics_ammoTemperatureEnabled = true; +force force ace_advanced_ballistics_barrelLengthInfluenceEnabled = true; +force ace_advanced_ballistics_bulletTraceEnabled = true; + +// ACE Advanced Fatigue +/*/* +ace_advanced_fatigue_enabled = true; +ace_advanced_fatigue_enableStaminaBar = true; +/* +force ace_advanced_fatigue_performanceFactor = 1; +force force ace_advanced_fatigue_terrainGradientFactor = 1; +*/ + +// End of file diff --git a/addons/settings/test_settings_comments_eof.sqf b/addons/settings/test_settings_comments_eof.sqf new file mode 100644 index 000000000..2c6d820b8 --- /dev/null +++ b/addons/settings/test_settings_comments_eof.sqf @@ -0,0 +1,5 @@ +force test1 = "[""item_1"",""item_2""]"; + +// ACE Advanced Fatigue +/* +force test2 = "[true,false]"; diff --git a/addons/settings/test_settings_multiline.sqf b/addons/settings/test_settings_multiline.sqf new file mode 100644 index 000000000..0c4d340bd --- /dev/null +++ b/addons/settings/test_settings_multiline.sqf @@ -0,0 +1,28 @@ +test1 = "[ + "" item_1 "", + "" item_2 "" +]"; + +force test2 = "[ + ' item_1 ', + ' item_2 ' +]"; + +force force test3 = + +"[ + ' item_1 ' , "" item_2 "" +]" + +; + +test4 = " + +[ +'"" item_1 ""', +' item_2 ' +] + +" + +; diff --git a/addons/settings/test_settings_regular.sqf b/addons/settings/test_settings_regular.sqf new file mode 100644 index 000000000..e95b0042e --- /dev/null +++ b/addons/settings/test_settings_regular.sqf @@ -0,0 +1,10 @@ +// ACE Advanced Ballistics +ace_advanced_ballistics_ammoTemperatureEnabled = true; +force force ace_advanced_ballistics_barrelLengthInfluenceEnabled = false; +force ace_advanced_ballistics_bulletTraceEnabled = true; + +// ACE Advanced Fatigue +ace_advanced_fatigue_enabled = false; +ace_advanced_fatigue_enableStaminaBar = true; +force ace_advanced_fatigue_performanceFactor = 1; +force force ace_advanced_fatigue_terrainGradientFactor = 1; diff --git a/addons/settings/test_settings_strings.sqf b/addons/settings/test_settings_strings.sqf new file mode 100644 index 000000000..c763042c1 --- /dev/null +++ b/addons/settings/test_settings_strings.sqf @@ -0,0 +1,25 @@ +test1 = ""; + +test2 = ''; + +test3 = ' T E S T '; + +test4 = " T E S T "; + +test5 = "[ ' t e s t ' , "" T E S T "" ]"; + +test6 = '[ " t e s t " , + + "" T E S T "" ]'; + +test7 = "[ true, false ]"; + +test8 = "[ "" item_1 "" , "" item_2 "" ]"; + +test9 = "[ ' item_1 ' , ' item_2 ' ]"; + +test10 = "[ ' item_1 ' , "" item_2 "" ]"; + +test11 = "[ '"" item_1 ""' , ' item_2 ' ]"; + +test12 = "[ ' item_1 ' , ""' item_2 '"" ]"; diff --git a/addons/settings/test_settings_unicode.sqf b/addons/settings/test_settings_unicode.sqf new file mode 100644 index 000000000..a04fea305 --- /dev/null +++ b/addons/settings/test_settings_unicode.sqf @@ -0,0 +1 @@ +force test1 = "[Āā, Ăă, Ҙ, привет]"; diff --git a/tools/sqf_validator.py b/tools/sqf_validator.py index facdb1142..e41f775af 100644 --- a/tools/sqf_validator.py +++ b/tools/sqf_validator.py @@ -162,6 +162,8 @@ def main(): print("Validating SQF") + files_to_ignore_lower = [x.lower() for x in ["test_parse.sqf"]] + sqf_list = [] bad_count = 0 @@ -177,6 +179,7 @@ def main(): for root, dirnames, filenames in os.walk(rootDir + '/' + args.module): for filename in fnmatch.filter(filenames, '*.sqf'): + if filename.lower() in files_to_ignore_lower: continue sqf_list.append(os.path.join(root, filename)) for filename in sqf_list: diff --git a/tools/sqfvmChecker.py b/tools/sqfvmChecker.py index b8c652ed2..45144cdad 100644 --- a/tools/sqfvmChecker.py +++ b/tools/sqfvmChecker.py @@ -2,11 +2,12 @@ import sys import subprocess import concurrent.futures +import tomllib addon_base_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) files_to_ignore_lower = [ - x.lower() for x in ["initSettings.sqf", "initKeybinds.sqf", "XEH_PREP.sqf", "backwards_comp.sqf", "gui_createCategory.sqf"] + x.lower() for x in ["initSettings.sqf", "initKeybinds.sqf", "XEH_PREP.sqf", "backwards_comp.sqf", "gui_createCategory.sqf", "test_settings_comments.sqf", "test_settings_comments_eof.sqf", "test_settings_multiline.sqf", "test_settings_regular.sqf", "test_settings_unicode.sqf"] ] sqfvm_exe = os.path.join(addon_base_path, "sqfvm.exe") virtual_paths = [ @@ -25,22 +26,14 @@ def get_files_to_process(basePath): if file.lower() in files_to_ignore_lower: continue skipPreprocessing = False - addonTomlPath = os.path.join(root, "addon.toml") - if os.path.isfile(addonTomlPath): - with open(addonTomlPath, "r") as f: - tomlFile = f.read() - if "preprocess = false" in tomlFile: - print("'preprocess = false' not supported") - raise - skipPreprocessing = "[preprocess]\nenabled = false" in tomlFile - addonTomlPath = os.path.join(os.path.dirname(root), "addon.toml") - if os.path.isfile(addonTomlPath): - with open(addonTomlPath, "r") as f: - tomlFile = f.read() - if "preprocess = false" in tomlFile: - print("'preprocess = false' not supported") - raise - skipPreprocessing = "[preprocess]\nenabled = false" in tomlFile + for addonTomlPath in [os.path.join(root, "addon.toml"), os.path.join(os.path.dirname(root), "addon.toml")]: + if os.path.isfile(addonTomlPath): + with open(addonTomlPath, "rb") as f: + tomlFile = tomllib.load(f) + try: + skipPreprocessing = not tomlFile.get('rapify')['enabled'] + except: + pass if file == "config.cpp" and skipPreprocessing: continue # ignore configs with __has_include filePath = os.path.join(root, file)