feat: update test case logic

This commit is contained in:
topsworld 2025-01-17 14:36:48 +08:00
parent a79fe144ab
commit ee1a910205
6 changed files with 229 additions and 244 deletions

View File

@ -977,8 +977,8 @@ class _SpecBoolTranslation:
DEFAULT_INTEGRATION_LANGUAGE, None)) DEFAULT_INTEGRATION_LANGUAGE, None))
if data_default: if data_default:
self._data_default = [ self._data_default = [
{'value': True, 'description': data_default[True]}, {'value': True, 'description': data_default['true']},
{'value': False, 'description': data_default[False]} {'value': False, 'description': data_default['false']}
] ]
for urn, key in data['data'].items(): for urn, key in data['data'].items():
@ -991,8 +991,8 @@ class _SpecBoolTranslation:
DEFAULT_INTEGRATION_LANGUAGE, None)) DEFAULT_INTEGRATION_LANGUAGE, None))
if trans_data: if trans_data:
self._data[urn] = [ self._data[urn] = [
{'value': True, 'description': trans_data[True]}, {'value': True, 'description': trans_data['true']},
{'value': False, 'description': trans_data[False]} {'value': False, 'description': trans_data['false']}
] ]
async def deinit_async(self) -> None: async def deinit_async(self) -> None:

View File

@ -65,13 +65,13 @@ from cryptography import x509
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ed25519 from cryptography.hazmat.primitives.asymmetric import ed25519
from custom_components.xiaomi_home.miot.common import MIoTHttp
# pylint: disable=relative-beyond-top-level # pylint: disable=relative-beyond-top-level
from .const import ( from .const import (
MANUFACTURER_EFFECTIVE_TIME, MANUFACTURER_EFFECTIVE_TIME,
MIHOME_CA_CERT_STR, MIHOME_CA_CERT_STR,
MIHOME_CA_CERT_SHA256) MIHOME_CA_CERT_SHA256)
from .common import MIoTHttp
from .miot_error import MIoTCertError, MIoTError, MIoTStorageError from .miot_error import MIoTCertError, MIoTError, MIoTStorageError
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -217,7 +217,8 @@ class MIoTStorage:
w_bytes = json.dumps(data).encode('utf-8') w_bytes = json.dumps(data).encode('utf-8')
else: else:
_LOGGER.error( _LOGGER.error(
'save error, unsupported data type, %s', type(data).__name__) 'save error, unsupported data type, %s',
type(data).__name__)
return False return False
with open(full_path, 'wb') as w_file: with open(full_path, 'wb') as w_file:
w_file.write(w_bytes) w_file.write(w_bytes)

View File

