Test for circular dependencies using manifests (#22908)

* Integration dependencies

* Lint

* Lint

* Fix one test

* Lint

* Fix load custom component integration

Fix async issue
Add circular dependency detection in manifest validation

* Fix test

* Address review comment

* Apply suggestions from code review

Co-Authored-By: balloob <paulus@home-assistant.io>
This commit is contained in:
Paulus Schoutsen 2019-04-09 09:30:32 -07:00 committed by Jason Hu
parent 4110bd0acf
commit cac00f5b26
11 changed files with 314 additions and 89 deletions

View file

@ -18,10 +18,16 @@ MANIFEST_SCHEMA = vol.Schema({
})
components_path = pathlib.Path('homeassistant/components')
COMPONENTS_PATH = pathlib.Path('homeassistant/components')
def validate_integration(path):
def validate_dependency(path, dependency, loaded, loading):
"""Validate dependency is exist and no circular dependency."""
dep_path = path.parent / dependency
return validate_integration(dep_path, loaded, loading)
def validate_integration(path, loaded, loading):
"""Validate that an integrations has a valid manifest."""
errors = []
path = pathlib.Path(path)
@ -29,7 +35,7 @@ def validate_integration(path):
manifest_path = path / 'manifest.json'
if not manifest_path.is_file():
errors.append('File manifest.json not found')
errors.append('Manifest file {} not found'.format(manifest_path))
return errors # Fatal error
try:
@ -47,10 +53,18 @@ def validate_integration(path):
errors.append('Domain does not match dir name')
for dep in manifest['dependencies']:
dep_manifest = path.parent / dep / 'manifest.json'
if not dep_manifest.is_file():
errors.append("Unable to find dependency {}".format(dep))
if dep in loaded:
continue
if dep in loading:
errors.append("Found circular dependency {} in {}".format(
dep, path
))
continue
loading.add(dep)
errors.extend(validate_dependency(path, dep, loaded, loading))
loaded.add(path.name)
return errors
@ -58,11 +72,11 @@ def validate_all():
"""Validate all integrations."""
invalid = []
for fil in components_path.iterdir():
for fil in COMPONENTS_PATH.iterdir():
if fil.is_file() or fil.name == '__pycache__':
continue
errors = validate_integration(fil)
errors = validate_integration(fil, set(), set())
if errors:
invalid.append((fil, errors))