mirror of
https://github.com/esphome/esphome.git
synced 2026-10-01 08:50:22 +00:00
Merge branch 'esp8266-native-ninja-emission' into esp8266-arduino-toolchain
This commit is contained in:
@@ -13,17 +13,18 @@ namespace api {
|
||||
class APIConnection;
|
||||
} // namespace api
|
||||
|
||||
namespace uart {
|
||||
enum class UARTFlushResult : uint8_t {
|
||||
UART_FLUSH_RESULT_SUCCESS,
|
||||
UART_FLUSH_RESULT_ASSUMED_SUCCESS,
|
||||
UART_FLUSH_RESULT_TIMEOUT,
|
||||
UART_FLUSH_RESULT_FAILED,
|
||||
};
|
||||
} // namespace uart
|
||||
|
||||
namespace serial_proxy {
|
||||
|
||||
enum class SerialProxyResult : uint8_t {
|
||||
SERIAL_PROXY_RESULT_OK,
|
||||
SERIAL_PROXY_RESULT_ASSUMED_SUCCESS,
|
||||
SERIAL_PROXY_RESULT_PORT_IN_USE,
|
||||
SERIAL_PROXY_RESULT_INVALID_ARGUMENT,
|
||||
SERIAL_PROXY_RESULT_ERROR,
|
||||
SERIAL_PROXY_RESULT_TIMEOUT,
|
||||
SERIAL_PROXY_RESULT_NOT_SUPPORTED,
|
||||
};
|
||||
|
||||
class SerialProxy {
|
||||
public:
|
||||
void set_instance_index(uint32_t index) { this->instance_index_ = index; }
|
||||
@@ -31,13 +32,20 @@ class SerialProxy {
|
||||
const char *get_name() const { return ""; }
|
||||
api::enums::SerialProxyPortType get_port_type() const { return {}; }
|
||||
api::APIConnection *get_api_connection() { return nullptr; }
|
||||
void serial_proxy_request(api::APIConnection *conn, api::enums::SerialProxyRequestType type) {}
|
||||
void configure(api::APIConnection *api_connection, uint32_t baudrate, bool flow_control, uint8_t parity,
|
||||
uint32_t stop_bits, uint32_t data_size) {}
|
||||
SerialProxyResult serial_proxy_request(api::APIConnection *conn, api::enums::SerialProxyRequestType type) {
|
||||
return SerialProxyResult::SERIAL_PROXY_RESULT_OK;
|
||||
}
|
||||
SerialProxyResult configure(api::APIConnection *api_connection, uint32_t baudrate, bool flow_control, uint8_t parity,
|
||||
uint8_t stop_bits, uint8_t data_size) {
|
||||
return SerialProxyResult::SERIAL_PROXY_RESULT_OK;
|
||||
}
|
||||
void write_from_client(api::APIConnection *api_connection, const uint8_t *data, size_t len) {}
|
||||
void set_modem_pins(api::APIConnection *api_connection, uint32_t line_states) {}
|
||||
SerialProxyResult set_modem_pins(api::APIConnection *api_connection, uint32_t line_states) {
|
||||
return SerialProxyResult::SERIAL_PROXY_RESULT_OK;
|
||||
}
|
||||
uint32_t get_modem_pins() const { return 0; }
|
||||
uart::UARTFlushResult flush_port() { return uart::UARTFlushResult::UART_FLUSH_RESULT_SUCCESS; }
|
||||
uint32_t get_configured_modem_pins() const { return 0; }
|
||||
SerialProxyResult flush_port(api::APIConnection *api_connection) { return SerialProxyResult::SERIAL_PROXY_RESULT_OK; }
|
||||
|
||||
protected:
|
||||
uint32_t instance_index_{0};
|
||||
|
||||
@@ -15,7 +15,9 @@ namespace zwave_proxy {
|
||||
class ZWaveProxy {
|
||||
public:
|
||||
api::APIConnection *get_api_connection() { return nullptr; }
|
||||
void zwave_proxy_request(api::APIConnection *conn, api::enums::ZWaveProxyRequestType type) {}
|
||||
api::enums::ZWaveProxyStatus zwave_proxy_request(api::APIConnection *conn, api::enums::ZWaveProxyRequestType type) {
|
||||
return api::enums::ZWAVE_PROXY_STATUS_OK;
|
||||
}
|
||||
void send_frame(api::APIConnection *api_connection, const uint8_t *data, size_t length) {}
|
||||
void api_connection_authenticated(api::APIConnection *conn) {}
|
||||
uint32_t get_feature_flags() const { return 0; }
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "esphome/components/runtime_image/runtime_image.h"
|
||||
|
||||
namespace esphome::runtime_image::testing {
|
||||
|
||||
TEST(RuntimeImageMime, FormatForKnownMimeTypes) {
|
||||
EXPECT_EQ(get_format_for_mime_type("image/bmp"), BMP);
|
||||
EXPECT_EQ(get_format_for_mime_type("image/x-ms-bmp"), BMP);
|
||||
EXPECT_EQ(get_format_for_mime_type("image/x-bmp"), BMP);
|
||||
EXPECT_EQ(get_format_for_mime_type("image/png"), PNG);
|
||||
EXPECT_EQ(get_format_for_mime_type("image/x-png"), PNG);
|
||||
#ifdef USE_RUNTIME_IMAGE_JPEG
|
||||
EXPECT_EQ(get_format_for_mime_type("image/jpeg"), JPEG);
|
||||
EXPECT_EQ(get_format_for_mime_type("image/jpg"), JPEG);
|
||||
#endif // USE_RUNTIME_IMAGE_JPEG
|
||||
}
|
||||
|
||||
TEST(RuntimeImageMime, FormatMatchingIsCaseInsensitive) {
|
||||
EXPECT_EQ(get_format_for_mime_type("Image/PNG"), PNG);
|
||||
EXPECT_EQ(get_format_for_mime_type("IMAGE/BMP"), BMP);
|
||||
}
|
||||
|
||||
TEST(RuntimeImageMime, FormatMatchesContentTypeWithParameters) {
|
||||
// Content-Type headers may carry parameters after the media type
|
||||
EXPECT_EQ(get_format_for_mime_type("image/png; charset=binary"), PNG);
|
||||
EXPECT_EQ(get_format_for_mime_type("image/bmp;name=\"a.bmp\""), BMP);
|
||||
}
|
||||
|
||||
TEST(RuntimeImageMime, UnknownMimeTypeHasNoFormat) {
|
||||
EXPECT_EQ(get_format_for_mime_type("text/html"), std::nullopt);
|
||||
EXPECT_EQ(get_format_for_mime_type("application/octet-stream"), std::nullopt);
|
||||
EXPECT_EQ(get_format_for_mime_type("image/*"), std::nullopt);
|
||||
EXPECT_EQ(get_format_for_mime_type(""), std::nullopt);
|
||||
EXPECT_EQ(get_format_for_mime_type(nullptr), std::nullopt);
|
||||
}
|
||||
|
||||
TEST(RuntimeImageMime, MimeTypeForFormatRoundTrip) {
|
||||
EXPECT_STREQ(get_mime_type_for_format(BMP), "image/bmp");
|
||||
EXPECT_STREQ(get_mime_type_for_format(PNG), "image/png");
|
||||
#ifdef USE_RUNTIME_IMAGE_JPEG
|
||||
EXPECT_STREQ(get_mime_type_for_format(JPEG), "image/jpeg");
|
||||
#endif // USE_RUNTIME_IMAGE_JPEG
|
||||
// AUTO has no single MIME type and falls back to the wildcard
|
||||
EXPECT_STREQ(get_mime_type_for_format(AUTO), "image/*");
|
||||
|
||||
// Every decodable format must resolve back to itself through its MIME type
|
||||
for (ImageFormat format : {
|
||||
BMP,
|
||||
PNG,
|
||||
#ifdef USE_RUNTIME_IMAGE_JPEG
|
||||
JPEG,
|
||||
#endif // USE_RUNTIME_IMAGE_JPEG
|
||||
}) {
|
||||
EXPECT_EQ(get_format_for_mime_type(get_mime_type_for_format(format)), format) << format;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::runtime_image::testing
|
||||
@@ -263,6 +263,17 @@ def test_device_capabilities_response_has_id_150() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_z_wave_proxy_request_response_has_id_151() -> None:
|
||||
body = _extract_proto_message(PROTO_TEXT, "ZWaveProxyRequestResponse")
|
||||
match = re.search(r"option \(id\) = (\d+);", body)
|
||||
assert match is not None, "ZWaveProxyRequestResponse is missing `option (id)`"
|
||||
assert int(match.group(1)) == 151, (
|
||||
f"ZWaveProxyRequestResponse has id {match.group(1)}, expected 151. "
|
||||
"Message ids are part of the wire protocol and must not change once "
|
||||
"assigned."
|
||||
)
|
||||
|
||||
|
||||
def test_superseded_fields_are_not_marked_deprecated_in_proto() -> None:
|
||||
"""The six superseded fields must not carry `[deprecated = true]` in
|
||||
api.proto, or the generator drops them and old clients stop receiving
|
||||
|
||||
@@ -299,10 +299,13 @@ def test_run_compile_restamps_cmakecache_after_discovery(setup_core: Path) -> No
|
||||
_setup_build(setup_core)
|
||||
config = {CONF_ESPHOME: {}}
|
||||
cmakecache = CORE.relative_build_path("build/CMakeCache.txt")
|
||||
build_ninja = CORE.relative_build_path("build/build.ninja")
|
||||
cmakecache.parent.mkdir(parents=True, exist_ok=True)
|
||||
cmakecache.write_text("")
|
||||
build_ninja.write_text("")
|
||||
old = cmakecache.stat().st_mtime - 100
|
||||
os.utime(cmakecache, (old, old))
|
||||
os.utime(build_ninja, (old, old))
|
||||
|
||||
with (
|
||||
patch.object(toolchain, "need_reconfigure", return_value=True),
|
||||
@@ -314,6 +317,8 @@ def test_run_compile_restamps_cmakecache_after_discovery(setup_core: Path) -> No
|
||||
assert toolchain.run_compile(config, verbose=False) == 0
|
||||
|
||||
assert cmakecache.stat().st_mtime > old
|
||||
# build.ninja must not be older than the cache or ninja re-runs cmake
|
||||
assert build_ninja.stat().st_mtime >= cmakecache.stat().st_mtime
|
||||
|
||||
|
||||
def test_run_compile_discovery_without_cmakecache(setup_core: Path) -> None:
|
||||
@@ -334,6 +339,50 @@ def test_run_compile_discovery_without_cmakecache(setup_core: Path) -> None:
|
||||
assert not CORE.relative_build_path("build/CMakeCache.txt").exists()
|
||||
|
||||
|
||||
def test_run_compile_reconfigures_after_full_write_outside_testing_mode(
|
||||
setup_core: Path,
|
||||
) -> None:
|
||||
"""The full CMakeLists write is followed by a reconfigure (#18682); a
|
||||
failure there stops the build and leaves the cache unstamped."""
|
||||
_setup_build(setup_core)
|
||||
config = {CONF_ESPHOME: {}}
|
||||
cmakecache = CORE.relative_build_path("build/CMakeCache.txt")
|
||||
cmakecache.parent.mkdir(parents=True, exist_ok=True)
|
||||
cmakecache.write_text("")
|
||||
old = cmakecache.stat().st_mtime - 100
|
||||
os.utime(cmakecache, (old, old))
|
||||
calls: list[tuple] = []
|
||||
reconfigures = 0
|
||||
|
||||
def record_write(minimal: bool = False) -> None:
|
||||
calls.append(("write_project", minimal))
|
||||
|
||||
def record_reconfigure() -> int:
|
||||
nonlocal reconfigures
|
||||
reconfigures += 1
|
||||
calls.append(("run_reconfigure",))
|
||||
return 1 if reconfigures == 2 else 0
|
||||
|
||||
with (
|
||||
patch.object(toolchain, "need_reconfigure", return_value=True),
|
||||
patch("esphome.build_gen.espidf.write_project", side_effect=record_write),
|
||||
patch.object(toolchain, "run_reconfigure", side_effect=record_reconfigure),
|
||||
patch.object(toolchain, "run_idf_py", return_value=0) as mock_build,
|
||||
patch.object(toolchain, "print_summary"),
|
||||
):
|
||||
assert not CORE.testing_mode
|
||||
assert toolchain.run_compile(config, verbose=False) == 1
|
||||
|
||||
assert calls == [
|
||||
("write_project", True),
|
||||
("run_reconfigure",),
|
||||
("write_project", False),
|
||||
("run_reconfigure",),
|
||||
]
|
||||
mock_build.assert_not_called()
|
||||
assert cmakecache.stat().st_mtime == old
|
||||
|
||||
|
||||
def test_run_compile_passes_compile_process_limit(setup_core: Path) -> None:
|
||||
"""compile_process_limit is forwarded to run_idf_py as the job limit."""
|
||||
_setup_build(setup_core)
|
||||
|
||||
Reference in New Issue
Block a user