@ -59,188 +59,188 @@ data:
urn:miot-spec-v2:property:wifi-ssid-hidden:000000E3: yes_no urn:miot-spec-v2:property:wifi-ssid-hidden:000000E3: yes_no
urn:miot-spec-v2:property:wind-reverse:00000117: yes_no urn:miot-spec-v2:property:wind-reverse:00000117: yes_no
translate: translate:
default:
de:
true: Wahr
false: Falsch
en:
true: True
false: False
es:
true: Verdadero
false: Falso
fr:
true: Vrai
false: Faux
it:
true: Vero
false: Falso
ja:
true:
false:
nl:
true: True
false: False
pt:
true: True
false: False
pt-BR:
true: True
false: False
ru:
true: Истина
false: Ложь
zh-Hans:
true:
false:
zh-Hant:
true:
false:
open_close:
de:
true: Öffnen
false: Schließen
en:
true: Open
false: Close
es:
true: Abierto
false: Cerrado
fr:
true: Ouvert
false: Fermer
it:
true: Aperto
false: Chiuso
ja:
true: 開く
false: 閉じる
nl:
true: Open
false: Dicht
pt:
true: Aberto
false: Fechado
pt-BR:
true: Aberto
false: Fechado
ru:
true: Открыть
false: Закрыть
zh-Hans:
true: 开启
false: 关闭
zh-Hant:
true: 開啟
false: 關閉
yes_no:
de:
true: Ja
false: Nein
en:
true: Yes
false: No
es:
true:
false: No
fr:
true: Oui
false: Non
it:
true: Si
false: No
ja:
true: はい
false: いいえ
nl:
true: Ja
false: Nee
pt:
true: Sim
false: Não
pt-BR:
true: Sim
false: Não
ru:
true: Да
false: Нет
zh-Hans:
true:
false:
zh-Hant:
true:
false:
motion_state:
de:
true: Bewegung erkannt
false: Keine Bewegung erkannt
en:
true: Motion Detected
false: No Motion Detected
es:
true: Movimiento detectado
false: No se detecta movimiento
fr:
true: Mouvement détecté
false: Aucun mouvement détecté
it:
true: Movimento Rilevato,
false: Nessun Movimento Rilevato
ja:
true: 動きを検知
false: 動きが検出されません
nl:
true: Contact
false: Geen contact
pt:
true: Contato
false: Sem contato
pt-BR:
true: Contato
false: Sem contato
ru:
true: Обнаружено движение
false: Движение не обнаружено
zh-Hans:
true: 有人
false: 无人
zh-Hant:
true: 有人
false: 無人
contact_state: contact_state:
de: de:
true: Kontakt 'false': Kein Kontakt
false: Kein Kontakt 'true': Kontakt
en: en:
true: Contact 'false': No Contact
false: No Contact 'true': Contact
es: es:
true: Contacto 'false': Sin contacto
false: Sin contacto 'true': Contacto
fr: fr:
true: Contact 'false': Pas de contact
false: Pas de contact 'true': Contact
it: it:
true: Contatto 'false': Nessun contatto
false: Nessun contatto 'true': Contatto
ja: ja:
true: 接触 'false': 非接触
false: 接触 'true': 接触
nl: nl:
true: Contact 'false': Geen contact
false: Geen contact 'true': Contact
pt: pt:
true: Contato 'false': Sem contato
false: Sem contato 'true': Contato
pt-BR: pt-BR:
true: Contato 'false': Sem contato
false: Sem contato 'true': Contato
ru: ru:
true: Контакт 'false': Нет контакта
false: Нет контакта 'true': Контакт
zh-Hans: zh-Hans:
true: 接触 'false': 分离
false: 分离 'true': 接触
zh-Hant: zh-Hant:
true: 接觸 'false': 分離
false: 分離 'true': 接觸
default:
de:
'false': Falsch
'true': Wahr
en:
'false': 'False'
'true': 'True'
es:
'false': Falso
'true': Verdadero
fr:
'false': Faux
'true': Vrai
it:
'false': Falso
'true': Vero
ja:
'false':
'true':
nl:
'false': 'False'
'true': 'True'
pt:
'false': 'False'
'true': 'True'
pt-BR:
'false': 'False'
'true': 'True'
ru:
'false': Ложь
'true': Истина
zh-Hans:
'false':
'true':
zh-Hant:
'false':
'true':
motion_state:
de:
'false': Keine Bewegung erkannt
'true': Bewegung erkannt
en:
'false': No Motion Detected
'true': Motion Detected
es:
'false': No se detecta movimiento
'true': Movimiento detectado
fr:
'false': Aucun mouvement détecté
'true': Mouvement détecté
it:
'false': Nessun Movimento Rilevato
'true': Movimento Rilevato
ja:
'false': 動きが検出されません
'true': 動きを検知
nl:
'false': Geen contact
'true': Contact
pt:
'false': Sem contato
'true': Contato
pt-BR:
'false': Sem contato
'true': Contato
ru:
'false': Движение не обнаружено
'true': Обнаружено движение
zh-Hans:
'false': 无人
'true': 有人
zh-Hant:
'false': 無人
'true': 有人
open_close:
de:
'false': Schließen
'true': Öffnen
en:
'false': Close
'true': Open
es:
'false': Cerrado
'true': Abierto
fr:
'false': Fermer
'true': Ouvert
it:
'false': Chiuso
'true': Aperto
ja:
'false': 閉じる
'true': 開く
nl:
'false': Dicht
'true': Open
pt:
'false': Fechado
'true': Aberto
pt-BR:
'false': Fechado
'true': Aberto
ru:
'false': Закрыть
'true': Открыть
zh-Hans:
'false': 关闭
'true': 开启
zh-Hant:
'false': 關閉
'true': 開啟
yes_no:
de:
'false': Nein
'true': Ja
en:
'false': 'No'
'true': 'Yes'
es:
'false': 'No'
'true':
fr:
'false': Non
'true': Oui
it:
'false': 'No'
'true': Si
ja:
'false': いいえ
'true': はい
nl:
'false': Nee
'true': Ja
pt:
'false': Não
'true': Sim
pt-BR:
'false': Não
'true': Sim
ru:
'false': Нет
'true': Да
zh-Hans:
'false':
'true':
zh-Hant:
'false':
'true':

