mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 14:43:51 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/dev' into integration
This commit is contained in:
		
							
								
								
									
										7
									
								
								tests/components/wts01/common.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								tests/components/wts01/common.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| uart: | ||||
|   rx_pin: ${rx_pin} | ||||
|   baud_rate: 9600 | ||||
|  | ||||
| sensor: | ||||
|   - platform: wts01 | ||||
|     id: wts01_sensor | ||||
							
								
								
									
										5
									
								
								tests/components/wts01/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/wts01/test.esp32-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   tx_pin: GPIO16 | ||||
|   rx_pin: GPIO17 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										5
									
								
								tests/components/wts01/test.esp32-c3-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/wts01/test.esp32-c3-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   tx_pin: GPIO6 | ||||
|   rx_pin: GPIO7 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										5
									
								
								tests/components/wts01/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/wts01/test.esp32-c3-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   tx_pin: GPIO6 | ||||
|   rx_pin: GPIO7 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										5
									
								
								tests/components/wts01/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/wts01/test.esp32-idf.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   tx_pin: GPIO16 | ||||
|   rx_pin: GPIO17 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										5
									
								
								tests/components/wts01/test.esp8266-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/wts01/test.esp8266-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   tx_pin: GPIO1 | ||||
|   rx_pin: GPIO3 | ||||
|  | ||||
| <<: !include common.yaml | ||||
							
								
								
									
										5
									
								
								tests/components/wts01/test.rp2040-ard.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/components/wts01/test.rp2040-ard.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| substitutions: | ||||
