mirror of
https://github.com/XiaoMi/ha_xiaomi_home.git
synced 2025-04-02 07:45:31 +08:00
feat: check rule format with pytest
This commit is contained in:
parent
42399ff1f6
commit
d79f13e76f
27
.github/workflows/test.yaml
vendored
Normal file
27
.github/workflows/test.yaml
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check-rule-format:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validate-lint]
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pytest pytest-asyncio
|
||||
|
||||
- name: Check rule format with pytest
|
||||
run: |
|
||||
pytest -v -s ./test/check_rule_format.py
|
17
.github/workflows/validate.yaml
vendored
17
.github/workflows/validate.yaml
vendored
@ -3,10 +3,11 @@ name: Validate
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
validate-hassfest:
|
||||
@ -30,16 +31,6 @@ jobs:
|
||||
category: integration
|
||||
ignore: brands
|
||||
|
||||
validate-format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check format
|
||||
run: |
|
||||
./custom_components/xiaomi_home/test/test_all.sh
|
||||
|
||||
validate-lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@ -51,7 +42,7 @@ jobs:
|
||||
python -m pip install --upgrade pip
|
||||
pip install pylint
|
||||
|
||||
- name: Analyse the code with pylint
|
||||
- name: Static analyse the code with pylint
|
||||
run: |
|
||||
pylint $(git ls-files '*.py')
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
"""Check if a file is a valid JSON file.
|
||||
|
||||
Usage:
|
||||
python json_check.py [JSON file path]
|
||||
|
||||
Example:
|
||||
python json_check.py multi_lang.json
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
def check_json_file(file_path):
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8") as file:
|
||||
json.load(file)
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
print(file_path, "is not found.")
|
||||
return False
|
||||
except json.JSONDecodeError:
|
||||
print(file_path, "is not a valid JSON file.")
|
||||
return False
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Check if a file is a valid JSON file.")
|
||||
parser.add_argument("file_path", help="JSON file path")
|
||||
args = parser.parse_args()
|
||||
script_name = os.path.basename(__file__)
|
||||
file_name = os.path.basename(args.file_path)
|
||||
|
||||
if not check_json_file(args.file_path):
|
||||
print(args.file_path, script_name, "FAIL")
|
||||
sys.exit(1)
|
||||
|
||||
print(script_name, file_name, "PASS")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,127 +0,0 @@
|
||||
"""Check if conversion rules are valid.
|
||||
|
||||
The files to be checked are in the directory of ../miot/specs/
|
||||
To run this script, PYTHONPATH must be set first.
|
||||
See test_all.sh for the usage.
|
||||
|
||||
You can run all tests by running:
|
||||
```
|
||||
./test_all.sh
|
||||
```
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
|
||||
def load_json(file_path: str) -> dict:
|
||||
"""Load json file."""
|
||||
with open(file_path, "r", encoding="utf-8") as file:
|
||||
data = json.load(file)
|
||||
return data
|
||||
|
||||
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
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
script_name = os.path.basename(__file__)
|
||||
|
||||
source_dir = "../miot/specs"
|
||||
if not bool_trans(load_json(f"{source_dir}/bool_trans.json")):
|
||||
print(script_name, "bool_trans FAIL")
|
||||
sys.exit(1)
|
||||
if not nested_3_dict_str_str(load_json(f"{source_dir}/multi_lang.json")):
|
||||
print(script_name, "multi_lang FAIL")
|
||||
sys.exit(1)
|
||||
if not spec_filter(load_json(f"{source_dir}/spec_filter.json")):
|
||||
print(script_name, "spec_filter FAIL")
|
||||
sys.exit(1)
|
||||
|
||||
source_dir = "../miot/i18n"
|
||||
if not nested_3_dict_str_str(load_json(f"{source_dir}/de.json")):
|
||||
print(script_name, "i18n de.json FAIL")
|
||||
sys.exit(1)
|
||||
if not nested_3_dict_str_str(load_json(f"{source_dir}/en.json")):
|
||||
print(script_name, "i18n en.json FAIL")
|
||||
sys.exit(1)
|
||||
if not nested_3_dict_str_str(load_json(f"{source_dir}/es.json")):
|
||||
print(script_name, "i18n es.json FAIL")
|
||||
sys.exit(1)
|
||||
if not nested_3_dict_str_str(load_json(f"{source_dir}/fr.json")):
|
||||
print(script_name, "i18n fr.json FAIL")
|
||||
sys.exit(1)
|
||||
if not nested_3_dict_str_str(load_json(f"{source_dir}/ja.json")):
|
||||
print(script_name, "i18n ja.json FAIL")
|
||||
sys.exit(1)
|
||||
if not nested_3_dict_str_str(load_json(f"{source_dir}/ru.json")):
|
||||
print(script_name, "i18n ru.json FAIL")
|
||||
sys.exit(1)
|
||||
if not nested_3_dict_str_str(load_json(f"{source_dir}/zh-Hans.json")):
|
||||
print(script_name, "i18n zh-Hans.json FAIL")
|
||||
sys.exit(1)
|
||||
if not nested_3_dict_str_str(load_json(f"{source_dir}/zh-Hant.json")):
|
||||
print(script_name, "i18n zh-Hant.json FAIL")
|
||||
sys.exit(1)
|
||||
|
||||
print(script_name, "PASS")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,29 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Get the script path.
|
||||
script_path=$(dirname "$0")
|
||||
# Change to the script path.
|
||||
cd "$script_path"
|
||||
# Set PYTHONPATH.
|
||||
cd ..
|
||||
export PYTHONPATH=`pwd`
|
||||
echo "PYTHONPATH=$PYTHONPATH"
|
||||
cd -
|
||||
|
||||
# Run the tests.
|
||||
export source_dir="../miot/specs"
|
||||
python3 json_format.py $source_dir/bool_trans.json
|
||||
python3 json_format.py $source_dir/multi_lang.json
|
||||
python3 json_format.py $source_dir/spec_filter.json
|
||||
export source_dir="../miot/i18n"
|
||||
python3 json_format.py $source_dir/de.json
|
||||
python3 json_format.py $source_dir/en.json
|
||||
python3 json_format.py $source_dir/es.json
|
||||
python3 json_format.py $source_dir/fr.json
|
||||
python3 json_format.py $source_dir/ja.json
|
||||
python3 json_format.py $source_dir/ru.json
|
||||
python3 json_format.py $source_dir/zh-Hans.json
|
||||
python3 json_format.py $source_dir/zh-Hant.json
|
||||
python3 rule_format.py
|
122
test/check_rule_format.py
Normal file
122
test/check_rule_format.py
Normal file
@ -0,0 +1,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Test rule format."""
|
||||
import json
|
||||
from os import listdir, path
|
||||
from typing import Optional
|
||||
|
||||
SOURCE_DIR: str = path.dirname(path.abspath(__file__))
|
||||
|
||||
|
||||
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 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
|
||||
return True
|
||||
|
||||
|
||||
def test_bool_trans():
|
||||
data: dict = load_json_file(
|
||||
path.join(
|
||||
SOURCE_DIR,
|
||||
'../custom_components/xiaomi_home/miot/specs/bool_trans.json'))
|
||||
assert data
|
||||
assert bool_trans(data)
|
||||
|
||||
|
||||
def test_spec_filter():
|
||||
data: dict = load_json_file(
|
||||
path.join(
|
||||
SOURCE_DIR,
|
||||
'../custom_components/xiaomi_home/miot/specs/spec_filter.json'))
|
||||
assert data
|
||||
assert spec_filter(data)
|
||||
|
||||
|
||||
def test_multi_lang():
|
||||
data: dict = load_json_file(
|
||||
path.join(
|
||||
SOURCE_DIR,
|
||||
'../custom_components/xiaomi_home/miot/specs/multi_lang.json'))
|
||||
assert data
|
||||
assert nested_3_dict_str_str(data)
|
||||
|
||||
|
||||
def test_miot_i18n():
|
||||
i18n_path: str = path.join(
|
||||
SOURCE_DIR, '../custom_components/xiaomi_home/miot/i18n')
|
||||
for file_name in listdir(i18n_path):
|
||||
file_path: str = path.join(i18n_path, file_name)
|
||||
data: dict = load_json_file(file_path)
|
||||
assert data
|
||||
assert nested_3_dict_str_str(data)
|
Loading…
x
Reference in New Issue
Block a user