Skip to content

Commit

Permalink
Refactor array handling and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
erikbosch committed Jul 10, 2023
1 parent 9f3ddb9 commit b319388
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 35 deletions.
82 changes: 47 additions & 35 deletions kuksa-client/kuksa_client/grpc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,27 @@ def from_message(cls, message: types_pb2.Datapoint):
) if message.HasField('timestamp') else None,
)


def cast_array_values(cast, array):

array = array.strip('[]')
pattern = r'(?:\\"|[^",])*"(?:\\"|[^"])*"|[^",]+'
values = re.findall(pattern, array)
for item in values:
if item == '':
#skip
pass
else:
yield cast(item)

def cast_bool(value) -> bool:
if value in ('False', 'false', 'F', 'f'):
value = 0
return bool(value)

def cast_str(value) -> str:
return str(value).replace('\"', '').replace('\\', '"').strip()

def to_message(self, value_type: DataType) -> types_pb2.Datapoint:
message = types_pb2.Datapoint()

Expand All @@ -323,27 +344,6 @@ def set_array_attr(obj, attr, values):
array.Clear()
array.values.extend(values)

def cast_array_values(cast, array):
array = array.strip('[]')
pattern = r'(?:\\.|[^",])*"(?:\\.|[^"])*"|[^",]+'
values = re.findall(pattern, array)
for item in values:
if item == '':
#skip
pass
else:
if cast == str:
item = item.replace('\"', '').replace('\\', '"').strip()
yield cast(item)

def cast_bool(value):
if value in ('False', 'false', 'F', 'f'):
value = 0
return bool(value)

def cast_str(value):
return str(value).replace('\"', '').replace('\\', '"').strip()


field, set_field, cast_field = {
DataType.INT8: ('int32', setattr, int),
Expand All @@ -356,20 +356,32 @@ def cast_str(value):
DataType.INT64: ('int64', setattr, int),
DataType.FLOAT: ('float', setattr, float),
DataType.DOUBLE: ('double', setattr, float),
DataType.BOOLEAN: ('bool', setattr, cast_bool),
DataType.STRING: ('string', setattr, cast_str),
DataType.INT8_ARRAY: ('int32_array', set_array_attr, lambda array: cast_array_values(int, array)),
DataType.INT16_ARRAY: ('int32_array', set_array_attr, lambda array: cast_array_values(int, array)),
DataType.INT32_ARRAY: ('int32_array', set_array_attr, lambda array: cast_array_values(int, array)),
DataType.UINT8_ARRAY: ('uint32_array', set_array_attr, lambda array: cast_array_values(int, array)),
DataType.UINT16_ARRAY: ('uint32_array', set_array_attr, lambda array: cast_array_values(int, array)),
DataType.UINT32_ARRAY: ('uint32_array', set_array_attr, lambda array: cast_array_values(int, array)),
DataType.UINT64_ARRAY: ('uint64_array', set_array_attr, lambda array: cast_array_values(int, array)),
DataType.INT64_ARRAY: ('int64_array', set_array_attr, lambda array: cast_array_values(int, array)),
DataType.FLOAT_ARRAY: ('float_array', set_array_attr, lambda array: cast_array_values(float, array)),
DataType.DOUBLE_ARRAY: ('double_array', set_array_attr, lambda array: cast_array_values(float, array)),
DataType.BOOLEAN_ARRAY: ('bool_array', set_array_attr, lambda array: cast_array_values(cast_bool, array)),
DataType.STRING_ARRAY: ('string_array', set_array_attr, lambda array: cast_array_values(str, array)),
DataType.BOOLEAN: ('bool', setattr, Datapoint.cast_bool),
DataType.STRING: ('string', setattr, Datapoint.cast_str),
DataType.INT8_ARRAY: ('int32_array', set_array_attr,
lambda array: Datapoint.cast_array_values(int, array)),
DataType.INT16_ARRAY: ('int32_array', set_array_attr,
lambda array: Datapoint.cast_array_values(int, array)),
DataType.INT32_ARRAY: ('int32_array', set_array_attr,
lambda array: Datapoint.cast_array_values(int, array)),
DataType.UINT8_ARRAY: ('uint32_array', set_array_attr,
lambda array: Datapoint.cast_array_values(int, array)),
DataType.UINT16_ARRAY: ('uint32_array', set_array_attr,
lambda array: Datapoint.cast_array_values(int, array)),
DataType.UINT32_ARRAY: ('uint32_array', set_array_attr,
lambda array: Datapoint.cast_array_values(int, array)),
DataType.UINT64_ARRAY: ('uint64_array', set_array_attr,
lambda array: Datapoint.cast_array_values(int, array)),
DataType.INT64_ARRAY: ('int64_array', set_array_attr,
lambda array: Datapoint.cast_array_values(int, array)),
DataType.FLOAT_ARRAY: ('float_array', set_array_attr,
lambda array: Datapoint.cast_array_values(float, array)),
DataType.DOUBLE_ARRAY: ('double_array', set_array_attr,
lambda array: Datapoint.cast_array_values(float, array)),
DataType.BOOLEAN_ARRAY: ('bool_array', set_array_attr,
lambda array: Datapoint.cast_array_values(Datapoint.cast_bool, array)),
DataType.STRING_ARRAY: ('string_array', set_array_attr,
lambda array: Datapoint.cast_array_values(Datapoint.cast_str, array)),
}.get(value_type, (None, None, None))
if self.value is not None:
if all((field, set_field, cast_field)):
Expand Down
110 changes: 110 additions & 0 deletions kuksa-client/tests/test_datapoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# /********************************************************************************
# * Copyright (c) 2023 Contributors to the Eclipse Foundation
# *
# * See the NOTICE file(s) distributed with this work for additional
# * information regarding copyright ownership.
# *
# * This program and the accompanying materials are made available under the
# * terms of the Apache License 2.0 which is available at
# * http://www.apache.org/licenses/LICENSE-2.0
# *
# * SPDX-License-Identifier: Apache-2.0
# ********************************************************************************/