View File

@ -1,43 +1,43 @@
urn:miot-spec-v2:device:air-purifier:0000A007:zhimi-ma4: urn:miot-spec-v2:device:air-purifier:0000A007:zhimi-ma4:
properties: properties:
- "9.*" - 9.*
- "13.*" - 13.*
- "15.*" - 15.*
services: services:
- "10" - '10'
urn:miot-spec-v2:device:curtain:0000A00C:lumi-hmcn01: urn:miot-spec-v2:device:curtain:0000A00C:lumi-hmcn01:
properties: properties:
- "5.1" - '5.1'
services: services:
- "4" - '4'
- "7" - '7'
- "8" - '8'
urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1: urn:miot-spec-v2:device:gateway:0000A019:xiaomi-hub1:
events: events:
- "2.1" - '2.1'
urn:miot-spec-v2:device:health-pot:0000A051:chunmi-a1: urn:miot-spec-v2:device:health-pot:0000A051:chunmi-a1:
services: services:
- "5" - '5'
urn:miot-spec-v2:device:light:0000A001:philips-strip3: urn:miot-spec-v2:device:light:0000A001:philips-strip3:
properties: properties:
- "2.2" - '2.2'
services: services:
- "1" - '1'
- "3" - '3'
urn:miot-spec-v2:device:light:0000A001:yeelink-color2: urn:miot-spec-v2:device:light:0000A001:yeelink-color2:
properties: properties:
- "3.*" - 3.*
- "2.5" - '2.5'
urn:miot-spec-v2:device:light:0000A001:yeelink-dnlight2: urn:miot-spec-v2:device:light:0000A001:yeelink-dnlight2:
services: services:
- "3" - '3'
urn:miot-spec-v2:device:light:0000A001:yeelink-mbulb3: urn:miot-spec-v2:device:light:0000A001:yeelink-mbulb3:
services: services:
- "3" - '3'
urn:miot-spec-v2:device:motion-sensor:0000A014:xiaomi-pir1: urn:miot-spec-v2:device:motion-sensor:0000A014:xiaomi-pir1:
services: services:
- "1" - '1'
- "5" - '5'
urn:miot-spec-v2:device:router:0000A036:xiaomi-rd03: urn:miot-spec-v2:device:router:0000A036:xiaomi-rd03:
services: services:
- "*" - '*'

View File

@ -53,7 +53,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.components.sensor import SensorEntity, SensorDeviceClass from homeassistant.components.sensor import SensorEntity, SensorDeviceClass
from homeassistant.components.sensor import DEVICE_CLASS_UNITS, SensorStateClass from homeassistant.components.sensor import DEVICE_CLASS_UNITS
from .miot.miot_device import MIoTDevice, MIoTPropertyEntity from .miot.miot_device import MIoTDevice, MIoTPropertyEntity
from .miot.miot_spec import MIoTSpecProperty from .miot.miot_spec import MIoTSpecProperty

View File

