mirror of
https://github.com/esphome/esphome.git
synced 2026-02-28 01:44:20 -07:00
Merge remote-tracking branch 'upstream/dev' into 20260218-zigbee-proxy
This commit is contained in:
5
tests/components/esp32_ble/test.esp32-c2-idf.yaml
Normal file
5
tests/components/esp32_ble/test.esp32-c2-idf.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
<<: !include common.yaml
|
||||
|
||||
esp32_ble:
|
||||
io_capability: keyboard_only
|
||||
disable_bt_logs: false
|
||||
@@ -33,6 +33,10 @@ esp32_ble_server:
|
||||
- uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc42d
|
||||
advertise: false
|
||||
characteristics:
|
||||
- id: test_lambda_characteristic
|
||||
uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc12c
|
||||
read: true
|
||||
value: !lambda return { 1, 2 };
|
||||
- id: test_change_characteristic
|
||||
uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc11c
|
||||
read: true
|
||||
|
||||
@@ -4,15 +4,16 @@ interval:
|
||||
- interval: 60s
|
||||
then:
|
||||
- lambda: |-
|
||||
// Test build_json
|
||||
std::string json_str = esphome::json::build_json([](JsonObject root) {
|
||||
// Test build_json - returns SerializationBuffer, use auto to avoid heap allocation
|
||||
auto json_buf = esphome::json::build_json([](JsonObject root) {
|
||||
root["sensor"] = "temperature";
|
||||
root["value"] = 23.5;
|
||||
root["unit"] = "°C";
|
||||
});
|
||||
ESP_LOGD("test", "Built JSON: %s", json_str.c_str());
|
||||
ESP_LOGD("test", "Built JSON: %s", json_buf.c_str());
|
||||
|
||||
// Test parse_json
|
||||
// Test parse_json - implicit conversion to std::string for backward compatibility
|
||||
std::string json_str = json_buf;
|
||||
bool parse_ok = esphome::json::parse_json(json_str, [](JsonObject root) {
|
||||
if (root["sensor"].is<const char*>() && root["value"].is<float>()) {
|
||||
const char* sensor = root["sensor"];
|
||||
@@ -26,10 +27,10 @@ interval:
|
||||
});
|
||||
ESP_LOGD("test", "Parse result (JSON syntax only): %s", parse_ok ? "success" : "failed");
|
||||
|
||||
// Test JsonBuilder class
|
||||
// Test JsonBuilder class - returns SerializationBuffer
|
||||
esphome::json::JsonBuilder builder;
|
||||
JsonObject obj = builder.root();
|
||||
obj["test"] = "direct_builder";
|
||||
obj["count"] = 42;
|
||||
std::string result = builder.serialize();
|
||||
auto result = builder.serialize();
|
||||
ESP_LOGD("test", "JsonBuilder result: %s", result.c_str());
|
||||
|
||||
@@ -23,8 +23,27 @@ media_player:
|
||||
- media_player.stop:
|
||||
- media_player.stop:
|
||||
announcement: true
|
||||
on_announcement:
|
||||
- media_player.play:
|
||||
on_turn_on:
|
||||
- media_player.play:
|
||||
on_turn_off:
|
||||
- media_player.stop:
|
||||
on_pause:
|
||||
- media_player.toggle:
|
||||
- media_player.turn_on:
|
||||
- media_player.turn_off:
|
||||
- media_player.next:
|
||||
- media_player.previous:
|
||||
- media_player.mute:
|
||||
- media_player.unmute:
|
||||
- media_player.repeat_off:
|
||||
- media_player.repeat_one:
|
||||
- media_player.repeat_all:
|
||||
- media_player.shuffle:
|
||||
- media_player.unshuffle:
|
||||
- media_player.group_join:
|
||||
- media_player.clear_playlist:
|
||||
- wait_until:
|
||||
media_player.is_idle:
|
||||
- wait_until:
|
||||
@@ -33,6 +52,12 @@ media_player:
|
||||
media_player.is_announcing:
|
||||
- wait_until:
|
||||
media_player.is_paused:
|
||||
- wait_until:
|
||||
media_player.is_on:
|
||||
- wait_until:
|
||||
media_player.is_off:
|
||||
- wait_until:
|
||||
media_player.is_muted:
|
||||
- media_player.volume_up:
|
||||
- media_player.volume_down:
|
||||
- media_player.volume_set: 50%
|
||||
|
||||
10
tests/components/pulse_counter/test-no-pcnt.esp32-idf.yaml
Normal file
10
tests/components/pulse_counter/test-no-pcnt.esp32-idf.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
sensor:
|
||||
- platform: pulse_counter
|
||||
name: Pulse Counter
|
||||
pin: 4
|
||||
use_pcnt: false
|
||||
count_mode:
|
||||
rising_edge: INCREMENT
|
||||
falling_edge: DECREMENT
|
||||
internal_filter: 13us
|
||||
update_interval: 15s
|
||||
11
tests/components/socket/common.yaml
Normal file
11
tests/components/socket/common.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
substitutions:
|
||||
network_enable_ipv6: "false"
|
||||
|
||||
socket:
|
||||
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
|
||||
network:
|
||||
enable_ipv6: ${network_enable_ipv6}
|
||||
4
tests/components/socket/test-ipv6.esp32-idf.yaml
Normal file
4
tests/components/socket/test-ipv6.esp32-idf.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
substitutions:
|
||||
network_enable_ipv6: "true"
|
||||
|
||||
<<: !include common.yaml
|
||||
4
tests/components/socket/test-ipv6.host.yaml
Normal file
4
tests/components/socket/test-ipv6.host.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
socket:
|
||||
|
||||
network:
|
||||
enable_ipv6: true
|
||||
1
tests/components/socket/test.esp32-idf.yaml
Normal file
1
tests/components/socket/test.esp32-idf.yaml
Normal file
@@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
||||
3
tests/components/socket/test.host.yaml
Normal file
3
tests/components/socket/test.host.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
socket:
|
||||
|
||||
network:
|
||||
@@ -1,10 +0,0 @@
|
||||
substitutions:
|
||||
i2s_bclk_pin: GPIO27
|
||||
i2s_lrclk_pin: GPIO26
|
||||
i2s_mclk_pin: GPIO25
|
||||
i2s_dout_pin: GPIO23
|
||||
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml
|
||||
|
||||
<<: !include common-audio_dac.yaml
|
||||
@@ -1,5 +1,11 @@
|
||||
<<: !include common.yaml
|
||||
|
||||
wifi:
|
||||
ap:
|
||||
|
||||
psram:
|
||||
mode: quad
|
||||
|
||||
media_player:
|
||||
- platform: speaker
|
||||
id: speaker_media_player_id
|
||||
@@ -10,3 +16,4 @@ media_player:
|
||||
volume_max: 0.95
|
||||
volume_min: 0.0
|
||||
task_stack_in_psram: true
|
||||
codec_support_enabled: all
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
substitutions:
|
||||
scl_pin: GPIO2
|
||||
sda_pin: GPIO3
|
||||
i2s_bclk_pin: GPIO4
|
||||
i2s_lrclk_pin: GPIO5
|
||||
i2s_mclk_pin: GPIO6
|
||||
i2s_dout_pin: GPIO7
|
||||
|
||||
<<: !include common-media_player.yaml
|
||||
@@ -1,10 +0,0 @@
|
||||
substitutions:
|
||||
i2s_bclk_pin: GPIO27
|
||||
i2s_lrclk_pin: GPIO26
|
||||
i2s_mclk_pin: GPIO25
|
||||
i2s_dout_pin: GPIO4
|
||||
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -30,6 +30,7 @@ esp32_camera:
|
||||
resolution: 640x480
|
||||
jpeg_quality: 10
|
||||
frame_buffer_location: PSRAM
|
||||
pixel_format: JPEG
|
||||
on_image:
|
||||
then:
|
||||
- lambda: |-
|
||||
|
||||
@@ -656,7 +656,7 @@ def test_clone_or_update_recover_broken_flag_prevents_infinite_loop(
|
||||
# Should raise on the second attempt when _recover_broken=False
|
||||
# This hits the "if not _recover_broken: raise" path
|
||||
with (
|
||||
unittest.mock.patch("esphome.git.shutil.rmtree", side_effect=mock_rmtree),
|
||||
unittest.mock.patch("esphome.git.rmtree", side_effect=mock_rmtree),
|
||||
pytest.raises(GitCommandError, match="fatal: unable to write new index file"),
|
||||
):
|
||||
git.clone_or_update(
|
||||
@@ -671,3 +671,114 @@ def test_clone_or_update_recover_broken_flag_prevents_infinite_loop(
|
||||
stash_calls = [c for c in call_list if "stash" in c[0][0]]
|
||||
# Should have exactly two stash calls
|
||||
assert len(stash_calls) == 2
|
||||
|
||||
|
||||
def test_clone_or_update_cleans_up_on_failed_ref_fetch(
|
||||
tmp_path: Path, mock_run_git_command: Mock
|
||||
) -> None:
|
||||
"""Test that a failed ref fetch removes the incomplete clone directory.
|
||||
|
||||
When cloning with a specific ref, if `git clone` succeeds but the
|
||||
subsequent `git fetch <ref>` fails, the clone directory should be
|
||||
removed so the next attempt starts fresh instead of finding a stale
|
||||
clone on the default branch.
|
||||
"""
|
||||
CORE.config_path = tmp_path / "test.yaml"
|
||||
|
||||
url = "https://github.com/test/repo"
|
||||
ref = "pull/123/head"
|
||||
domain = "test"
|
||||
repo_dir = _compute_repo_dir(url, ref, domain)
|
||||
|
||||
def git_command_side_effect(
|
||||
cmd: list[str], cwd: str | None = None, **kwargs: Any
|
||||
) -> str:
|
||||
cmd_type = _get_git_command_type(cmd)
|
||||
if cmd_type == "clone":
|
||||
# Simulate successful clone by creating the directory
|
||||
repo_dir.mkdir(parents=True, exist_ok=True)
|
||||
(repo_dir / ".git").mkdir(exist_ok=True)
|
||||
return ""
|
||||
if cmd_type == "fetch":
|
||||
raise GitCommandError("fatal: couldn't find remote ref pull/123/head")
|
||||
return ""
|
||||
|
||||
mock_run_git_command.side_effect = git_command_side_effect
|
||||
|
||||
refresh = TimePeriodSeconds(days=1)
|
||||
|
||||
with pytest.raises(GitCommandError, match="couldn't find remote ref"):
|
||||
git.clone_or_update(
|
||||
url=url,
|
||||
ref=ref,
|
||||
refresh=refresh,
|
||||
domain=domain,
|
||||
)
|
||||
|
||||
# The incomplete clone directory should have been removed
|
||||
assert not repo_dir.exists()
|
||||
|
||||
# Verify clone was attempted then fetch failed
|
||||
call_list = mock_run_git_command.call_args_list
|
||||
clone_calls = [c for c in call_list if "clone" in c[0][0]]
|
||||
assert len(clone_calls) == 1
|
||||
fetch_calls = [c for c in call_list if "fetch" in c[0][0]]
|
||||
assert len(fetch_calls) == 1
|
||||
|
||||
|
||||
def test_clone_or_update_stale_clone_is_retried_after_cleanup(
|
||||
tmp_path: Path, mock_run_git_command: Mock
|
||||
) -> None:
|
||||
"""Test that after cleanup, a subsequent call does a fresh clone.
|
||||
|
||||
This is the full scenario: first call fails at fetch (directory cleaned up),
|
||||
second call sees no directory and clones fresh.
|
||||
"""
|
||||
CORE.config_path = tmp_path / "test.yaml"
|
||||
|
||||
url = "https://github.com/test/repo"
|
||||
ref = "pull/123/head"
|
||||
domain = "test"
|
||||
repo_dir = _compute_repo_dir(url, ref, domain)
|
||||
|
||||
call_count = {"clone": 0, "fetch": 0}
|
||||
|
||||
def git_command_side_effect(
|
||||
cmd: list[str], cwd: str | None = None, **kwargs: Any
|
||||
) -> str:
|
||||
cmd_type = _get_git_command_type(cmd)
|
||||
if cmd_type == "clone":
|
||||
call_count["clone"] += 1
|
||||
repo_dir.mkdir(parents=True, exist_ok=True)
|
||||
(repo_dir / ".git").mkdir(exist_ok=True)
|
||||
return ""
|
||||
if cmd_type == "fetch":
|
||||
call_count["fetch"] += 1
|
||||
if call_count["fetch"] == 1:
|
||||
# First fetch fails
|
||||
raise GitCommandError("fatal: couldn't find remote ref pull/123/head")
|
||||
# Second fetch succeeds
|
||||
return ""
|
||||
if cmd_type == "reset":
|
||||
return ""
|
||||
return ""
|
||||
|
||||
mock_run_git_command.side_effect = git_command_side_effect
|
||||
|
||||
refresh = TimePeriodSeconds(days=1)
|
||||
|
||||
# First call: clone succeeds, fetch fails, directory cleaned up
|
||||
with pytest.raises(GitCommandError, match="couldn't find remote ref"):
|
||||
git.clone_or_update(url=url, ref=ref, refresh=refresh, domain=domain)
|
||||
|
||||
assert not repo_dir.exists()
|
||||
|
||||
# Second call: fresh clone + fetch succeeds
|
||||
result_dir, _ = git.clone_or_update(
|
||||
url=url, ref=ref, refresh=refresh, domain=domain
|
||||
)
|
||||
|
||||
assert result_dir == repo_dir
|
||||
assert repo_dir.exists()
|
||||
assert call_count["clone"] == 2
|
||||
assert call_count["fetch"] == 2
|
||||
|
||||
Reference in New Issue
Block a user