|   tx_pin: GPIO0 | ||||
|   rx_pin: GPIO1 | ||||
|  | ||||
| <<: !include common.yaml | ||||
| @@ -4,7 +4,7 @@ from __future__ import annotations | ||||
|  | ||||
| import asyncio | ||||
|  | ||||
| from aioesphomeapi import APIConnectionError | ||||
| from aioesphomeapi import APIConnectionError, InvalidAuthAPIError | ||||
| import pytest | ||||
|  | ||||
| from .types import APIClientConnectedFactory, RunCompiledFunction | ||||
| @@ -48,6 +48,22 @@ async def test_host_mode_api_password( | ||||
|             assert len(states) > 0 | ||||
|  | ||||
|         # Test with wrong password - should fail | ||||
|         with pytest.raises(APIConnectionError, match="Invalid password"): | ||||
|             async with api_client_connected(password="wrong_password"): | ||||
|                 pass  # Should not reach here | ||||
|         # Try connecting with wrong password | ||||
|         try: | ||||
|             async with api_client_connected( | ||||
|                 password="wrong_password", timeout=5 | ||||
|             ) as client: | ||||
|                 # If we get here without exception, try to use the connection | ||||
|                 # which should fail if auth failed | ||||
|                 await client.device_info_and_list_entities() | ||||
|                 # If we successfully got device info and entities, auth didn't fail properly | ||||
|                 pytest.fail("Connection succeeded with wrong password") | ||||
|         except (InvalidAuthAPIError, APIConnectionError) as e: | ||||
|             # Expected - auth should fail | ||||
|             # Accept either InvalidAuthAPIError or generic APIConnectionError | ||||
|             # since the client might not always distinguish | ||||
|             assert ( | ||||
|                 "password" in str(e).lower() | ||||
|                 or "auth" in str(e).lower() | ||||
|                 or "invalid" in str(e).lower() | ||||
|             ) | ||||
|   | ||||
| @@ -17,7 +17,7 @@ from esphome import platformio_api | ||||
| from esphome.__main__ import ( | ||||
|     Purpose, | ||||
|     choose_upload_log_host, | ||||
|     command_clean_platform, | ||||
|     command_clean_all, | ||||
|     command_rename, | ||||
|     command_update_all, | ||||
|     command_wizard, | ||||
| @@ -1857,33 +1857,31 @@ esp32: | ||||
|     assert "can only concatenate str" not in clean_output | ||||
|  | ||||
|  | ||||
| def test_command_clean_platform_success( | ||||
| def test_command_clean_all_success( | ||||
|     caplog: pytest.LogCaptureFixture, | ||||
| ) -> None: | ||||
|     """Test command_clean_platform when writer.clean_platform() succeeds.""" | ||||
|     args = MockArgs() | ||||
|     config = {} | ||||
|     """Test command_clean_all when writer.clean_all() succeeds.""" | ||||
|     args = MockArgs(configuration=["/path/to/config1", "/path/to/config2"]) | ||||
|  | ||||
|     # Set logger level to capture INFO messages | ||||
|     with ( | ||||
|         caplog.at_level(logging.INFO), | ||||
|         patch("esphome.writer.clean_platform") as mock_clean_platform, | ||||
|         patch("esphome.writer.clean_all") as mock_clean_all, | ||||
|     ): | ||||
|         result = command_clean_platform(args, config) | ||||
|         result = command_clean_all(args) | ||||
|  | ||||
|         assert result == 0 | ||||
|         mock_clean_platform.assert_called_once() | ||||
|         mock_clean_all.assert_called_once_with(["/path/to/config1", "/path/to/config2"]) | ||||
|  | ||||
|         # Check that success message was logged | ||||
|         assert "Done!" in caplog.text | ||||
|  | ||||
|  | ||||
| def test_command_clean_platform_oserror( | ||||
| def test_command_clean_all_oserror( | ||||
|     caplog: pytest.LogCaptureFixture, | ||||
| ) -> None: | ||||
|     """Test command_clean_platform when writer.clean_platform() raises OSError.""" | ||||
|     args = MockArgs() | ||||
|     config = {} | ||||
|     """Test command_clean_all when writer.clean_all() raises OSError.""" | ||||
|     args = MockArgs(configuration=["/path/to/config1"]) | ||||
|  | ||||
|     # Create a mock OSError with a specific message | ||||
|     mock_error = OSError("Permission denied: cannot delete directory") | ||||
| @@ -1891,30 +1889,27 @@ def test_command_clean_platform_oserror( | ||||
|     # Set logger level to capture ERROR and INFO messages | ||||
|     with ( | ||||
|         caplog.at_level(logging.INFO), | ||||
|         patch( | ||||
|             "esphome.writer.clean_platform", side_effect=mock_error | ||||
|         ) as mock_clean_platform, | ||||
|         patch("esphome.writer.clean_all", side_effect=mock_error) as mock_clean_all, | ||||
|     ): | ||||
|         result = command_clean_platform(args, config) | ||||
|         result = command_clean_all(args) | ||||
|  | ||||
|         assert result == 1 | ||||
|         mock_clean_platform.assert_called_once() | ||||
|         mock_clean_all.assert_called_once_with(["/path/to/config1"]) | ||||
|  | ||||
|         # Check that error message was logged | ||||
|         assert ( | ||||
|             "Error deleting platform files: Permission denied: cannot delete directory" | ||||
|             "Error cleaning all files: Permission denied: cannot delete directory" | ||||
|             in caplog.text | ||||
|         ) | ||||
|         # Should not have success message | ||||
|         assert "Done!" not in caplog.text | ||||
|  | ||||
|  | ||||
| def test_command_clean_platform_oserror_no_message( | ||||
| def test_command_clean_all_oserror_no_message( | ||||
|     caplog: pytest.LogCaptureFixture, | ||||
| ) -> None: | ||||
|     """Test command_clean_platform when writer.clean_platform() raises OSError without message.""" | ||||
|     args = MockArgs() | ||||
|     config = {} | ||||
|     """Test command_clean_all when writer.clean_all() raises OSError without message.""" | ||||
|     args = MockArgs(configuration=["/path/to/config1"]) | ||||
|  | ||||
|     # Create a mock OSError without a message | ||||
|     mock_error = OSError() | ||||
| @@ -1922,34 +1917,33 @@ def test_command_clean_platform_oserror_no_message( | ||||
|     # Set logger level to capture ERROR and INFO messages | ||||
|     with ( | ||||
|         caplog.at_level(logging.INFO), | ||||
|         patch( | ||||
|             "esphome.writer.clean_platform", side_effect=mock_error | ||||
|         ) as mock_clean_platform, | ||||
|         patch("esphome.writer.clean_all", side_effect=mock_error) as mock_clean_all, | ||||
|     ): | ||||
|         result = command_clean_platform(args, config) | ||||
|         result = command_clean_all(args) | ||||
|  | ||||
|         assert result == 1 | ||||
|         mock_clean_platform.assert_called_once() | ||||
|         mock_clean_all.assert_called_once_with(["/path/to/config1"]) | ||||
|  | ||||
|         # Check that error message was logged (should show empty string for OSError without message) | ||||
|         assert "Error deleting platform files:" in caplog.text | ||||
|         assert "Error cleaning all files:" in caplog.text | ||||
|         # Should not have success message | ||||
|         assert "Done!" not in caplog.text | ||||
|  | ||||
|  | ||||
| def test_command_clean_platform_args_and_config_ignored() -> None: | ||||
|     """Test that command_clean_platform ignores args and config parameters.""" | ||||
|     # Test with various args and config to ensure they don't affect the function | ||||
|     args1 = MockArgs(name="test1", file="test.bin") | ||||
|     config1 = {"wifi": {"ssid": "test"}} | ||||
| def test_command_clean_all_args_used() -> None: | ||||
|     """Test that command_clean_all uses args.configuration parameter.""" | ||||
|     # Test with different configuration paths | ||||
|     args1 = MockArgs(configuration=["/path/to/config1"]) | ||||
|     args2 = MockArgs(configuration=["/path/to/config2", "/path/to/config3"]) | ||||
|  | ||||
|     args2 = MockArgs(name="test2", dashboard=True) | ||||
|     config2 = {"api": {}, "ota": {}} | ||||
|  | ||||
|     with patch("esphome.writer.clean_platform") as mock_clean_platform: | ||||
|         result1 = command_clean_platform(args1, config1) | ||||
|         result2 = command_clean_platform(args2, config2) | ||||
|     with patch("esphome.writer.clean_all") as mock_clean_all: | ||||
|         result1 = command_clean_all(args1) | ||||
|         result2 = command_clean_all(args2) | ||||
|  | ||||
|         assert result1 == 0 | ||||
|         assert result2 == 0 | ||||
|         assert mock_clean_platform.call_count == 2 | ||||
|         assert mock_clean_all.call_count == 2 | ||||
|  | ||||
|         # Verify the correct configuration paths were passed | ||||
|         mock_clean_all.assert_any_call(["/path/to/config1"]) | ||||
|         mock_clean_all.assert_any_call(["/path/to/config2", "/path/to/config3"]) | ||||
|   | ||||
| @@ -739,16 +739,24 @@ def test_write_cpp_with_duplicate_markers( | ||||
|  | ||||
|  | ||||
| @patch("esphome.writer.CORE") | ||||
| def test_clean_platform( | ||||
| def test_clean_all( | ||||
|     mock_core: MagicMock, | ||||
|     tmp_path: Path, | ||||
|     caplog: pytest.LogCaptureFixture, | ||||
| ) -> None: | ||||
|     """Test clean_platform removes build and PlatformIO dirs.""" | ||||
|     # Create build directory | ||||
|     build_dir = tmp_path / "build" | ||||
|     build_dir.mkdir() | ||||
|     (build_dir / "dummy.txt").write_text("x") | ||||
|     """Test clean_all removes build and PlatformIO dirs.""" | ||||
|     # Create build directories for multiple configurations | ||||
|     config1_dir = tmp_path / "config1" | ||||
|     config2_dir = tmp_path / "config2" | ||||
|     config1_dir.mkdir() | ||||
|     config2_dir.mkdir() | ||||
|  | ||||
|     build_dir1 = config1_dir / ".esphome" | ||||
|     build_dir2 = config2_dir / ".esphome" | ||||
|     build_dir1.mkdir() | ||||
|     build_dir2.mkdir() | ||||
|     (build_dir1 / "dummy.txt").write_text("x") | ||||
|     (build_dir2 / "dummy.txt").write_text("x") | ||||
|  | ||||
|     # Create PlatformIO directories | ||||
|     pio_cache = tmp_path / "pio_cache" | ||||
| @@ -759,9 +767,6 @@ def test_clean_platform( | ||||
|         d.mkdir() | ||||
|         (d / "keep").write_text("x") | ||||
|  | ||||
|     # Setup CORE | ||||
|     mock_core.build_path = build_dir | ||||
|  | ||||
|     # Mock ProjectConfig | ||||
|     with patch( | ||||
|         "platformio.project.config.ProjectConfig.get_instance" | ||||
| @@ -781,13 +786,14 @@ def test_clean_platform( | ||||
|         mock_config.get.side_effect = cfg_get | ||||
|  | ||||
|         # Call | ||||
|         from esphome.writer import clean_platform | ||||
|         from esphome.writer import clean_all | ||||
|  | ||||
|         with caplog.at_level("INFO"): | ||||
|             clean_platform() | ||||
|             clean_all([str(config1_dir), str(config2_dir)]) | ||||
|  | ||||
|     # Verify deletions | ||||
|     assert not build_dir.exists() | ||||
|     assert not build_dir1.exists() | ||||
|     assert not build_dir2.exists() | ||||
|     assert not pio_cache.exists() | ||||
|     assert not pio_packages.exists() | ||||
|     assert not pio_platforms.exists() | ||||
| @@ -795,7 +801,8 @@ def test_clean_platform( | ||||
|  | ||||
|     # Verify logging mentions each | ||||
|     assert "Deleting" in caplog.text | ||||
|     assert str(build_dir) in caplog.text | ||||
|     assert str(build_dir1) in caplog.text | ||||
|     assert str(build_dir2) in caplog.text | ||||
|     assert "PlatformIO cache" in caplog.text | ||||
|     assert "PlatformIO packages" in caplog.text | ||||
|     assert "PlatformIO platforms" in caplog.text | ||||
| @@ -803,28 +810,29 @@ def test_clean_platform( | ||||
|  | ||||
|  | ||||
| @patch("esphome.writer.CORE") | ||||
| def test_clean_platform_platformio_not_available( | ||||
| def test_clean_all_platformio_not_available( | ||||
|     mock_core: MagicMock, | ||||
|     tmp_path: Path, | ||||
|     caplog: pytest.LogCaptureFixture, | ||||
| ) -> None: | ||||
|     """Test clean_platform when PlatformIO is not available.""" | ||||
|     # Build dir | ||||
|     build_dir = tmp_path / "build" | ||||
|     """Test clean_all when PlatformIO is not available.""" | ||||
|     # Build dirs | ||||
|     config_dir = tmp_path / "config" | ||||
|     config_dir.mkdir() | ||||
|     build_dir = config_dir / ".esphome" | ||||
|     build_dir.mkdir() | ||||
|     mock_core.build_path = build_dir | ||||
|  | ||||
|     # PlatformIO dirs that should remain untouched | ||||
|     pio_cache = tmp_path / "pio_cache" | ||||
|     pio_cache.mkdir() | ||||
|  | ||||
|     from esphome.writer import clean_platform | ||||
|     from esphome.writer import clean_all | ||||
|  | ||||
|     with ( | ||||
|         patch.dict("sys.modules", {"platformio.project.config": None}), | ||||
|         caplog.at_level("INFO"), | ||||
|     ): | ||||
|         clean_platform() | ||||
|         clean_all([str(config_dir)]) | ||||
|  | ||||
|     # Build dir removed, PlatformIO dirs remain | ||||
|     assert not build_dir.exists() | ||||
| @@ -835,14 +843,15 @@ def test_clean_platform_platformio_not_available( | ||||
|  | ||||
|  | ||||
| @patch("esphome.writer.CORE") | ||||
| def test_clean_platform_partial_exists( | ||||
| def test_clean_all_partial_exists( | ||||
|     mock_core: MagicMock, | ||||
|     tmp_path: Path, | ||||
| ) -> None: | ||||
|     """Test clean_platform when only build dir exists.""" | ||||
|     build_dir = tmp_path / "build" | ||||
|     """Test clean_all when only some build dirs exist.""" | ||||
|     config_dir = tmp_path / "config" | ||||
|     config_dir.mkdir() | ||||
|     build_dir = config_dir / ".esphome" | ||||
|     build_dir.mkdir() | ||||
|     mock_core.build_path = build_dir | ||||
|  | ||||
|     with patch( | ||||
|         "platformio.project.config.ProjectConfig.get_instance" | ||||
| @@ -854,8 +863,8 @@ def test_clean_platform_partial_exists( | ||||
|             tmp_path / "does_not_exist" | ||||
|         ) | ||||
|  | ||||
|         from esphome.writer import clean_platform | ||||
|         from esphome.writer import clean_all | ||||
|  | ||||
|         clean_platform() | ||||
|         clean_all([str(config_dir)]) | ||||
|  | ||||
|     assert not build_dir.exists() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user