@ -16,13 +16,10 @@ MIOT_I18N_RELATIVE_PATH: str = path.join(
ROOT_PATH, '../custom_components/xiaomi_home/miot/i18n') ROOT_PATH, '../custom_components/xiaomi_home/miot/i18n')
SPEC_BOOL_TRANS_FILE = path.join( SPEC_BOOL_TRANS_FILE = path.join(
ROOT_PATH, ROOT_PATH,
'../custom_components/xiaomi_home/miot/specs/bool_trans.json') '../custom_components/xiaomi_home/miot/specs/bool_trans.yaml')
SPEC_MULTI_LANG_FILE = path.join(
ROOT_PATH,
'../custom_components/xiaomi_home/miot/specs/multi_lang.json')
SPEC_FILTER_FILE = path.join( SPEC_FILTER_FILE = path.join(
ROOT_PATH, ROOT_PATH,
'../custom_components/xiaomi_home/miot/specs/spec_filter.json') '../custom_components/xiaomi_home/miot/specs/spec_filter.yaml')
def load_json_file(file_path: str) -> Optional[dict]: def load_json_file(file_path: str) -> Optional[dict]:
@ -54,6 +51,12 @@ def load_yaml_file(file_path: str) -> Optional[dict]:
return None return None
def save_yaml_file(file_path: str, data: dict) -> None:
with open(file_path, 'w', encoding='utf-8') as file:
yaml.safe_dump(
data, file, default_flow_style=False, allow_unicode=True, indent=2)
def dict_str_str(d: dict) -> bool: def dict_str_str(d: dict) -> bool:
"""restricted format: dict[str, str]""" """restricted format: dict[str, str]"""
if not isinstance(d, dict): if not isinstance(d, dict):
@ -161,25 +164,17 @@ def compare_dict_structure(dict1: dict, dict2: dict) -> bool:
def sort_bool_trans(file_path: str): def sort_bool_trans(file_path: str):
trans_data: dict = load_json_file(file_path=file_path) trans_data = load_yaml_file(file_path=file_path)
assert isinstance(trans_data, dict), f'{file_path} format error'
trans_data['data'] = dict(sorted(trans_data['data'].items())) trans_data['data'] = dict(sorted(trans_data['data'].items()))
for key, trans in trans_data['translate'].items(): for key, trans in trans_data['translate'].items():
trans_data['translate'][key] = dict(sorted(trans.items())) trans_data['translate'][key] = dict(sorted(trans.items()))
return trans_data return trans_data
def sort_multi_lang(file_path: str):
multi_lang: dict = load_json_file(file_path=file_path)
multi_lang = dict(sorted(multi_lang.items()))
for urn, trans in multi_lang.items():
multi_lang[urn] = dict(sorted(trans.items()))
for lang, spec in multi_lang[urn].items():
multi_lang[urn][lang] = dict(sorted(spec.items()))
return multi_lang
def sort_spec_filter(file_path: str): def sort_spec_filter(file_path: str):
filter_data: dict = load_json_file(file_path=file_path) filter_data = load_yaml_file(file_path=file_path)
assert isinstance(filter_data, dict), f'{file_path} format error'
filter_data = dict(sorted(filter_data.items())) filter_data = dict(sorted(filter_data.items()))
for urn, spec in filter_data.items(): for urn, spec in filter_data.items():
filter_data[urn] = dict(sorted(spec.items())) filter_data[urn] = dict(sorted(spec.items()))
@ -188,30 +183,26 @@ def sort_spec_filter(file_path: str):
@pytest.mark.github @pytest.mark.github
def test_bool_trans(): def test_bool_trans():
data: dict = load_json_file(SPEC_BOOL_TRANS_FILE) data = load_yaml_file(SPEC_BOOL_TRANS_FILE)
assert isinstance(data, dict)
assert data, f'load {SPEC_BOOL_TRANS_FILE} failed' assert data, f'load {SPEC_BOOL_TRANS_FILE} failed'
assert bool_trans(data), f'{SPEC_BOOL_TRANS_FILE} format error' assert bool_trans(data), f'{SPEC_BOOL_TRANS_FILE} format error'
@pytest.mark.github @pytest.mark.github
def test_spec_filter(): def test_spec_filter():
data: dict = load_json_file(SPEC_FILTER_FILE) data = load_yaml_file(SPEC_FILTER_FILE)
assert isinstance(data, dict)
assert data, f'load {SPEC_FILTER_FILE} failed' assert data, f'load {SPEC_FILTER_FILE} failed'
assert spec_filter(data), f'{SPEC_FILTER_FILE} format error' assert spec_filter(data), f'{SPEC_FILTER_FILE} format error'
@pytest.mark.github
def test_multi_lang():
data: dict = load_json_file(SPEC_MULTI_LANG_FILE)
assert data, f'load {SPEC_MULTI_LANG_FILE} failed'
assert nested_3_dict_str_str(data), f'{SPEC_MULTI_LANG_FILE} format error'
@pytest.mark.github @pytest.mark.github
def test_miot_i18n(): def test_miot_i18n():
for file_name in listdir(MIOT_I18N_RELATIVE_PATH): for file_name in listdir(MIOT_I18N_RELATIVE_PATH):
file_path: str = path.join(MIOT_I18N_RELATIVE_PATH, file_name) file_path: str = path.join(MIOT_I18N_RELATIVE_PATH, file_name)
data: dict = load_json_file(file_path) data = load_json_file(file_path)
assert isinstance(data, dict)
assert data, f'load {file_path} failed' assert data, f'load {file_path} failed'
assert nested_3_dict_str_str(data), f'{file_path} format error' assert nested_3_dict_str_str(data), f'{file_path} format error'
@ -220,7 +211,8 @@ def test_miot_i18n():
def test_translations(): def test_translations():
for file_name in listdir(TRANS_RELATIVE_PATH): for file_name in listdir(TRANS_RELATIVE_PATH):
file_path: str = path.join(TRANS_RELATIVE_PATH, file_name) file_path: str = path.join(TRANS_RELATIVE_PATH, file_name)
data: dict = load_json_file(file_path) data = load_json_file(file_path)
assert isinstance(data, dict)
assert data, f'load {file_path} failed' assert data, f'load {file_path} failed'
assert dict_str_dict(data), f'{file_path} format error' assert dict_str_dict(data), f'{file_path} format error'
@ -237,15 +229,16 @@ def test_miot_lang_integrity():
i18n_names: set[str] = set(listdir(MIOT_I18N_RELATIVE_PATH)) i18n_names: set[str] = set(listdir(MIOT_I18N_RELATIVE_PATH))
assert len(i18n_names) == len(translations_names) assert len(i18n_names) == len(translations_names)
assert i18n_names == translations_names assert i18n_names == translations_names
bool_trans_data: set[str] = load_json_file(SPEC_BOOL_TRANS_FILE) bool_trans_data = load_yaml_file(SPEC_BOOL_TRANS_FILE)
assert isinstance(bool_trans_data, dict)
bool_trans_names: set[str] = set( bool_trans_names: set[str] = set(
bool_trans_data['translate']['default'].keys()) bool_trans_data['translate']['default'].keys())
assert len(bool_trans_names) == len(translations_names) assert len(bool_trans_names) == len(translations_names)
# Check translation files structure # Check translation files structure
default_dict: dict = load_json_file( default_dict = load_json_file(
path.join(TRANS_RELATIVE_PATH, integration_lang_list[0])) path.join(TRANS_RELATIVE_PATH, integration_lang_list[0]))
for name in list(integration_lang_list)[1:]: for name in list(integration_lang_list)[1:]:
compare_dict: dict = load_json_file( compare_dict = load_json_file(
path.join(TRANS_RELATIVE_PATH, name)) path.join(TRANS_RELATIVE_PATH, name))
if not compare_dict_structure(default_dict, compare_dict): if not compare_dict_structure(default_dict, compare_dict):
_LOGGER.info( _LOGGER.info(
@ -255,7 +248,7 @@ def test_miot_lang_integrity():
default_dict = load_json_file( default_dict = load_json_file(
path.join(MIOT_I18N_RELATIVE_PATH, integration_lang_list[0])) path.join(MIOT_I18N_RELATIVE_PATH, integration_lang_list[0]))
for name in list(integration_lang_list)[1:]: for name in list(integration_lang_list)[1:]:
compare_dict: dict = load_json_file( compare_dict = load_json_file(
path.join(MIOT_I18N_RELATIVE_PATH, name)) path.join(MIOT_I18N_RELATIVE_PATH, name))
if not compare_dict_structure(default_dict, compare_dict): if not compare_dict_structure(default_dict, compare_dict):
_LOGGER.info( _LOGGER.info(
@ -272,19 +265,13 @@ def test_miot_data_sort():
'INTEGRATION_LANGUAGES not sorted, correct order\r\n' 'INTEGRATION_LANGUAGES not sorted, correct order\r\n'
f'{list(sort_langs.keys())}') f'{list(sort_langs.keys())}')
assert json.dumps( assert json.dumps(
load_json_file(file_path=SPEC_BOOL_TRANS_FILE)) == json.dumps( load_yaml_file(file_path=SPEC_BOOL_TRANS_FILE)) == json.dumps(
sort_bool_trans(file_path=SPEC_BOOL_TRANS_FILE)), ( sort_bool_trans(file_path=SPEC_BOOL_TRANS_FILE)), (
f'{SPEC_BOOL_TRANS_FILE} not sorted, goto project root path' f'{SPEC_BOOL_TRANS_FILE} not sorted, goto project root path'
' and run the following command sorting, ', ' and run the following command sorting, ',
'pytest -s -v -m update ./test/check_rule_format.py') 'pytest -s -v -m update ./test/check_rule_format.py')
assert json.dumps( assert json.dumps(
load_json_file(file_path=SPEC_MULTI_LANG_FILE)) == json.dumps( load_yaml_file(file_path=SPEC_FILTER_FILE)) == json.dumps(
sort_multi_lang(file_path=SPEC_MULTI_LANG_FILE)), (
f'{SPEC_MULTI_LANG_FILE} not sorted, goto project root path'
' and run the following command sorting, ',
'pytest -s -v -m update ./test/check_rule_format.py')
assert json.dumps(
load_json_file(file_path=SPEC_FILTER_FILE)) == json.dumps(
sort_spec_filter(file_path=SPEC_FILTER_FILE)), ( sort_spec_filter(file_path=SPEC_FILTER_FILE)), (
f'{SPEC_FILTER_FILE} not sorted, goto project root path' f'{SPEC_FILTER_FILE} not sorted, goto project root path'
' and run the following command sorting, ', ' and run the following command sorting, ',
@ -294,11 +281,8 @@ def test_miot_data_sort():
@pytest.mark.update @pytest.mark.update
def test_sort_spec_data(): def test_sort_spec_data():
sort_data: dict = sort_bool_trans(file_path=SPEC_BOOL_TRANS_FILE) sort_data: dict = sort_bool_trans(file_path=SPEC_BOOL_TRANS_FILE)
save_json_file(file_path=SPEC_BOOL_TRANS_FILE, data=sort_data) save_yaml_file(file_path=SPEC_BOOL_TRANS_FILE, data=sort_data)
_LOGGER.info('%s formatted.', SPEC_BOOL_TRANS_FILE) _LOGGER.info('%s formatted.', SPEC_BOOL_TRANS_FILE)
sort_data = sort_multi_lang(file_path=SPEC_MULTI_LANG_FILE)
save_json_file(file_path=SPEC_MULTI_LANG_FILE, data=sort_data)
_LOGGER.info('%s formatted.', SPEC_MULTI_LANG_FILE)
sort_data = sort_spec_filter(file_path=SPEC_FILTER_FILE) sort_data = sort_spec_filter(file_path=SPEC_FILTER_FILE)
save_json_file(file_path=SPEC_FILTER_FILE, data=sort_data) save_yaml_file(file_path=SPEC_FILTER_FILE, data=sort_data)
_LOGGER.info('%s formatted.', SPEC_FILTER_FILE) _LOGGER.info('%s formatted.', SPEC_FILTER_FILE)