mirror of
https://github.com/esphome/esphome.git
synced 2025-07-29 14:46:40 +00:00
Factor PlatformIO buildgen out of writer.py (#9378)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
e485895d97
commit
16a426c182
@ -34,6 +34,7 @@ from esphome.const import (
|
|||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
CONF_SUBSTITUTIONS,
|
CONF_SUBSTITUTIONS,
|
||||||
CONF_TOPIC,
|
CONF_TOPIC,
|
||||||
|
ENV_NOGITIGNORE,
|
||||||
PLATFORM_ESP32,
|
PLATFORM_ESP32,
|
||||||
PLATFORM_ESP8266,
|
PLATFORM_ESP8266,
|
||||||
PLATFORM_RP2040,
|
PLATFORM_RP2040,
|
||||||
@ -209,6 +210,9 @@ def wrap_to_code(name, comp):
|
|||||||
|
|
||||||
|
|
||||||
def write_cpp(config):
|
def write_cpp(config):
|
||||||
|
if not get_bool_env(ENV_NOGITIGNORE):
|
||||||
|
writer.write_gitignore()
|
||||||
|
|
||||||
generate_cpp_contents(config)
|
generate_cpp_contents(config)
|
||||||
return write_cpp_file()
|
return write_cpp_file()
|
||||||
|
|
||||||
@ -225,10 +229,13 @@ def generate_cpp_contents(config):
|
|||||||
|
|
||||||
|
|
||||||
def write_cpp_file():
|
def write_cpp_file():
|
||||||
writer.write_platformio_project()
|
|
||||||
|
|
||||||
code_s = indent(CORE.cpp_main_section)
|
code_s = indent(CORE.cpp_main_section)
|
||||||
writer.write_cpp(code_s)
|
writer.write_cpp(code_s)
|
||||||
|
|
||||||
|
from esphome.build_gen import platformio
|
||||||
|
|
||||||
|
platformio.write_project()
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
0
esphome/build_gen/__init__.py
Normal file
0
esphome/build_gen/__init__.py
Normal file
102
esphome/build_gen/platformio.py
Normal file
102
esphome/build_gen/platformio.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from esphome.const import __version__
|
||||||
|
from esphome.core import CORE
|
||||||
|
from esphome.helpers import mkdir_p, read_file, write_file_if_changed
|
||||||
|
from esphome.writer import find_begin_end, update_storage_json
|
||||||
|
|
||||||
|
INI_AUTO_GENERATE_BEGIN = "; ========== AUTO GENERATED CODE BEGIN ==========="
|
||||||
|
INI_AUTO_GENERATE_END = "; =========== AUTO GENERATED CODE END ============"
|
||||||
|
|
||||||
|
INI_BASE_FORMAT = (
|
||||||
|
"""; Auto generated code by esphome
|
||||||
|
|
||||||
|
[common]
|
||||||
|
lib_deps =
|
||||||
|
build_flags =
|
||||||
|
upload_flags =
|
||||||
|
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def format_ini(data: dict[str, str | list[str]]) -> str:
|
||||||
|
content = ""
|
||||||
|
for key, value in sorted(data.items()):
|
||||||
|
if isinstance(value, list):
|
||||||
|
content += f"{key} =\n"
|
||||||
|
for x in value:
|
||||||
|
content += f" {x}\n"
|
||||||
|
else:
|
||||||
|
content += f"{key} = {value}\n"
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
def get_ini_content():
|
||||||
|
CORE.add_platformio_option(
|
||||||
|
"lib_deps",
|
||||||
|
[x.as_lib_dep for x in CORE.platformio_libraries.values()]
|
||||||
|
+ ["${common.lib_deps}"],
|
||||||
|
)
|
||||||
|
# Sort to avoid changing build flags order
|
||||||
|
CORE.add_platformio_option("build_flags", sorted(CORE.build_flags))
|
||||||
|
|
||||||
|
# Sort to avoid changing build unflags order
|
||||||
|
CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags))
|
||||||
|
|
||||||
|
# Add extra script for C++ flags
|
||||||
|
CORE.add_platformio_option("extra_scripts", [f"pre:{CXX_FLAGS_FILE_NAME}"])
|
||||||
|
|
||||||
|
content = "[platformio]\n"
|
||||||
|
content += f"description = ESPHome {__version__}\n"
|
||||||
|
|
||||||
|
content += f"[env:{CORE.name}]\n"
|
||||||
|
content += format_ini(CORE.platformio_options)
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
def write_ini(content):
|
||||||
|
update_storage_json()
|
||||||
|
path = CORE.relative_build_path("platformio.ini")
|
||||||
|
|
||||||
|
if os.path.isfile(path):
|
||||||
|
text = read_file(path)
|
||||||
|
content_format = find_begin_end(
|
||||||
|
text, INI_AUTO_GENERATE_BEGIN, INI_AUTO_GENERATE_END
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
content_format = INI_BASE_FORMAT
|
||||||
|
full_file = f"{content_format[0] + INI_AUTO_GENERATE_BEGIN}\n{content}"
|
||||||
|
full_file += INI_AUTO_GENERATE_END + content_format[1]
|
||||||
|
write_file_if_changed(path, full_file)
|
||||||
|
|
||||||
|
|
||||||
|
def write_project():
|
||||||
|
mkdir_p(CORE.build_path)
|
||||||
|
|
||||||
|
content = get_ini_content()
|
||||||
|
write_ini(content)
|
||||||
|
|
||||||
|
# Write extra script for C++ specific flags
|
||||||
|
write_cxx_flags_script()
|
||||||
|
|
||||||
|
|
||||||
|
CXX_FLAGS_FILE_NAME = "cxx_flags.py"
|
||||||
|
CXX_FLAGS_FILE_CONTENTS = """# Auto-generated ESPHome script for C++ specific compiler flags
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
# Add C++ specific flags
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def write_cxx_flags_script() -> None:
|
||||||
|
path = CORE.relative_build_path(CXX_FLAGS_FILE_NAME)
|
||||||
|
contents = CXX_FLAGS_FILE_CONTENTS
|
||||||
|
if not CORE.is_host:
|
||||||
|
contents += 'env.Append(CXXFLAGS=["-Wno-volatile"])'
|
||||||
|
contents += "\n"
|
||||||
|
write_file_if_changed(path, contents)
|
@ -470,6 +470,52 @@ class Library:
|
|||||||
return self.as_tuple == other.as_tuple
|
return self.as_tuple == other.as_tuple
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
|
def reconcile_with(self, other):
|
||||||
|
"""Merge two libraries, reconciling any conflicts."""
|
||||||
|
|
||||||
|
if self.name != other.name:
|
||||||
|
# Different libraries, no reconciliation possible
|
||||||
|
raise ValueError(
|
||||||
|
f"Cannot reconcile libraries with different names: {self.name} and {other.name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# repository specificity takes precedence over version specificity
|
||||||
|
if self.repository is None and other.repository is None:
|
||||||
|
pass # No repositories, no conflict, continue on
|
||||||
|
|
||||||
|
elif self.repository is None:
|
||||||
|
# incoming library has a repository, use it
|
||||||
|
self.repository = other.repository
|
||||||
|
self.version = other.version
|
||||||
|
return self
|
||||||
|
|
||||||
|
elif other.repository is None:
|
||||||
|
return self # use the repository/version already present
|
||||||
|
|
||||||
|
elif self.repository != other.repository:
|
||||||
|
raise ValueError(
|
||||||
|
f"Reconciliation failed! Libraries {self} and {other} requested with conflicting repositories!"
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.version is None and other.version is None:
|
||||||
|
return self # Arduino library reconciled against another Arduino library, current is acceptable
|
||||||
|
|
||||||
|
if self.version is None:
|
||||||
|
# incoming library has a version, use it
|
||||||
|
self.version = other.version
|
||||||
|
return self
|
||||||
|
|
||||||
|
if other.version is None:
|
||||||
|
return self # incoming library has no version, current is acceptable
|
||||||
|
|
||||||
|
# Same versions, current library is acceptable
|
||||||
|
if self.version != other.version:
|
||||||
|
raise ValueError(
|
||||||
|
f"Version pinning failed! Libraries {other} and {self} "
|
||||||
|
"requested with conflicting versions!"
|
||||||
|
)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods
|
||||||
class EsphomeCore:
|
class EsphomeCore:
|
||||||
@ -505,8 +551,8 @@ class EsphomeCore:
|
|||||||
self.main_statements: list[Statement] = []
|
self.main_statements: list[Statement] = []
|
||||||
# A list of statements to insert in the global block (includes and global variables)
|
# A list of statements to insert in the global block (includes and global variables)
|
||||||
self.global_statements: list[Statement] = []
|
self.global_statements: list[Statement] = []
|
||||||
# A set of platformio libraries to add to the project
|
# A map of platformio libraries to add to the project (shortname: (name, version, repository))
|
||||||
self.libraries: list[Library] = []
|
self.platformio_libraries: dict[str, Library] = {}
|
||||||
# A set of build flags to set in the platformio project
|
# A set of build flags to set in the platformio project
|
||||||
self.build_flags: set[str] = set()
|
self.build_flags: set[str] = set()
|
||||||
# A set of build unflags to set in the platformio project
|
# A set of build unflags to set in the platformio project
|
||||||
@ -550,7 +596,7 @@ class EsphomeCore:
|
|||||||
self.variables = {}
|
self.variables = {}
|
||||||
self.main_statements = []
|
self.main_statements = []
|
||||||
self.global_statements = []
|
self.global_statements = []
|
||||||
self.libraries = []
|
self.platformio_libraries = {}
|
||||||
self.build_flags = set()
|
self.build_flags = set()
|
||||||
self.build_unflags = set()
|
self.build_unflags = set()
|
||||||
self.defines = set()
|
self.defines = set()
|
||||||
@ -738,54 +784,22 @@ class EsphomeCore:
|
|||||||
_LOGGER.debug("Adding global: %s", expression)
|
_LOGGER.debug("Adding global: %s", expression)
|
||||||
return expression
|
return expression
|
||||||
|
|
||||||
def add_library(self, library):
|
def add_library(self, library: Library):
|
||||||
if not isinstance(library, Library):
|
if not isinstance(library, Library):
|
||||||
raise ValueError(
|
raise TypeError(
|
||||||
f"Library {library} must be instance of Library, not {type(library)}"
|
f"Library {library} must be instance of Library, not {type(library)}"
|
||||||
)
|
)
|
||||||
for other in self.libraries[:]:
|
short_name = (
|
||||||
if other.name is None or library.name is None:
|
library.name if "/" not in library.name else library.name.split("/")[-1]
|
||||||
continue
|
)
|
||||||
library_name = (
|
|
||||||
library.name if "/" not in library.name else library.name.split("/")[1]
|
|
||||||
)
|
|
||||||
other_name = (
|
|
||||||
other.name if "/" not in other.name else other.name.split("/")[1]
|
|
||||||
)
|
|
||||||
if other_name != library_name:
|
|
||||||
continue
|
|
||||||
if other.repository is not None:
|
|
||||||
if library.repository is None or other.repository == library.repository:
|
|
||||||
# Other is using a/the same repository, takes precedence
|
|
||||||
break
|
|
||||||
raise ValueError(
|
|
||||||
f"Adding named Library with repository failed! Libraries {library} and {other} "
|
|
||||||
"requested with conflicting repositories!"
|
|
||||||
)
|
|
||||||
|
|
||||||
if library.repository is not None:
|
if short_name not in self.platformio_libraries:
|
||||||
# This is more specific since its using a repository
|
|
||||||
self.libraries.remove(other)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if library.version is None:
|
|
||||||
# Other requirement is more specific
|
|
||||||
break
|
|
||||||
if other.version is None:
|
|
||||||
# Found more specific version requirement
|
|
||||||
self.libraries.remove(other)
|
|
||||||
continue
|
|
||||||
if other.version == library.version:
|
|
||||||
break
|
|
||||||
|
|
||||||
raise ValueError(
|
|
||||||
f"Version pinning failed! Libraries {library} and {other} "
|
|
||||||
"requested with conflicting versions!"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
_LOGGER.debug("Adding library: %s", library)
|
_LOGGER.debug("Adding library: %s", library)
|
||||||
self.libraries.append(library)
|
self.platformio_libraries[short_name] = library
|
||||||
return library
|
return library
|
||||||
|
|
||||||
|
self.platformio_libraries[short_name].reconcile_with(library)
|
||||||
|
return self.platformio_libraries[short_name]
|
||||||
|
|
||||||
def add_build_flag(self, build_flag: str) -> str:
|
def add_build_flag(self, build_flag: str) -> str:
|
||||||
self.build_flags.add(build_flag)
|
self.build_flags.add(build_flag)
|
||||||
|
@ -7,7 +7,6 @@ import re
|
|||||||
from esphome import loader
|
from esphome import loader
|
||||||
from esphome.config import iter_component_configs, iter_components
|
from esphome.config import iter_component_configs, iter_components
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
ENV_NOGITIGNORE,
|
|
||||||
HEADER_FILE_EXTENSIONS,
|
HEADER_FILE_EXTENSIONS,
|
||||||
PLATFORM_ESP32,
|
PLATFORM_ESP32,
|
||||||
SOURCE_FILE_EXTENSIONS,
|
SOURCE_FILE_EXTENSIONS,
|
||||||
@ -16,8 +15,6 @@ from esphome.const import (
|
|||||||
from esphome.core import CORE, EsphomeError
|
from esphome.core import CORE, EsphomeError
|
||||||
from esphome.helpers import (
|
from esphome.helpers import (
|
||||||
copy_file_if_changed,
|
copy_file_if_changed,
|
||||||
get_bool_env,
|
|
||||||
mkdir_p,
|
|
||||||
read_file,
|
read_file,
|
||||||
walk_files,
|
walk_files,
|
||||||
write_file_if_changed,
|
write_file_if_changed,
|
||||||
@ -30,8 +27,6 @@ CPP_AUTO_GENERATE_BEGIN = "// ========== AUTO GENERATED CODE BEGIN ==========="
|
|||||||
CPP_AUTO_GENERATE_END = "// =========== AUTO GENERATED CODE END ============"
|
CPP_AUTO_GENERATE_END = "// =========== AUTO GENERATED CODE END ============"
|
||||||
CPP_INCLUDE_BEGIN = "// ========== AUTO GENERATED INCLUDE BLOCK BEGIN ==========="
|
CPP_INCLUDE_BEGIN = "// ========== AUTO GENERATED INCLUDE BLOCK BEGIN ==========="
|
||||||
CPP_INCLUDE_END = "// ========== AUTO GENERATED INCLUDE BLOCK END ==========="
|
CPP_INCLUDE_END = "// ========== AUTO GENERATED INCLUDE BLOCK END ==========="
|
||||||
INI_AUTO_GENERATE_BEGIN = "; ========== AUTO GENERATED CODE BEGIN ==========="
|
|
||||||
INI_AUTO_GENERATE_END = "; =========== AUTO GENERATED CODE END ============"
|
|
||||||
|
|
||||||
CPP_BASE_FORMAT = (
|
CPP_BASE_FORMAT = (
|
||||||
"""// Auto generated code by esphome
|
"""// Auto generated code by esphome
|
||||||
@ -50,20 +45,6 @@ void loop() {
|
|||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
INI_BASE_FORMAT = (
|
|
||||||
"""; Auto generated code by esphome
|
|
||||||
|
|
||||||
[common]
|
|
||||||
lib_deps =
|
|
||||||
build_flags =
|
|
||||||
upload_flags =
|
|
||||||
|
|
||||||
""",
|
|
||||||
"""
|
|
||||||
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
|
|
||||||
UPLOAD_SPEED_OVERRIDE = {
|
UPLOAD_SPEED_OVERRIDE = {
|
||||||
"esp210": 57600,
|
"esp210": 57600,
|
||||||
}
|
}
|
||||||
@ -140,40 +121,6 @@ def update_storage_json():
|
|||||||
new.save(path)
|
new.save(path)
|
||||||
|
|
||||||
|
|
||||||
def format_ini(data: dict[str, str | list[str]]) -> str:
|
|
||||||
content = ""
|
|
||||||
for key, value in sorted(data.items()):
|
|
||||||
if isinstance(value, list):
|
|
||||||
content += f"{key} =\n"
|
|
||||||
for x in value:
|
|
||||||
content += f" {x}\n"
|
|
||||||
else:
|
|
||||||
content += f"{key} = {value}\n"
|
|
||||||
return content
|
|
||||||
|
|
||||||
|
|
||||||
def get_ini_content():
|
|
||||||
CORE.add_platformio_option(
|
|
||||||
"lib_deps", [x.as_lib_dep for x in CORE.libraries] + ["${common.lib_deps}"]
|
|
||||||
)
|
|
||||||
# Sort to avoid changing build flags order
|
|
||||||
CORE.add_platformio_option("build_flags", sorted(CORE.build_flags))
|
|
||||||
|
|
||||||
# Sort to avoid changing build unflags order
|
|
||||||
CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags))
|
|
||||||
|
|
||||||
# Add extra script for C++ flags
|
|
||||||
CORE.add_platformio_option("extra_scripts", [f"pre:{CXX_FLAGS_FILE_NAME}"])
|
|
||||||
|
|
||||||
content = "[platformio]\n"
|
|
||||||
content += f"description = ESPHome {__version__}\n"
|
|
||||||
|
|
||||||
content += f"[env:{CORE.name}]\n"
|
|
||||||
content += format_ini(CORE.platformio_options)
|
|
||||||
|
|
||||||
return content
|
|
||||||
|
|
||||||
|
|
||||||
def find_begin_end(text, begin_s, end_s):
|
def find_begin_end(text, begin_s, end_s):
|
||||||
begin_index = text.find(begin_s)
|
begin_index = text.find(begin_s)
|
||||||
if begin_index == -1:
|
if begin_index == -1:
|
||||||
@ -201,34 +148,6 @@ def find_begin_end(text, begin_s, end_s):
|
|||||||
return text[:begin_index], text[(end_index + len(end_s)) :]
|
return text[:begin_index], text[(end_index + len(end_s)) :]
|
||||||
|
|
||||||
|
|
||||||
def write_platformio_ini(content):
|
|
||||||
update_storage_json()
|
|
||||||
path = CORE.relative_build_path("platformio.ini")
|
|
||||||
|
|
||||||
if os.path.isfile(path):
|
|
||||||
text = read_file(path)
|
|
||||||
content_format = find_begin_end(
|
|
||||||
text, INI_AUTO_GENERATE_BEGIN, INI_AUTO_GENERATE_END
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
content_format = INI_BASE_FORMAT
|
|
||||||
full_file = f"{content_format[0] + INI_AUTO_GENERATE_BEGIN}\n{content}"
|
|
||||||
full_file += INI_AUTO_GENERATE_END + content_format[1]
|
|
||||||
write_file_if_changed(path, full_file)
|
|
||||||
|
|
||||||
|
|
||||||
def write_platformio_project():
|
|
||||||
mkdir_p(CORE.build_path)
|
|
||||||
|
|
||||||
content = get_ini_content()
|
|
||||||
if not get_bool_env(ENV_NOGITIGNORE):
|
|
||||||
write_gitignore()
|
|
||||||
write_platformio_ini(content)
|
|
||||||
|
|
||||||
# Write extra script for C++ specific flags
|
|
||||||
write_cxx_flags_script()
|
|
||||||
|
|
||||||
|
|
||||||
DEFINES_H_FORMAT = ESPHOME_H_FORMAT = """\
|
DEFINES_H_FORMAT = ESPHOME_H_FORMAT = """\
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "esphome/core/macros.h"
|
#include "esphome/core/macros.h"
|
||||||
@ -400,20 +319,3 @@ def write_gitignore():
|
|||||||
if not os.path.isfile(path):
|
if not os.path.isfile(path):
|
||||||
with open(file=path, mode="w", encoding="utf-8") as f:
|
with open(file=path, mode="w", encoding="utf-8") as f:
|
||||||
f.write(GITIGNORE_CONTENT)
|
f.write(GITIGNORE_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
CXX_FLAGS_FILE_NAME = "cxx_flags.py"
|
|
||||||
CXX_FLAGS_FILE_CONTENTS = """# Auto-generated ESPHome script for C++ specific compiler flags
|
|
||||||
Import("env")
|
|
||||||
|
|
||||||
# Add C++ specific flags
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def write_cxx_flags_script() -> None:
|
|
||||||
path = CORE.relative_build_path(CXX_FLAGS_FILE_NAME)
|
|
||||||
contents = CXX_FLAGS_FILE_CONTENTS
|
|
||||||
if not CORE.is_host:
|
|
||||||
contents += 'env.Append(CXXFLAGS=["-Wno-volatile"])'
|
|
||||||
contents += "\n"
|
|
||||||
write_file_if_changed(path, contents)
|
|
||||||
|
@ -473,6 +473,61 @@ class TestLibrary:
|
|||||||
|
|
||||||
assert actual == expected
|
assert actual == expected
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"target, other, result, exception",
|
||||||
|
(
|
||||||
|
(core.Library("libfoo", None), core.Library("libfoo", None), True, None),
|
||||||
|
(
|
||||||
|
core.Library("libfoo", "1.2.3"),
|
||||||
|
core.Library("libfoo", "1.2.3"),
|
||||||
|
True, # target is unchanged
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
core.Library("libfoo", None),
|
||||||
|
core.Library("libfoo", "1.2.3"),
|
||||||
|
False, # Use version from other
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
core.Library("libfoo", "1.2.3"),
|
||||||
|
core.Library("libfoo", "1.2.4"),
|
||||||
|
False,
|
||||||
|
ValueError, # Version mismatch
|
||||||
|
),
|
||||||
|
(
|
||||||
|
core.Library("libfoo", "1.2.3"),
|
||||||
|
core.Library("libbar", "1.2.3"),
|
||||||
|
False,
|
||||||
|
ValueError, # Name mismatch
|
||||||
|
),
|
||||||
|
(
|
||||||
|
core.Library(
|
||||||
|
"libfoo", "1.2.4", "https://github.com/esphome/ESPAsyncWebServer"
|
||||||
|
),
|
||||||
|
core.Library("libfoo", "1.2.3"),
|
||||||
|
True, # target is unchanged due to having a repository
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
core.Library("libfoo", "1.2.3"),
|
||||||
|
core.Library(
|
||||||
|
"libfoo", "1.2.4", "https://github.com/esphome/ESPAsyncWebServer"
|
||||||
|
),
|
||||||
|
False, # use other due to having a repository
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_reconcile(self, target, other, result, exception):
|
||||||
|
if exception is not None:
|
||||||
|
with pytest.raises(exception):
|
||||||
|
target.reconcile_with(other)
|
||||||
|
else:
|
||||||
|
expected = target if result else other
|
||||||
|
actual = target.reconcile_with(other)
|
||||||
|
assert actual == expected
|
||||||
|
|
||||||
|
|
||||||
class TestEsphomeCore:
|
class TestEsphomeCore:
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
Loading…
x
Reference in New Issue
Block a user