From ea5285cf49f401eb1e7d9d9b0778c6e32e38b1a9 Mon Sep 17 00:00:00 2001 From: Niclas Wesemann Date: Thu, 18 Jan 2024 11:01:31 +0100 Subject: [PATCH 1/8] Update upstream (#8) * to lowercase before patch Signed-off-by: Adnan Bekan * Binary parser bug fix. Signed-off-by: Ulf Bjorkengren --------- Signed-off-by: Adnan Bekan Signed-off-by: Ulf Bjorkengren Co-authored-by: Adnan Bekan Co-authored-by: Ulf Bjorkengren --- binary/c_parser/cparserlib.c | 21 ++++++++++++++-- binary/c_parser/cparserlib.h | 2 ++ binary/go_parser/parserlib/parser.go | 20 +++++++++++++-- .../test_static_uids/test_static_uids.py | 10 ++++---- tests/vspec/test_static_uids/validation.yaml | 22 ++++++++-------- .../validation_vspecs/validation.vspec | 25 ++++++++++--------- .../validation_semantic_change.vspec | 24 ++++++++++-------- vspec/utils/idgen_utils.py | 2 +- 8 files changed, 82 insertions(+), 44 deletions(-) diff --git a/binary/c_parser/cparserlib.c b/binary/c_parser/cparserlib.c index 17f9f73a..95302236 100644 --- a/binary/c_parser/cparserlib.c +++ b/binary/c_parser/cparserlib.c @@ -50,7 +50,24 @@ typedef struct SearchContext_t { FILE* listFp; } SearchContext_t; -uint8_t validationMatrix[4][4] = {{1,2,11,12}, {2,2,12,12}, {11,12,11,12}, {12,12,12,12}}; +// Access control values: none=0, write-only=1. read-write=2, consent +=10 +// matrix preserving inherited value with read-write having priority over write-only and consent over no consent +uint8_t validationMatrix[5][5] = {{0,1,2,11,12}, {1,1,2,11,12}, {2,2,2,12,12}, {11,11,12,11,12}, {12,12,12,12,12}}; + +uint8_t getMaxValidation(uint8_t newValidation, uint8_t currentMaxValidation) { + return validationMatrix[translateToMatrixIndex(newValidation)][translateToMatrixIndex(currentMaxValidation)]; +} + +uint8_t translateToMatrixIndex(uint8_t index) { + switch (index) { + case 0: return 0; + case 1: return 1; + case 2: return 2; + case 11: return 3; + case 12: return 4; + } + return 0; +} void initReadMetadata() { readTreeMetadata.currentDepth = 0; @@ -219,7 +236,7 @@ int saveMatchingNode(long thisNode, SearchContext_t* context, bool* done) { if (strcmp(getPathSegment(0, context), "*") == 0) { context->speculationIndex++; } - context->maxValidation = validationMatrix[VSSgetValidation(thisNode)][context->maxValidation]; + context->maxValidation = getMaxValidation(VSSgetValidation(thisNode), context->maxValidation); if (VSSgetType(thisNode) != BRANCH && VSSgetType(thisNode) != STRUCT || context->leafNodesOnly == false) { if ( isGetLeafNodeList == false && isGetUuidList == false) { strcpy(context->searchData[context->numOfMatches].responsePaths, context->matchPath); diff --git a/binary/c_parser/cparserlib.h b/binary/c_parser/cparserlib.h index cca94bdf..782d02cf 100644 --- a/binary/c_parser/cparserlib.h +++ b/binary/c_parser/cparserlib.h @@ -72,3 +72,5 @@ char* VSSgetDescr(long nodeHandle); int VSSgetNumOfAllowedElements(long nodeHandle); char* VSSgetAllowedElement(long nodeHandle, int index); char* VSSgetUnit(long nodeHandle); +uint8_t getMaxValidation(uint8_t newValidation, uint8_t currentMaxValidation); +uint8_t translateToMatrixIndex(uint8_t index); diff --git a/binary/go_parser/parserlib/parser.go b/binary/go_parser/parserlib/parser.go index 4a282ea1..53667277 100644 --- a/binary/go_parser/parserlib/parser.go +++ b/binary/go_parser/parserlib/parser.go @@ -53,8 +53,24 @@ type SearchContext_t struct { ListFp *os.File } -var validationMatrix [4][4]int = [4][4]int{{1,2,11,12}, {2,2,12,12}, {11,12,11,12}, {12,12,12,12}} +// Access control values: none=0, write-only=1. read-write=2, consent +=10 +// matrix preserving inherited value with read-write having priority over write-only and consent over no consent +var validationMatrix [5][5]int = [5][5]int{{0,1,2,11,12}, {1,1,2,11,12}, {2,2,2,12,12}, {11,11,12,11,12}, {12,12,12,12,12}} +func getMaxValidation(newValidation int, currentMaxValidation int) int { + return validationMatrix[translateToMatrixIndex(newValidation)][translateToMatrixIndex(currentMaxValidation)] +} + +func translateToMatrixIndex(index int) int { + switch index { + case 0: return 0 + case 1: return 1 + case 2: return 2 + case 11: return 3 + case 12: return 4 + } + return 0 +} func initReadMetadata() { readTreeMetadata.CurrentDepth = 0 @@ -179,7 +195,7 @@ func saveMatchingNode(thisNode *def.Node_t, context *SearchContext_t, done *bool if (getPathSegment(0, context) == "*") { context.SpeculationIndex++ } - context.MaxValidation = validationMatrix[VSSgetValidation(thisNode)][context.MaxValidation] + context.MaxValidation = getMaxValidation(VSSgetValidation(thisNode), context.MaxValidation) if (VSSgetType(thisNode) != def.BRANCH && VSSgetType(thisNode) != def.STRUCT || context.LeafNodesOnly == false) { if ( isGetLeafNodeList == false && isGetUuidList == false) { context.SearchData[context.NumOfMatches].NodePath = context.MatchPath diff --git a/tests/vspec/test_static_uids/test_static_uids.py b/tests/vspec/test_static_uids/test_static_uids.py index 64d3ab5e..2aa8a6c5 100644 --- a/tests/vspec/test_static_uids/test_static_uids.py +++ b/tests/vspec/test_static_uids/test_static_uids.py @@ -83,11 +83,11 @@ def change_test_dir(request, monkeypatch): @pytest.mark.parametrize( "node_name, unit, datatype, allowed, minimum, maximum, result_static_uid", [ - ("TestNode", "m", VSSDataType.UINT16, "", 0, 10000, "156365B2"), - ("TestNode", "mm", VSSDataType.UINT32, "", "", "", "0931A8FA"), - ("TestUnit", "degrees/s", VSSDataType.FLOAT, "", "", "", "C733138C"), - ("TestMinMax", "percent", VSSDataType.UINT8, "", 0, 100, "72A24EF1"), - ("TestEnum", "m", VSSDataType.STRING, ["YES, NO"], "", "", "DEA50110"), + ("TestNode", "m", VSSDataType.UINT16, "", 0, 10000, "A1D565B2"), + ("TestNode", "mm", VSSDataType.UINT32, "", "", "", "B5D7A8FA"), + ("TestUnit", "degrees/s", VSSDataType.FLOAT, "", "", "", "DEA9138C"), + ("TestMinMax", "percent", VSSDataType.UINT8, "", 0, 100, "88FC5491"), + ("TestEnum", "m", VSSDataType.STRING, ["YES, NO"], "", "", "06AEB370"), ], ) def test_generate_id( diff --git a/tests/vspec/test_static_uids/validation.yaml b/tests/vspec/test_static_uids/validation.yaml index f41d1de2..ee6c6b7e 100644 --- a/tests/vspec/test_static_uids/validation.yaml +++ b/tests/vspec/test_static_uids/validation.yaml @@ -1,15 +1,15 @@ A: description: A is a test node - staticUID: '0x6865518C' + staticUID: '0x5B38F4AC' type: branch A.B: description: B is a branch of A - staticUID: '0x5EE0702C' + staticUID: '0x4687862C' type: branch A.B.Int32: datatype: int32 description: A.B.Int32 is a leaf of A.B of datatype int32 - staticUID: '0x4322A0BF' + staticUID: '0xF59053DF' type: sensor unit: rpm A.B.IsLeaf: @@ -18,20 +18,20 @@ A.B.IsLeaf: - 'NO' datatype: string description: This node is a leaf of the tree and it has allowed values (aka an enum). - staticUID: '0x51887178' + staticUID: '0x41EF3218' type: actuator A.B.Max: datatype: uint8 description: A leaf that uses a maximum value. max: 100 - staticUID: '0xACFF72AC' + staticUID: '0xCF708DCC' type: sensor unit: percent A.B.Min: datatype: uint8 description: A leaf that uses a minimum value. min: 10 - staticUID: '0xF66F948E' + staticUID: '0x2FC6BA2E' type: sensor unit: percent A.B.NewName: @@ -40,29 +40,29 @@ A.B.NewName: fka: - A.B.OldName - A.B.OlderName - staticUID: '0x63C22EF6' + staticUID: '0x126503B6' type: sensor unit: mm A.Float: datatype: float description: A.Float is a leaf of A of datatype float - staticUID: '0xC83DDDBF' + staticUID: '0xA493DFBF' type: actuator unit: mm A.Int16: datatype: int16 description: A.Int16 is a leaf of A of datatype int16 - staticUID: '0xC50253A3' + staticUID: '0x3D9D89A3' type: sensor unit: rpm A.String: datatype: string deprecation: This is test deprecation, let's say it used to be called Str instead String. description: A.String is a leaf of A of datatype string - staticUID: '0x215470FC' + staticUID: '0x49A622FC' type: sensor A.StringArray: datatype: string[] description: A.StringArray is a leaf of A of datatype string array - staticUID: '0x2115794B' + staticUID: '0xE8A4A62B' type: sensor diff --git a/tests/vspec/test_static_uids/validation_vspecs/validation.vspec b/tests/vspec/test_static_uids/validation_vspecs/validation.vspec index 3b51fdaa..065f3e76 100644 --- a/tests/vspec/test_static_uids/validation_vspecs/validation.vspec +++ b/tests/vspec/test_static_uids/validation_vspecs/validation.vspec @@ -8,16 +8,16 @@ A: description: A is a test node - staticUID: '0x6865518C' + staticUID: '0x5B38F4AC' type: branch A.B: description: B is a branch of A - staticUID: '0x5EE0702C' + staticUID: '0x4687862C' type: branch A.B.Int32: datatype: int32 description: A.B.Int32 is a leaf of A.B of datatype int32 - staticUID: '0x4322A0BF' + staticUID: '0xF59053DF' type: sensor unit: rpm A.B.IsLeaf: @@ -26,20 +26,20 @@ A.B.IsLeaf: - 'NO' datatype: string description: This node is a leaf of the tree and it has allowed values (aka an enum). - staticUID: '0x51887178' + staticUID: '0x41EF3218' type: actuator A.B.Max: datatype: uint8 description: A leaf that uses a maximum value. max: 100 - staticUID: '0xACFF72AC' + staticUID: '0xCF708DCC' type: sensor unit: percent A.B.Min: datatype: uint8 description: A leaf that uses a minimum value. min: 10 - staticUID: '0xF66F948E' + staticUID: '0x2FC6BA2E' type: sensor unit: percent A.B.NewName: @@ -48,29 +48,30 @@ A.B.NewName: fka: - A.B.OldName - A.B.OlderName - staticUID: '0x63C22EF6' + staticUID: '0x126503B6' type: sensor unit: mm A.Float: datatype: float description: A.Float is a leaf of A of datatype float - staticUID: '0xC83DDDBF' + staticUID: '0xA493DFBF' type: actuator unit: mm A.Int16: datatype: int16 description: A.Int16 is a leaf of A of datatype int16 - staticUID: '0xC50253A3' + staticUID: '0x3D9D89A3' type: sensor unit: rpm A.String: datatype: string - deprecation: This is test deprecation, let's say it used to be called Str instead String. + deprecation: This is test deprecation, let's say it used to be called Str instead + String. description: A.String is a leaf of A of datatype string - staticUID: '0x215470FC' + staticUID: '0x49A622FC' type: sensor A.StringArray: datatype: string[] description: A.StringArray is a leaf of A of datatype string array - staticUID: '0x2115794B' + staticUID: '0xE8A4A62B' type: sensor diff --git a/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change.vspec b/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change.vspec index 2f36dd65..2c7b8007 100644 --- a/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change.vspec +++ b/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change.vspec @@ -8,16 +8,16 @@ A: description: A is a test node - staticUID: '0x6865518C' + staticUID: '0x5B38F4AC' type: branch A.B: description: B is a branch of A - staticUID: '0x5EE0702C' + staticUID: '0x4687862C' type: branch A.B.Int32: datatype: int32 description: A.B.Int32 is a leaf of A.B of datatype int32 - staticUID: '0x4322A0BF' + staticUID: '0xF59053DF' type: sensor unit: rpm A.B.IsLeaf: @@ -26,20 +26,20 @@ A.B.IsLeaf: - 'NO' datatype: string description: This node is a leaf of the tree and it has allowed values (aka an enum). - staticUID: '0x51887178' + staticUID: '0x41EF3218' type: actuator A.B.Max: datatype: uint8 description: A leaf that uses a maximum value. max: 100 - staticUID: '0xACFF72AC' + staticUID: '0xCF708DCC' type: sensor unit: percent A.B.Min: datatype: uint8 description: A leaf that uses a minimum value. min: 10 - staticUID: '0xF66F948E' + staticUID: '0x2FC6BA2E' type: sensor unit: percent A.B.OldName: @@ -47,28 +47,30 @@ A.B.OldName: description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. fka: - A.B.OlderName - staticUID: '0xD4DFF5FD' + staticUID: '0x8B7DE17D' type: sensor unit: mm A.Float: datatype: float description: A.Float is a leaf of A of datatype float - staticUID: '0xC83DDDBF' + staticUID: '0xA493DFBF' type: actuator unit: mm A.Int16: datatype: int16 description: A.Int16 is a leaf of A of datatype int16 - staticUID: '0xC50253A3' + staticUID: '0x3D9D89A3' type: sensor unit: rpm A.String: datatype: string + deprecation: This is test deprecation, let's say it used to be called Str instead + String. description: A.String is a leaf of A of datatype string - staticUID: '0x215470FC' + staticUID: '0x49A622FC' type: sensor A.StringArray: datatype: string[] description: A.StringArray is a leaf of A of datatype string array - staticUID: '0x2115794B' + staticUID: '0xE8A4A62B' type: sensor diff --git a/vspec/utils/idgen_utils.py b/vspec/utils/idgen_utils.py index bcea2cf7..cd9fd85e 100644 --- a/vspec/utils/idgen_utils.py +++ b/vspec/utils/idgen_utils.py @@ -35,7 +35,7 @@ def get_node_identifier_bytes( f"allowed: {allowed}" f"min: {minimum}" f"max: {maximum}" - ).encode("utf-8") + ).encode("utf-8").lower() def fnv1_32_hash(identifier: bytes) -> int: From 5a158621a9cd859c0431594d7eca8b74beeccaa5 Mon Sep 17 00:00:00 2001 From: Niclas Wesemann Date: Thu, 18 Jan 2024 11:13:35 +0100 Subject: [PATCH 2/8] Update upstream again (#9) * to lowercase before patch Signed-off-by: Adnan Bekan * Binary parser bug fix. Signed-off-by: Ulf Bjorkengren --------- Signed-off-by: Adnan Bekan Signed-off-by: Ulf Bjorkengren Co-authored-by: Adnan Bekan Co-authored-by: Ulf Bjorkengren From c650169bda4637344cc209daeb3b8684bdb659a3 Mon Sep 17 00:00:00 2001 From: Niclas Wesemann Date: Tue, 23 Jan 2024 14:06:51 +0100 Subject: [PATCH 3/8] add strict mode for case sensitivity (#10) * add strict mode for case sensitivity Signed-off-by: Niclas Wesemann * update documentation Signed-off-by: Niclas Wesemann * fix trailing whitespace Signed-off-by: Niclas Wesemann --------- Signed-off-by: Niclas Wesemann --- docs/vspec2id.md | 11 ++- .../test_static_uids/test_static_uids.py | 85 +++++++++++++------ vspec/utils/idgen_utils.py | 16 +++- vspec/utils/vss2id_val.py | 11 ++- vspec/vssexporters/vss2id.py | 39 +++++---- 5 files changed, 110 insertions(+), 52 deletions(-) diff --git a/docs/vspec2id.md b/docs/vspec2id.md index 0de0592d..47907bdb 100644 --- a/docs/vspec2id.md +++ b/docs/vspec2id.md @@ -1,6 +1,6 @@ # vspec2id - vspec static UID generator and validator -The vspecID.py script is used to generate and validate static UIDs for all nodes in the tree. +The vspec2id.py script is used to generate and validate static UIDs for all nodes in the tree. They will be used as unique identifiers to transmit data between nodes. The static UIDs are implemented to replace long strings like `Vehicle.Body.Lights.DirectionIndicator.Right.IsSignaling` with a 4-byte identifier. @@ -9,8 +9,9 @@ with a 4-byte identifier. ```bash usage: vspec2id.py [-h] [-I dir] [-e EXTENDED_ATTRIBUTES] [-s] [--abort-on-unknown-attribute] [--abort-on-name-style] [--format format] [--uuid] [--no-expand] [-o overlays] [-u unit_file] - [-vt vspec_types_file] [-ot ] [--json-all-extended-attributes] [--json-pretty] [--yaml-all-extended-attributes] [-v version] [--all-idl-features] - [--gqlfield GQLFIELD GQLFIELD] [--validate-static-uid VALIDATE_STATIC_UID] [--only-validate-no-export] + [-q quantity_file] [-vt vspec_types_file] [-ot ] [--json-all-extended-attributes] [--json-pretty] [--yaml-all-extended-attributes] [-v version] [--all-idl-features] + [--gqlfield GQLFIELD GQLFIELD] [--jsonschema-all-extended-attributes] [--jsonschema-disallow-additional-properties] [--jsonschema-require-all-properties] [--jsonschema-pretty] + [--validate-static-uid VALIDATE_STATIC_UID] [--only-validate-no-export] [--strict-mode] Convert vspec to other formats. @@ -27,6 +28,7 @@ IDGEN arguments: Path to validation file. --only-validate-no-export For pytests and pipelines you can skip the export of the + --strict-mode Strict mode means that the generation of static UIDs is case-sensitive. ``` ## Example @@ -43,6 +45,9 @@ cd path/to/your/vss-tools Great, you generated your first overlay that will also be used as your validation file as soon as you update your vehicle signal specification file. +If needed you can make the static UID generation case-sensitive using the command line argument `--strict-mode`. It +will default to false. + ### Generate e.g. yaml file with static UIDs Now if you just want to generate a new e.g. yaml file including your static UIDs, please use the overlay function of diff --git a/tests/vspec/test_static_uids/test_static_uids.py b/tests/vspec/test_static_uids/test_static_uids.py index 2aa8a6c5..85fc5b7b 100644 --- a/tests/vspec/test_static_uids/test_static_uids.py +++ b/tests/vspec/test_static_uids/test_static_uids.py @@ -11,19 +11,19 @@ # import os -import pytest import shlex -import vspec -import vspec.vssexporters.vss2id as vss2id -import vspec2x +from typing import Dict + +import pytest import yaml -from typing import Dict -from vspec.model.constants import VSSTreeType, VSSDataType, VSSUnit +import vspec +import vspec2x +import vspec.vssexporters.vss2id as vss2id +from vspec.model.constants import VSSDataType, VSSTreeType, VSSUnit from vspec.model.vsstree import VSSNode from vspec.utils.idgen_utils import get_all_keys_values - # HELPERS @@ -81,13 +81,13 @@ def change_test_dir(request, monkeypatch): @pytest.mark.parametrize( - "node_name, unit, datatype, allowed, minimum, maximum, result_static_uid", + "node_name, unit, datatype, allowed, minimum, maximum, result_static_uid, strict_mode", [ - ("TestNode", "m", VSSDataType.UINT16, "", 0, 10000, "A1D565B2"), - ("TestNode", "mm", VSSDataType.UINT32, "", "", "", "B5D7A8FA"), - ("TestUnit", "degrees/s", VSSDataType.FLOAT, "", "", "", "DEA9138C"), - ("TestMinMax", "percent", VSSDataType.UINT8, "", 0, 100, "88FC5491"), - ("TestEnum", "m", VSSDataType.STRING, ["YES, NO"], "", "", "06AEB370"), + ("TestNode", "m", VSSDataType.UINT16, "", 0, 10000, "A1D565B2", False), + ("TestNode", "mm", VSSDataType.UINT32, "", "", "", "B5D7A8FA", False), + ("TestUnit", "degrees/s", VSSDataType.FLOAT, "", "", "", "DEA9138C", False), + ("TestMinMax", "percent", VSSDataType.UINT8, "", 0, 100, "88FC5491", False), + ("TestEnum", "m", VSSDataType.STRING, ["YES, NO"], "", "", "06AEB370", False), ], ) def test_generate_id( @@ -98,13 +98,54 @@ def test_generate_id( minimum: str, maximum: str, result_static_uid: str, + strict_mode: bool, ): node = get_test_node(node_name, unit, datatype, allowed, minimum, maximum) - result, _ = vss2id.generate_split_id(node, id_counter=0) + result, _ = vss2id.generate_split_id(node, id_counter=0, strict_mode=strict_mode) assert result == result_static_uid +@pytest.mark.parametrize( + "node_name_case_sensitive, node_name_case_insensitive, strict_mode", + [ + ("TestNode", "testnode", False), + ("TestNode", "testnode", True), + ], +) +def test_strict_mode( + node_name_case_sensitive: str, node_name_case_insensitive: str, strict_mode: bool +): + node_case_sensitive = get_test_node( + node_name=node_name_case_sensitive, + unit="m", + datatype=VSSDataType.FLOAT, + allowed="", + minimum="", + maximum="", + ) + result_case_sensitive, _ = vss2id.generate_split_id( + node_case_sensitive, id_counter=0, strict_mode=strict_mode + ) + + node_case_insensitive = get_test_node( + node_name=node_name_case_insensitive, + unit="m", + datatype=VSSDataType.FLOAT, + allowed="", + minimum="", + maximum="", + ) + result_case_insensitive, _ = vss2id.generate_split_id( + node_case_insensitive, id_counter=0, strict_mode=strict_mode + ) + + if strict_mode: + assert result_case_sensitive != result_case_insensitive + else: + assert result_case_sensitive == result_case_insensitive + + @pytest.mark.parametrize( "test_file, validation_file", [("test_vspecs/test.vspec", "./validation.yaml")], @@ -122,10 +163,10 @@ def test_export_node( vspec_file, include_paths=["."], tree_type=VSSTreeType.SIGNAL_TREE ) yaml_dict: Dict[str, str] = {} - vss2id.export_node(yaml_dict, tree, id_counter=0) + vss2id.export_node(yaml_dict, tree, id_counter=0, strict_mode=False) result_dict: Dict[str, str] - with open(os.path.join(dir_path, validation_file), "r") as f: + with open(os.path.join(dir_path, validation_file)) as f: result_dict = yaml.load(f, Loader=yaml.FullLoader) assert result_dict.keys() == yaml_dict.keys() @@ -149,11 +190,7 @@ def test_duplicate_hash(caplog: pytest.LogCaptureFixture, children_names: list): if children_names[0] == children_names[1]: # assert system exit and log with pytest.raises(SystemExit) as pytest_wrapped_e: - vss2id.export_node( - yaml_dict, - tree, - id_counter=0, - ) + vss2id.export_node(yaml_dict, tree, id_counter=0, strict_mode=False) assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == -1 assert len(caplog.records) == 1 and all( @@ -161,11 +198,7 @@ def test_duplicate_hash(caplog: pytest.LogCaptureFixture, children_names: list): ) else: # assert all IDs different - vss2id.export_node( - yaml_dict, - tree, - id_counter=0, - ) + vss2id.export_node(yaml_dict, tree, id_counter=0, strict_mode=False) assigned_ids: list = [] for key, value in get_all_keys_values(yaml_dict): if not isinstance(value, dict) and key == "staticUID": diff --git a/vspec/utils/idgen_utils.py b/vspec/utils/idgen_utils.py index cd9fd85e..2c4ef94e 100644 --- a/vspec/utils/idgen_utils.py +++ b/vspec/utils/idgen_utils.py @@ -6,6 +6,7 @@ # # SPDX-License-Identifier: MPL-2.0 + def get_node_identifier_bytes( qualified_name: str, data_type: str, @@ -14,6 +15,7 @@ def get_node_identifier_bytes( allowed: str, minimum: str, maximum: str, + strict_mode: bool, ) -> bytes: """Get a node identifier as bytes. Used as an input for hashing @@ -24,10 +26,11 @@ def get_node_identifier_bytes( @param allowed: the enum for allowed values @param minimum: min value for the data if exists @param maximum: max value for the data if exists + @param strict_mode: strict mode means case sensitivity of node qualified names @return: a bytes representation of the node """ - return ( + node_identifier: bytes = ( f"{qualified_name}: " f"unit: {unit}, " f"datatype: {data_type}, " @@ -35,7 +38,12 @@ def get_node_identifier_bytes( f"allowed: {allowed}" f"min: {minimum}" f"max: {maximum}" - ).encode("utf-8").lower() + ).encode() + + if strict_mode: + return node_identifier + else: + return node_identifier.lower() def fnv1_32_hash(identifier: bytes) -> int: @@ -52,12 +60,13 @@ def fnv1_32_hash(identifier: bytes) -> int: return id_hash -def fnv1_32_wrapper(name: str, source: dict): +def fnv1_32_wrapper(name: str, source: dict, strict_mode: bool): """A wrapper for the 32-bit hashing function if the input node is represented as dict instead of VSSNode @param name: full name aka qualified name @param source: + @param strict_mode: strict mode means case sensitivity of node qualified names @return: """ allowed: str = source["allowed"] if "allowed" in source.keys() else "" @@ -71,6 +80,7 @@ def fnv1_32_wrapper(name: str, source: dict): allowed, minimum, maximum, + strict_mode, ) return format(fnv1_32_hash(identifier), "08X") diff --git a/vspec/utils/vss2id_val.py b/vspec/utils/vss2id_val.py index c5a6bb2f..4242c647 100644 --- a/vspec/utils/vss2id_val.py +++ b/vspec/utils/vss2id_val.py @@ -6,11 +6,13 @@ # # SPDX-License-Identifier: MPL-2.0 -from anytree import PreOrderIter # type: ignore import argparse import logging import sys from typing import Optional + +from anytree import PreOrderIter # type: ignore + from vspec.model.vsstree import VSSNode from vspec.utils.idgen_utils import fnv1_32_wrapper @@ -40,19 +42,20 @@ def check_description(k: str, v: dict, match_tuple: tuple): f"vspec: '{v['description']}'" ) - def check_semantics(k: str, v: dict) -> Optional[int]: + def check_semantics(k: str, v: dict, strict_mode: bool) -> Optional[int]: """Checks if the change was a semantic or path change. This can be triggered by manually adding a fka (formerly known as) attribute to the vspec. The result is that the old hash can be matched such that a node keeps the same UID. @param k: the current key @param v: the current value (dict) + @param strict_mode: strict mode means case sensitivity for static UID generation @return: boolean if it was a semantic or path change """ if "fka" in v.keys(): semantic_match: Optional[int] = None for fka_val in v["fka"]: - old_static_uid = "0x" + fnv1_32_wrapper(fka_val, v) + old_static_uid = "0x" + fnv1_32_wrapper(fka_val, v, strict_mode) for i, validation_node in enumerate(validation_tree_nodes): if ( old_static_uid @@ -106,7 +109,7 @@ def hashed_pipeline(): # if not matched via UID check semantics or path change if len(matched_uids) == 0: - semantic_match = check_semantics(key, value) + semantic_match = check_semantics(key, value, config.strict_mode) if semantic_match is None: key_found: bool = False for i, node in enumerate(validation_tree_nodes): diff --git a/vspec/vssexporters/vss2id.py b/vspec/vssexporters/vss2id.py index 93f87d57..f6a3636f 100644 --- a/vspec/vssexporters/vss2id.py +++ b/vspec/vssexporters/vss2id.py @@ -15,17 +15,16 @@ import os import sys from typing import Dict, Tuple + +import yaml + from vspec import load_tree -from vspec.model.constants import VSSTreeType from vspec.loggingconfig import initLogging +from vspec.model.constants import VSSTreeType from vspec.model.vsstree import VSSNode from vspec.utils import vss2id_val -from vspec.utils.idgen_utils import ( - get_node_identifier_bytes, - fnv1_32_hash, - get_all_keys_values, -) -import yaml +from vspec.utils.idgen_utils import (fnv1_32_hash, get_all_keys_values, + get_node_identifier_bytes) def add_arguments(parser: argparse.ArgumentParser) -> None: @@ -42,13 +41,21 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: default=False, help="For pytests and pipelines you can skip the export of the vspec file.", ) + parser.add_argument( + "--strict-mode", + action="store_true", + help="Strict mode means that the generation of static UIDs is case-sensitive.", + ) -def generate_split_id(node: VSSNode, id_counter: int) -> Tuple[str, int]: +def generate_split_id( + node: VSSNode, id_counter: int, strict_mode: bool +) -> Tuple[str, int]: """Generates static UIDs using 4-byte FNV-1 hash. @param node: VSSNode that we want to generate a static UID for @param id_counter: consecutive numbers counter for amount of nodes + @param strict_mode: strict mode means case sensitivity for static UID generation @return: tuple of hashed string and id counter """ @@ -60,21 +67,23 @@ def generate_split_id(node: VSSNode, id_counter: int) -> Tuple[str, int]: node.allowed, node.min, node.max, + strict_mode, ) hashed_str = format(fnv1_32_hash(identifier), "08X") return hashed_str, id_counter + 1 -def export_node(yaml_dict, node, id_counter) -> Tuple[int, int]: +def export_node(yaml_dict, node, id_counter, strict_mode: bool) -> Tuple[int, int]: """Recursive function to export the full tree to a dict @param yaml_dict: the to be exported dict @param node: parent node of the tree @param id_counter: counter for amount of ids + @param strict_mode: strict mode means case sensitivity for static UID generation @return: id_counter, id_counter """ - node_id, id_counter = generate_split_id(node, id_counter) + node_id, id_counter = generate_split_id(node, id_counter, strict_mode) # check for hash duplicates for key, value in get_all_keys_values(yaml_dict): @@ -114,11 +123,7 @@ def export_node(yaml_dict, node, id_counter) -> Tuple[int, int]: yaml_dict[node_path]["deprecation"] = node.deprecation for child in node.children: - id_counter, id_counter = export_node( - yaml_dict, - child, - id_counter, - ) + id_counter, id_counter = export_node(yaml_dict, child, id_counter, strict_mode) return id_counter, id_counter @@ -134,7 +139,9 @@ def export(config: argparse.Namespace, signal_root: VSSNode, print_uuid): id_counter: int = 0 signals_yaml_dict: Dict[str, str] = {} # Use str for ID values - id_counter, _ = export_node(signals_yaml_dict, signal_root, id_counter) + id_counter, _ = export_node( + signals_yaml_dict, signal_root, id_counter, config.strict_mode + ) if config.validate_static_uid: logging.info( From 1e2a3a0eac2750215d7b49356e72d5ae17ab452f Mon Sep 17 00:00:00 2001 From: Niclas Wesemann Date: Tue, 23 Jan 2024 15:34:28 +0100 Subject: [PATCH 4/8] sync master (#13) * to lowercase before patch Signed-off-by: Adnan Bekan * Binary parser bug fix. Signed-off-by: Ulf Bjorkengren --------- Signed-off-by: Adnan Bekan Signed-off-by: Ulf Bjorkengren Signed-off-by: Niclas Wesemann Co-authored-by: Adnan Bekan Co-authored-by: Ulf Bjorkengren --- tests/vspec/test_static_uids/test_static_uids.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/vspec/test_static_uids/test_static_uids.py b/tests/vspec/test_static_uids/test_static_uids.py index 85fc5b7b..98ee1052 100644 --- a/tests/vspec/test_static_uids/test_static_uids.py +++ b/tests/vspec/test_static_uids/test_static_uids.py @@ -81,13 +81,13 @@ def change_test_dir(request, monkeypatch): @pytest.mark.parametrize( - "node_name, unit, datatype, allowed, minimum, maximum, result_static_uid, strict_mode", + "node_name, unit, datatype, allowed, minimum, maximum, result_static_uid", [ - ("TestNode", "m", VSSDataType.UINT16, "", 0, 10000, "A1D565B2", False), - ("TestNode", "mm", VSSDataType.UINT32, "", "", "", "B5D7A8FA", False), - ("TestUnit", "degrees/s", VSSDataType.FLOAT, "", "", "", "DEA9138C", False), - ("TestMinMax", "percent", VSSDataType.UINT8, "", 0, 100, "88FC5491", False), - ("TestEnum", "m", VSSDataType.STRING, ["YES, NO"], "", "", "06AEB370", False), + ("TestNode", "m", VSSDataType.UINT16, "", 0, 10000, "A1D565B2"), + ("TestNode", "mm", VSSDataType.UINT32, "", "", "", "B5D7A8FA"), + ("TestUnit", "degrees/s", VSSDataType.FLOAT, "", "", "", "DEA9138C"), + ("TestMinMax", "percent", VSSDataType.UINT8, "", 0, 100, "88FC5491"), + ("TestEnum", "m", VSSDataType.STRING, ["YES, NO"], "", "", "06AEB370"), ], ) def test_generate_id( @@ -98,10 +98,9 @@ def test_generate_id( minimum: str, maximum: str, result_static_uid: str, - strict_mode: bool, ): node = get_test_node(node_name, unit, datatype, allowed, minimum, maximum) - result, _ = vss2id.generate_split_id(node, id_counter=0, strict_mode=strict_mode) + result, _ = vss2id.generate_split_id(node, id_counter=0, strict_mode=False) assert result == result_static_uid From b54a97e1b9124dd37199103e5186ee5cec38b380 Mon Sep 17 00:00:00 2001 From: Adnan Bekan Date: Tue, 23 Jan 2024 14:35:11 +0100 Subject: [PATCH 5/8] patch fka function and update docu Signed-off-by: Adnan Bekan --- docs/vspec2id.md | 9 ++++----- vspec/utils/idgen_utils.py | 17 +++++++++++------ vspec/vssexporters/vss2id.py | 3 ++- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/docs/vspec2id.md b/docs/vspec2id.md index 47907bdb..e948de64 100644 --- a/docs/vspec2id.md +++ b/docs/vspec2id.md @@ -83,11 +83,10 @@ A.B.NewName: type: actuator allowed: ["YES", "NO"] description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. - fka: ['A.B.OldName', 'A.B.OlderName'] + fka: ['A.B.OlderName', 'A.B.OldName'] ``` -As stated if you want to rename the node `A.B.NewName` to `A.NewName` you can also write the `fka` attribute -stating its legacy path. +As stated if you want to rename the node `A.B.NewName` to `A.NewName` you can also write the `fka` attribute stating its legacy path. For hashing function in previous case `A.B.OlderName` will be used. To summarize these are the `BREAKING CHANGES` that affect the hash and `NON-BREAKING CHANGES` that throw warnings only: @@ -98,14 +97,14 @@ warnings only: | Data type | | Deprecation | | Type (i.e. node type) | | Deleted Attribute | | Unit | | Change description | -| Enum values (allowed) | | | +| Enum values (allowed) | | Qualified name (fka) | | Minimum | | | | Maximum | | | Now you should know about all possible changes. To run the validation step, please do: ```bash -./vspecID.py ../vehicle_signal_specification/spec/VehicleSignalSpecification.vspec ../output_id_v2.vspec --validate-static-uid ../output_id_v1.vspec +./vspec2id.py ../vehicle_signal_specification/spec/VehicleSignalSpecification.vspec ../output_id_v2.vspec --validate-static-uid ../output_id_v1.vspec ``` Depending on what you changed in the vehicle signal specification the corresponding errors will be triggered. diff --git a/vspec/utils/idgen_utils.py b/vspec/utils/idgen_utils.py index 2c4ef94e..39b409d7 100644 --- a/vspec/utils/idgen_utils.py +++ b/vspec/utils/idgen_utils.py @@ -69,14 +69,19 @@ def fnv1_32_wrapper(name: str, source: dict, strict_mode: bool): @param strict_mode: strict mode means case sensitivity of node qualified names @return: """ - allowed: str = source["allowed"] if "allowed" in source.keys() else "" - minimum: str = source["min"] if "min" in source.keys() else "" - maximum: str = source["max"] if "max" in source.keys() else "" + # Verify and assign values from source dictionary using source.get + allowed: str = source.get("allowed", "") + minimum: str = source.get("min", "") + maximum: str = source.get("max", "") + datatype: str = source.get("datatype", "") + vsstype: str = source.get("type", "") + unit: str = source.get("unit", "") + identifier = get_node_identifier_bytes( name, - source["datatype"], - source["type"], - source["unit"], + datatype, + vsstype, + unit, allowed, minimum, maximum, diff --git a/vspec/vssexporters/vss2id.py b/vspec/vssexporters/vss2id.py index f6a3636f..0fbc882d 100644 --- a/vspec/vssexporters/vss2id.py +++ b/vspec/vssexporters/vss2id.py @@ -59,8 +59,9 @@ def generate_split_id( @return: tuple of hashed string and id counter """ + name = node.fka[0] if hasattr(node, 'fka') and node.fka else node.qualified_name() identifier = get_node_identifier_bytes( - node.qualified_name(), + name, node.data_type_str, node.type.value, node.get_unit(), From c4f9c1defdeb1ac5e64e0a3a4791096a85c7fbbc Mon Sep 17 00:00:00 2001 From: Adnan Bekan Date: Tue, 23 Jan 2024 23:31:55 +0100 Subject: [PATCH 6/8] update tests Signed-off-by: Adnan Bekan --- .../vspec/test_static_uids/test_static_uids.py | 1 - .../test_static_uids/test_vspecs/test.vspec | 2 +- .../test_vspecs/test_added_attribute.vspec | 2 +- .../test_vspecs/test_datatype.vspec | 2 +- .../test_vspecs/test_deleted_attribute.vspec | 2 +- .../test_vspecs/test_deprecation.vspec | 2 +- .../test_vspecs/test_description.vspec | 2 +- .../test_vspecs/test_name_datatype.vspec | 2 +- .../test_vspecs/test_unit.vspec | 2 +- .../test_vspecs/test_vss_path.vspec | 2 +- tests/vspec/test_static_uids/validation.yaml | 4 ++-- .../validation_vspecs/validation.vspec | 4 ++-- .../validation_semantic_change.vspec | 2 +- vspec/utils/vss2id_val.py | 18 ++++++++++++------ 14 files changed, 26 insertions(+), 21 deletions(-) diff --git a/tests/vspec/test_static_uids/test_static_uids.py b/tests/vspec/test_static_uids/test_static_uids.py index 98ee1052..505b55a3 100644 --- a/tests/vspec/test_static_uids/test_static_uids.py +++ b/tests/vspec/test_static_uids/test_static_uids.py @@ -235,7 +235,6 @@ def test_vss_path(caplog: pytest.LogCaptureFixture): test_file: str = "./test_vspecs/test_vss_path.vspec" clas = shlex.split(get_cla_test(test_file)) vspec2x.main(["--format", "idgen"] + clas[1:]) - assert len(caplog.records) == 1 and all( log.levelname == "WARNING" for log in caplog.records ) diff --git a/tests/vspec/test_static_uids/test_vspecs/test.vspec b/tests/vspec/test_static_uids/test_vspecs/test.vspec index bb5ec60d..58769626 100644 --- a/tests/vspec/test_static_uids/test_vspecs/test.vspec +++ b/tests/vspec/test_static_uids/test_vspecs/test.vspec @@ -41,7 +41,7 @@ A.B.NewName: type: sensor unit: mm description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. - fka: ['A.B.OldName', 'A.B.OlderName'] + fka: ['A.B.OlderName', 'A.B.OldName'] A.B.IsLeaf: datatype: string type: actuator diff --git a/tests/vspec/test_static_uids/test_vspecs/test_added_attribute.vspec b/tests/vspec/test_static_uids/test_vspecs/test_added_attribute.vspec index 8c5d4e4c..ca93b2c6 100644 --- a/tests/vspec/test_static_uids/test_vspecs/test_added_attribute.vspec +++ b/tests/vspec/test_static_uids/test_vspecs/test_added_attribute.vspec @@ -40,7 +40,7 @@ A.B.NewName: type: sensor unit: mm description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. - fka: ['A.B.OldName', 'A.B.OlderName'] + fka: ['A.B.OlderName', 'A.B.OldName'] A.B.IsLeaf: datatype: string type: actuator diff --git a/tests/vspec/test_static_uids/test_vspecs/test_datatype.vspec b/tests/vspec/test_static_uids/test_vspecs/test_datatype.vspec index 34ee2983..ccb89c88 100644 --- a/tests/vspec/test_static_uids/test_vspecs/test_datatype.vspec +++ b/tests/vspec/test_static_uids/test_vspecs/test_datatype.vspec @@ -41,7 +41,7 @@ A.B.NewName: type: sensor unit: mm description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. - fka: ['A.B.OldName', 'A.B.OlderName'] + fka: ['A.B.OlderName', 'A.B.OldName'] A.B.IsLeaf: datatype: string type: actuator diff --git a/tests/vspec/test_static_uids/test_vspecs/test_deleted_attribute.vspec b/tests/vspec/test_static_uids/test_vspecs/test_deleted_attribute.vspec index a28b973a..bf2df77e 100644 --- a/tests/vspec/test_static_uids/test_vspecs/test_deleted_attribute.vspec +++ b/tests/vspec/test_static_uids/test_vspecs/test_deleted_attribute.vspec @@ -41,7 +41,7 @@ A.B.NewName: type: sensor unit: mm description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. - fka: ['A.B.OldName', 'A.B.OlderName'] + fka: ['A.B.OlderName', 'A.B.OldName'] A.B.IsLeaf: datatype: string type: actuator diff --git a/tests/vspec/test_static_uids/test_vspecs/test_deprecation.vspec b/tests/vspec/test_static_uids/test_vspecs/test_deprecation.vspec index 2c1bc8fd..03e441c7 100644 --- a/tests/vspec/test_static_uids/test_vspecs/test_deprecation.vspec +++ b/tests/vspec/test_static_uids/test_vspecs/test_deprecation.vspec @@ -41,7 +41,7 @@ A.B.NewName: type: sensor unit: mm description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. - fka: ['A.B.OldName', 'A.B.OlderName'] + fka: ['A.B.OlderName', 'A.B.OldName'] A.B.IsLeaf: datatype: string type: actuator diff --git a/tests/vspec/test_static_uids/test_vspecs/test_description.vspec b/tests/vspec/test_static_uids/test_vspecs/test_description.vspec index 336ca49a..99452558 100644 --- a/tests/vspec/test_static_uids/test_vspecs/test_description.vspec +++ b/tests/vspec/test_static_uids/test_vspecs/test_description.vspec @@ -41,7 +41,7 @@ A.B.NewName: type: sensor unit: mm description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. - fka: ['A.B.OldName', 'A.B.OlderName'] + fka: ['A.B.OlderName', 'A.B.OldName'] A.B.IsLeaf: datatype: string type: actuator diff --git a/tests/vspec/test_static_uids/test_vspecs/test_name_datatype.vspec b/tests/vspec/test_static_uids/test_vspecs/test_name_datatype.vspec index 8df7e62e..897182ea 100644 --- a/tests/vspec/test_static_uids/test_vspecs/test_name_datatype.vspec +++ b/tests/vspec/test_static_uids/test_vspecs/test_name_datatype.vspec @@ -41,7 +41,7 @@ A.B.NewName: type: sensor unit: mm description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. - fka: ['A.B.OldName', 'A.B.OlderName'] + fka: ['A.B.OlderName', 'A.B.OldName'] A.B.IsLeaf: datatype: string type: actuator diff --git a/tests/vspec/test_static_uids/test_vspecs/test_unit.vspec b/tests/vspec/test_static_uids/test_vspecs/test_unit.vspec index 6ec0a3a2..e70db936 100644 --- a/tests/vspec/test_static_uids/test_vspecs/test_unit.vspec +++ b/tests/vspec/test_static_uids/test_vspecs/test_unit.vspec @@ -41,7 +41,7 @@ A.B.NewName: type: sensor unit: mm description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. - fka: ['A.B.OldName', 'A.B.OlderName'] + fka: ['A.B.OlderName', 'A.B.OldName'] A.B.IsLeaf: datatype: string type: actuator diff --git a/tests/vspec/test_static_uids/test_vspecs/test_vss_path.vspec b/tests/vspec/test_static_uids/test_vspecs/test_vss_path.vspec index ca11d073..4f3d5556 100644 --- a/tests/vspec/test_static_uids/test_vspecs/test_vss_path.vspec +++ b/tests/vspec/test_static_uids/test_vspecs/test_vss_path.vspec @@ -42,7 +42,7 @@ A.B.NewName: type: sensor unit: mm description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. - fka: ['A.B.OldName', 'A.B.OlderName'] + fka: ['A.B.OlderName', 'A.B.OldName'] A.B.IsLeaf: datatype: string type: actuator diff --git a/tests/vspec/test_static_uids/validation.yaml b/tests/vspec/test_static_uids/validation.yaml index ee6c6b7e..f7fe3e78 100644 --- a/tests/vspec/test_static_uids/validation.yaml +++ b/tests/vspec/test_static_uids/validation.yaml @@ -38,9 +38,9 @@ A.B.NewName: datatype: uint32 description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. fka: - - A.B.OldName - A.B.OlderName - staticUID: '0x126503B6' + - A.B.OldName + staticUID: '0x3FB9A236' type: sensor unit: mm A.Float: diff --git a/tests/vspec/test_static_uids/validation_vspecs/validation.vspec b/tests/vspec/test_static_uids/validation_vspecs/validation.vspec index 065f3e76..8587c1a4 100644 --- a/tests/vspec/test_static_uids/validation_vspecs/validation.vspec +++ b/tests/vspec/test_static_uids/validation_vspecs/validation.vspec @@ -46,9 +46,9 @@ A.B.NewName: datatype: uint32 description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. fka: - - A.B.OldName - A.B.OlderName - staticUID: '0x126503B6' + - A.B.OldName + staticUID: '0x3FB9A236' type: sensor unit: mm A.Float: diff --git a/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change.vspec b/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change.vspec index 2c7b8007..f4541431 100644 --- a/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change.vspec +++ b/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change.vspec @@ -47,7 +47,7 @@ A.B.OldName: description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. fka: - A.B.OlderName - staticUID: '0x8B7DE17D' + staticUID: '0x3FB9A236' type: sensor unit: mm A.Float: diff --git a/vspec/utils/vss2id_val.py b/vspec/utils/vss2id_val.py index 4242c647..b92a431a 100644 --- a/vspec/utils/vss2id_val.py +++ b/vspec/utils/vss2id_val.py @@ -101,12 +101,18 @@ def hashed_pipeline(): nonlocal validation_tree_nodes for key, value in signals_dict.items(): - matched_uids = [ - (key, id_validation_tree) - for id_validation_tree, other_node in enumerate(validation_tree_nodes) - if value["staticUID"] == other_node.extended_attributes["staticUID"] - ] - + matched_uids = [] + for id_validation_tree, other_node in enumerate(validation_tree_nodes): + if value["staticUID"] == other_node.extended_attributes["staticUID"]: + if key != other_node.qualified_name(): + logging.warning( + f"[Validation] NON-BREAKING change via fka: " + f"SEMANTIC NAME CHANGE or PATH CHANGE '{key}', " + f"it used to be '{other_node.qualified_name()}'. " + f"Static UIDs are matching '{value['staticUID']}', " + f"'{other_node.extended_attributes['staticUID']}'." + ) + matched_uids.append((key, id_validation_tree, other_node)) # if not matched via UID check semantics or path change if len(matched_uids) == 0: semantic_match = check_semantics(key, value, config.strict_mode) From 9cc74b4c5a8d5450e4d0ee0e41e594a777a63bba Mon Sep 17 00:00:00 2001 From: Niclas Wesemann Date: Wed, 24 Jan 2024 13:41:43 +0100 Subject: [PATCH 7/8] check semantics while matching Signed-off-by: Niclas Wesemann --- .../test_static_uids/test_static_uids.py | 10 ++- ...pec => validation_semantic_change_1.vspec} | 0 .../validation_semantic_change_2.vspec | 74 +++++++++++++++++++ vspec/utils/vss2id_val.py | 10 +-- vspec/vssexporters/vss2id.py | 2 +- 5 files changed, 85 insertions(+), 11 deletions(-) rename tests/vspec/test_static_uids/validation_vspecs/{validation_semantic_change.vspec => validation_semantic_change_1.vspec} (100%) create mode 100644 tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change_2.vspec diff --git a/tests/vspec/test_static_uids/test_static_uids.py b/tests/vspec/test_static_uids/test_static_uids.py index 505b55a3..43269326 100644 --- a/tests/vspec/test_static_uids/test_static_uids.py +++ b/tests/vspec/test_static_uids/test_static_uids.py @@ -218,8 +218,14 @@ def test_full_script(caplog: pytest.LogCaptureFixture): @pytest.mark.usefixtures("change_test_dir") -def test_semantic(caplog: pytest.LogCaptureFixture): - validation_file: str = "./validation_vspecs/validation_semantic_change.vspec" +@pytest.mark.parametrize( + "validation_file", + [ + "./validation_vspecs/validation_semantic_change_1.vspec", + "./validation_vspecs/validation_semantic_change_2.vspec", + ], +) +def test_semantic(caplog: pytest.LogCaptureFixture, validation_file: str): clas = shlex.split(get_cla_validation(validation_file)) vspec2x.main(["--format", "idgen"] + clas[1:]) diff --git a/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change.vspec b/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change_1.vspec similarity index 100% rename from tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change.vspec rename to tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change_1.vspec diff --git a/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change_2.vspec b/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change_2.vspec new file mode 100644 index 00000000..21fd81e7 --- /dev/null +++ b/tests/vspec/test_static_uids/validation_vspecs/validation_semantic_change_2.vspec @@ -0,0 +1,74 @@ +# Copyright (c) 2023 Contributors to COVESA +# +# This program and the accompanying materials are made available under the +# terms of the Mozilla Public License 2.0 which is available at +# https://www.mozilla.org/en-US/MPL/2.0/ +# +# SPDX-License-Identifier: MPL-2.0 + +A: + description: A is a test node + staticUID: '0x5B38F4AC' + type: branch +A.B: + description: B is a branch of A + staticUID: '0x4687862C' + type: branch +A.B.Int32: + datatype: int32 + description: A.B.Int32 is a leaf of A.B of datatype int32 + staticUID: '0xF59053DF' + type: sensor + unit: rpm +A.B.IsLeaf: + allowed: + - 'YES' + - 'NO' + datatype: string + description: This node is a leaf of the tree and it has allowed values (aka an enum). + staticUID: '0x41EF3218' + type: actuator +A.B.Max: + datatype: uint8 + description: A leaf that uses a maximum value. + max: 100 + staticUID: '0xCF708DCC' + type: sensor + unit: percent +A.B.Min: + datatype: uint8 + description: A leaf that uses a minimum value. + min: 10 + staticUID: '0x2FC6BA2E' + type: sensor + unit: percent +A.B.OlderName: + datatype: uint32 + description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. + staticUID: '0x3FB9A236' + type: sensor + unit: mm +A.Float: + datatype: float + description: A.Float is a leaf of A of datatype float + staticUID: '0xA493DFBF' + type: actuator + unit: mm +A.Int16: + datatype: int16 + description: A.Int16 is a leaf of A of datatype int16 + staticUID: '0x3D9D89A3' + type: sensor + unit: rpm +A.String: + datatype: string + deprecation: This is test deprecation, let's say it used to be called Str instead + String. + description: A.String is a leaf of A of datatype string + staticUID: '0x49A622FC' + type: sensor +A.StringArray: + datatype: string[] + description: A.StringArray is a leaf of A of datatype string array + staticUID: '0xE8A4A62B' + type: sensor diff --git a/vspec/utils/vss2id_val.py b/vspec/utils/vss2id_val.py index b92a431a..c5be37cb 100644 --- a/vspec/utils/vss2id_val.py +++ b/vspec/utils/vss2id_val.py @@ -105,14 +105,8 @@ def hashed_pipeline(): for id_validation_tree, other_node in enumerate(validation_tree_nodes): if value["staticUID"] == other_node.extended_attributes["staticUID"]: if key != other_node.qualified_name(): - logging.warning( - f"[Validation] NON-BREAKING change via fka: " - f"SEMANTIC NAME CHANGE or PATH CHANGE '{key}', " - f"it used to be '{other_node.qualified_name()}'. " - f"Static UIDs are matching '{value['staticUID']}', " - f"'{other_node.extended_attributes['staticUID']}'." - ) - matched_uids.append((key, id_validation_tree, other_node)) + _ = check_semantics(key, value, config.strict_mode) + matched_uids.append((key, id_validation_tree)) # if not matched via UID check semantics or path change if len(matched_uids) == 0: semantic_match = check_semantics(key, value, config.strict_mode) diff --git a/vspec/vssexporters/vss2id.py b/vspec/vssexporters/vss2id.py index 0fbc882d..3d6a029e 100644 --- a/vspec/vssexporters/vss2id.py +++ b/vspec/vssexporters/vss2id.py @@ -59,7 +59,7 @@ def generate_split_id( @return: tuple of hashed string and id counter """ - name = node.fka[0] if hasattr(node, 'fka') and node.fka else node.qualified_name() + name = node.fka[0] if hasattr(node, "fka") and node.fka else node.qualified_name() identifier = get_node_identifier_bytes( name, node.data_type_str, From e5383fd8a3c6680b123d9616c3f3080a17b64bba Mon Sep 17 00:00:00 2001 From: Adnan Bekan Date: Tue, 30 Jan 2024 13:43:52 +0100 Subject: [PATCH 8/8] Patch/fka validation (#16) * check if fka is a list Signed-off-by: Adnan Bekan * update documentation Signed-off-by: Adnan Bekan --------- Signed-off-by: Adnan Bekan --- docs/vspec2id.md | 23 ++++++++++++++++++++--- vspec/vssexporters/vss2id.py | 5 ++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/docs/vspec2id.md b/docs/vspec2id.md index e948de64..ec0705f6 100644 --- a/docs/vspec2id.md +++ b/docs/vspec2id.md @@ -9,8 +9,7 @@ with a 4-byte identifier. ```bash usage: vspec2id.py [-h] [-I dir] [-e EXTENDED_ATTRIBUTES] [-s] [--abort-on-unknown-attribute] [--abort-on-name-style] [--format format] [--uuid] [--no-expand] [-o overlays] [-u unit_file] - [-q quantity_file] [-vt vspec_types_file] [-ot ] [--json-all-extended-attributes] [--json-pretty] [--yaml-all-extended-attributes] [-v version] [--all-idl-features] - [--gqlfield GQLFIELD GQLFIELD] [--jsonschema-all-extended-attributes] [--jsonschema-disallow-additional-properties] [--jsonschema-require-all-properties] [--jsonschema-pretty] + [-q quantity_file] [-vt vspec_types_file] [-ot ] [--yaml-all-extended-attributes] [-v version] [--all-idl-features] [--validate-static-uid VALIDATE_STATIC_UID] [--only-validate-no-export] [--strict-mode] @@ -85,8 +84,26 @@ A.B.NewName: description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. fka: ['A.B.OlderName', 'A.B.OldName'] ``` +or +``` +A.B.NewName: + datatype: string + type: actuator + allowed: ["YES", "NO"] + description: A.B.NewName's old name is 'OldName'. And its even older name is 'OlderName'. + fka: A.B.OlderName +``` -As stated if you want to rename the node `A.B.NewName` to `A.NewName` you can also write the `fka` attribute stating its legacy path. For hashing function in previous case `A.B.OlderName` will be used. +In order to add fka attribute, one can add fka directly into the vspec file or use overlay feature of vss-tools. + +Example mycustom-overlay-fka.vspec +``` +A.B.NewName: + datatype: string + type: actuator + fka: A.B.OlderName +``` +As stated if you want to rename the node `A.B.NewName` to `A.NewName` you can also write the Formerly Known As `fka` attribute stating its legacy path. For hashing function in previous case `A.B.OlderName` will be used. To summarize these are the `BREAKING CHANGES` that affect the hash and `NON-BREAKING CHANGES` that throw warnings only: diff --git a/vspec/vssexporters/vss2id.py b/vspec/vssexporters/vss2id.py index 3d6a029e..3a38c0a2 100644 --- a/vspec/vssexporters/vss2id.py +++ b/vspec/vssexporters/vss2id.py @@ -59,7 +59,10 @@ def generate_split_id( @return: tuple of hashed string and id counter """ - name = node.fka[0] if hasattr(node, "fka") and node.fka else node.qualified_name() + if hasattr(node, "fka") and node.fka: + name = node.fka[0] if isinstance(node.fka, list) else node.fka + else: + name = node.qualified_name() identifier = get_node_identifier_bytes( name, node.data_type_str,