import pytest
from kuksa_client.grpc import Datapoint

#
# Client rules:
# For simple strings like abd it is optional to quote them ("abc") or not (abc)
# Quotes are needed if you have commas ("ab, c")
# If you have duoble quotes in strings you must escape them

def test_array_parse_no_quote():
test_str = r'[say hello, abc]'
my_array = list(Datapoint.cast_array_values(Datapoint.cast_str,test_str))
assert len(my_array) == 2
assert my_array[0] == "say hello"
assert my_array[1] == "abc"

def test_array_parse_no_inside_quote():
test_str = r'["say hello","abc"]'
my_array = list(Datapoint.cast_array_values(Datapoint.cast_str,test_str))
assert len(my_array) == 2
assert my_array[0] == "say hello"
assert my_array[1] == "abc"

def test_array_parse_double_quote():
test_str = r'["say \"hello\"","abc"]'
my_array = list(Datapoint.cast_array_values(Datapoint.cast_str,test_str))
assert len(my_array) == 2
assert my_array[0] == "say \"hello\""
assert my_array[1] == "abc"

def test_array_parse_double_quote_2():
test_str = r'[say \"hello\",abc]'
my_array = list(Datapoint.cast_array_values(Datapoint.cast_str,test_str))
assert len(my_array) == 2
assert my_array[0] == "say \"hello\""
assert my_array[1] == "abc"

def test_array_parse_comma():
test_str = r'["say, hello","abc"]'
my_array = list(Datapoint.cast_array_values(Datapoint.cast_str,test_str))
assert len(my_array) == 2
assert my_array[0] == "say, hello"
assert my_array[1] == "abc"

def test_array_square():
"""No problem having square brackets as part of strings"""
test_str = r'[say hello[], abc]'
my_array = list(Datapoint.cast_array_values(Datapoint.cast_str,test_str))
assert len(my_array) == 2
assert my_array[0] == "say hello[]"
assert my_array[1] == "abc"

def test_int_no_quote():
test_str = r'[123,456]'
my_array = list(Datapoint.cast_array_values(int,test_str))
assert len(my_array) == 2
assert my_array[0] == 123
assert my_array[1] == 456

def test_int_quote():
"""Quoting individual int values is not allowed"""
test_str = r'["123","456"]'
with pytest.raises(ValueError):
my_array = list(Datapoint.cast_array_values(int,test_str))


def test_float_no_quote():
test_str = r'[123,456.23]'
my_array = list(Datapoint.cast_array_values(float,test_str))
assert len(my_array) == 2
assert my_array[0] == 123
assert my_array[1] == 456.23

def test_cast_str():
"""Unquoted quotation marks shall be removed, quoted kept without quotes"""
test_str = r'"say hello"'
assert Datapoint.cast_str(test_str) == r'say hello'
test_str = r'"say \"hello\""'
assert Datapoint.cast_str(test_str) == r'say "hello"'
test_str = r'say "hello"'
assert Datapoint.cast_str(test_str) == r'say hello'

def test_cast_bool():
assert Datapoint.cast_bool("true") is True
assert Datapoint.cast_bool("True") is True
assert Datapoint.cast_bool("T") is True
assert Datapoint.cast_bool("t") is True
assert Datapoint.cast_bool("false") is False
assert Datapoint.cast_bool("False") is False
assert Datapoint.cast_bool("F") is False
assert Datapoint.cast_bool("f") is False

# And then some other, treated as true for now
assert Datapoint.cast_bool("Ja") is True
assert Datapoint.cast_bool("Nein") is True
assert Datapoint.cast_bool("Doch") is True

0 comments on commit b319388

Please sign in to comment.