forked from XiaoMi/ha_xiaomi_home
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcheck_rule_format.py
293 lines (250 loc) · 10.7 KB
/
check_rule_format.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# -*- coding: utf-8 -*-
"""Test rule format."""
import json
from os import listdir, path
from typing import Optional
import pytest
import yaml
ROOT_PATH: str = path.dirname(path.abspath(__file__))
TRANS_RELATIVE_PATH: str = path.join(
ROOT_PATH, '../custom_components/xiaomi_home/translations')
MIOT_I18N_RELATIVE_PATH: str = path.join(
ROOT_PATH, '../custom_components/xiaomi_home/miot/i18n')
SPEC_BOOL_TRANS_FILE = path.join(
ROOT_PATH,
'../custom_components/xiaomi_home/miot/specs/bool_trans.json')
SPEC_MULTI_LANG_FILE = path.join(
ROOT_PATH,
'../custom_components/xiaomi_home/miot/specs/multi_lang.json')
SPEC_FILTER_FILE = path.join(
ROOT_PATH,
'../custom_components/xiaomi_home/miot/specs/spec_filter.json')
def load_json_file(file_path: str) -> Optional[dict]:
try:
with open(file_path, 'r', encoding='utf-8') as file:
return json.load(file)
except FileNotFoundError:
print(file_path, 'is not found.')
return None
except json.JSONDecodeError:
print(file_path, 'is not a valid JSON file.')
return None
def save_json_file(file_path: str, data: dict) -> None:
with open(file_path, 'w', encoding='utf-8') as file:
json.dump(data, file, ensure_ascii=False, indent=4)
def load_yaml_file(file_path: str) -> Optional[dict]:
try:
with open(file_path, 'r', encoding='utf-8') as file:
return yaml.safe_load(file)
except FileNotFoundError:
print(file_path, 'is not found.')
return None
except yaml.YAMLError:
print(file_path, 'is not a valid YAML file.')
return None
def dict_str_str(d: dict) -> bool:
"""restricted format: dict[str, str]"""
if not isinstance(d, dict):
return False
for k, v in d.items():
if not isinstance(k, str) or not isinstance(v, str):
return False
return True
def dict_str_dict(d: dict) -> bool:
"""restricted format: dict[str, dict]"""
if not isinstance(d, dict):
return False
for k, v in d.items():
if not isinstance(k, str) or not isinstance(v, dict):
return False
return True
def nested_2_dict_str_str(d: dict) -> bool:
"""restricted format: dict[str, dict[str, str]]"""
if not dict_str_dict(d):
return False
for v in d.values():
if not dict_str_str(v):
return False
return True
def nested_3_dict_str_str(d: dict) -> bool:
"""restricted format: dict[str, dict[str, dict[str, str]]]"""
if not dict_str_dict(d):
return False
for v in d.values():
if not nested_2_dict_str_str(v):
return False
return True
def spec_filter(d: dict) -> bool:
"""restricted format: dict[str, dict[str, list<str>]]"""
if not dict_str_dict(d):
return False
for value in d.values():
for k, v in value.items():
if not isinstance(k, str) or not isinstance(v, list):
return False
if not all(isinstance(i, str) for i in v):
return False
return True
def bool_trans(d: dict) -> bool:
"""dict[str, dict[str, str] | dict[str, dict[str, str]] ]"""
if not isinstance(d, dict):
return False
if 'data' not in d or 'translate' not in d:
return False
if not dict_str_str(d['data']):
return False
if not nested_3_dict_str_str(d['translate']):
return False
default_trans: dict = d['translate'].pop('default')
if not default_trans:
print('default trans is empty')
return False
default_keys: set[str] = set(default_trans.keys())
for key, trans in d['translate'].items():
trans_keys: set[str] = set(trans.keys())
if set(trans.keys()) != default_keys:
print('bool trans inconsistent', key, default_keys, trans_keys)
return False
return True
def compare_dict_structure(dict1: dict, dict2: dict) -> bool:
if not isinstance(dict1, dict) or not isinstance(dict2, dict):
print('invalid type')
return False
if dict1.keys() != dict2.keys():
print('inconsistent key values, ', dict1.keys(), dict2.keys())
return False
for key in dict1:
if isinstance(dict1[key], dict) and isinstance(dict2[key], dict):
if not compare_dict_structure(dict1[key], dict2[key]):
print('inconsistent key values, dict, ', key)
return False
elif isinstance(dict1[key], list) and isinstance(dict2[key], list):
if not all(
isinstance(i, type(j))
for i, j in zip(dict1[key], dict2[key])):
print('inconsistent key values, list, ', key)
return False
elif not isinstance(dict1[key], type(dict2[key])):
print('inconsistent key values, type, ', key)
return False
return True
def sort_bool_trans(file_path: str):
trans_data: dict = load_json_file(file_path=file_path)
trans_data['data'] = dict(sorted(trans_data['data'].items()))
for key, trans in trans_data['translate'].items():
trans_data['translate'][key] = dict(sorted(trans.items()))
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):
filter_data: dict = load_json_file(file_path=file_path)
filter_data = dict(sorted(filter_data.items()))
for urn, spec in filter_data.items():
filter_data[urn] = dict(sorted(spec.items()))
return filter_data
@pytest.mark.github
def test_bool_trans():
data: dict = load_json_file(SPEC_BOOL_TRANS_FILE)
assert data, f'load {SPEC_BOOL_TRANS_FILE} failed'
assert bool_trans(data), f'{SPEC_BOOL_TRANS_FILE} format error'
@pytest.mark.github
def test_spec_filter():
data: dict = load_json_file(SPEC_FILTER_FILE)
assert data, f'load {SPEC_FILTER_FILE} failed'
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
def test_miot_i18n():
for file_name in listdir(MIOT_I18N_RELATIVE_PATH):
file_path: str = path.join(MIOT_I18N_RELATIVE_PATH, file_name)
data: dict = load_json_file(file_path)
assert data, f'load {file_path} failed'
assert nested_3_dict_str_str(data), f'{file_path} format error'
@pytest.mark.github
def test_translations():
for file_name in listdir(TRANS_RELATIVE_PATH):
file_path: str = path.join(TRANS_RELATIVE_PATH, file_name)
data: dict = load_json_file(file_path)
assert data, f'load {file_path} failed'
assert dict_str_dict(data), f'{file_path} format error'
@pytest.mark.github
def test_miot_lang_integrity():
# pylint: disable=import-outside-toplevel
from miot.const import INTEGRATION_LANGUAGES
integration_lang_list: list[str] = [
f'{key}.json' for key in list(INTEGRATION_LANGUAGES.keys())]
translations_names: set[str] = set(listdir(TRANS_RELATIVE_PATH))
assert len(translations_names) == len(integration_lang_list)
assert translations_names == set(integration_lang_list)
i18n_names: set[str] = set(listdir(MIOT_I18N_RELATIVE_PATH))
assert len(i18n_names) == len(translations_names)
assert i18n_names == translations_names
bool_trans_data: set[str] = load_json_file(SPEC_BOOL_TRANS_FILE)
bool_trans_names: set[str] = set(
bool_trans_data['translate']['default'].keys())
assert len(bool_trans_names) == len(translations_names)
# Check translation files structure
default_dict: dict = load_json_file(
path.join(TRANS_RELATIVE_PATH, integration_lang_list[0]))
for name in list(integration_lang_list)[1:]:
compare_dict: dict = load_json_file(
path.join(TRANS_RELATIVE_PATH, name))
if not compare_dict_structure(default_dict, compare_dict):
print('compare_dict_structure failed /translations, ', name)
assert False
# Check i18n files structure
default_dict = load_json_file(
path.join(MIOT_I18N_RELATIVE_PATH, integration_lang_list[0]))
for name in list(integration_lang_list)[1:]:
compare_dict: dict = load_json_file(
path.join(MIOT_I18N_RELATIVE_PATH, name))
if not compare_dict_structure(default_dict, compare_dict):
print('compare_dict_structure failed /miot/i18n, ', name)
assert False
@pytest.mark.github
def test_miot_data_sort():
# pylint: disable=import-outside-toplevel
from miot.const import INTEGRATION_LANGUAGES
sort_langs: dict = dict(sorted(INTEGRATION_LANGUAGES.items()))
assert list(INTEGRATION_LANGUAGES.keys()) == list(sort_langs.keys()), (
'INTEGRATION_LANGUAGES not sorted, correct order\r\n'
f'{list(sort_langs.keys())}')
assert json.dumps(
load_json_file(file_path=SPEC_BOOL_TRANS_FILE)) == json.dumps(
sort_bool_trans(file_path=SPEC_BOOL_TRANS_FILE)), (
f'{SPEC_BOOL_TRANS_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_MULTI_LANG_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)), (
f'{SPEC_FILTER_FILE} not sorted, goto project root path'
' and run the following command sorting, ',
'pytest -s -v -m update ./test/check_rule_format.py')
@pytest.mark.update
def test_sort_spec_data():
sort_data: dict = sort_bool_trans(file_path=SPEC_BOOL_TRANS_FILE)
save_json_file(file_path=SPEC_BOOL_TRANS_FILE, data=sort_data)
print(SPEC_BOOL_TRANS_FILE, 'formatted.')
sort_data = sort_multi_lang(file_path=SPEC_MULTI_LANG_FILE)
save_json_file(file_path=SPEC_MULTI_LANG_FILE, data=sort_data)
print(SPEC_MULTI_LANG_FILE, 'formatted.')
sort_data = sort_spec_filter(file_path=SPEC_FILTER_FILE)
save_json_file(file_path=SPEC_FILTER_FILE, data=sort_data)
print(SPEC_FILTER_FILE, 'formatted.')