diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d47c3971..c5df40835 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Ongoing + +- Implement async file-open in tests + ## v1.7.6 - Maintenance chores (mostly reworking Github CI Actions) backporting from efforts on Python Plugwise [USB: #264](https://github.com/plugwise/python-plugwise-usb/pull/264) after porting our progress using [USB: #263](https://github.com/plugwise/python-plugwise-usb/pull/263) diff --git a/pyproject.toml b/pyproject.toml index ede4e456a..ef4299677 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ maintainers = [ ] requires-python = ">=3.13" dependencies = [ + "aiofiles", "aiohttp", "defusedxml", "munch", diff --git a/tests/test_adam.py b/tests/test_adam.py index 96b015ce5..1ef41c0de 100644 --- a/tests/test_adam.py +++ b/tests/test_adam.py @@ -21,7 +21,7 @@ async def test_connect_adam_plus_anna_new(self): """Test extended Adam (firmware 3.7) with Anna and a switch-group setup.""" self.smile_setup = "adam_plus_anna_new" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -139,7 +139,7 @@ async def test_connect_adam_plus_anna_new(self): # Now change some data and change directory reading xml from # emulating reading newer dataset after an update_interval - testdata_updated = self.load_testdata( + testdata_updated = await self.load_testdata( SMILE_TYPE, f"{self.smile_setup}_UPDATED_DATA" ) self.smile_setup = "updated/adam_plus_anna_new" @@ -167,7 +167,7 @@ async def test_connect_adam_plus_anna_new(self): await self.disconnect(server, client) self.smile_setup = "adam_plus_anna_new" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper(raise_timeout=True) await self.device_test( smile, "2023-12-17 00:00:01", testdata, skip_testing=True @@ -190,7 +190,7 @@ async def test_connect_adam_plus_anna_new_regulation_off(self): """Test regultaion_mode off with control_state key missing for Adam.""" self.smile_setup = "adam_plus_anna_new_regulation_off" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -204,7 +204,7 @@ async def test_connect_adam_zone_per_device(self): """Test an extensive setup of Adam with a zone per device.""" self.smile_setup = "adam_zone_per_device" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -285,7 +285,7 @@ async def test_connect_adam_multiple_devices_per_zone(self): """Test an extensive setup of Adam with multiple devices per zone.""" self.smile_setup = "adam_multiple_devices_per_zone" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -333,7 +333,7 @@ async def test_adam_heatpump_cooling(self): """Test Adam with heatpump in cooling mode and idle.""" self.smile_setup = "adam_heatpump_cooling" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() await self.device_test(smile, "2022-01-02 00:00:01", testdata) @@ -359,7 +359,7 @@ async def test_connect_adam_onoff_cooling_fake_firmware(self): """Test an Adam with a fake OnOff cooling device in cooling mode.""" self.smile_setup = "adam_onoff_cooling_fake_firmware" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -382,7 +382,7 @@ async def test_connect_adam_plus_anna(self): """Test Adam (firmware 3.0) with Anna setup.""" self.smile_setup = "adam_plus_anna" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -432,7 +432,7 @@ async def test_adam_plus_jip(self): """Test Adam with Jip setup.""" self.smile_setup = "adam_jip" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() await self.device_test(smile, "2021-06-20 00:00:01", testdata) diff --git a/tests/test_anna.py b/tests/test_anna.py index bce21b1ab..92efae3d9 100644 --- a/tests/test_anna.py +++ b/tests/test_anna.py @@ -17,7 +17,7 @@ async def test_connect_anna_v4(self): """Test an Anna firmware 4 setup.""" self.smile_setup = "anna_v4" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -55,7 +55,7 @@ async def test_connect_anna_v4(self): # Now change some data and change directory reading xml from # emulating reading newer dataset after an update_interval - testdata_updated = self.load_testdata( + testdata_updated = await self.load_testdata( SMILE_TYPE, f"{self.smile_setup}_UPDATED_DATA" ) @@ -97,7 +97,7 @@ async def test_connect_anna_v4_dhw(self): """Test an Anna firmware 4 setup for domestic hot water.""" self.smile_setup = "anna_v4_dhw" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -127,7 +127,7 @@ async def test_connect_anna_v4_no_tag(self): """Test an Anna firmware 4 setup - missing tag (issue).""" self.smile_setup = "anna_v4_no_tag" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -155,7 +155,7 @@ async def test_connect_anna_without_boiler_fw441(self): """Test an Anna with firmware 4.4, without a boiler.""" self.smile_setup = "anna_without_boiler_fw441" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -183,7 +183,7 @@ async def test_connect_anna_heatpump_heating(self): self.smile_setup = "anna_heatpump_heating" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -218,7 +218,7 @@ async def test_connect_anna_heatpump_heating(self): # Now change some data and change directory reading xml from # emulating reading newer dataset after an update_interval, # set testday to Monday to force an incremental update - testdata_updated = self.load_testdata( + testdata_updated = await self.load_testdata( SMILE_TYPE, f"{self.smile_setup}_UPDATED_DATA" ) @@ -240,7 +240,7 @@ async def test_connect_anna_heatpump_cooling(self): """ self.smile_setup = "anna_heatpump_cooling" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -287,7 +287,7 @@ async def test_connect_anna_heatpump_cooling_fake_firmware(self): """ self.smile_setup = "anna_heatpump_cooling_fake_firmware" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -312,7 +312,7 @@ async def test_connect_anna_elga_no_cooling(self): self.smile_setup = "anna_elga_no_cooling" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -337,7 +337,7 @@ async def test_connect_anna_elga_2(self): """Test a 2nd Anna with Elga setup, cooling off, in idle mode (with missing outdoor temperature - solved).""" self.smile_setup = "anna_elga_2" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -366,7 +366,7 @@ async def test_connect_anna_elga_2_schedule_off(self): """Test Anna with Elga setup, cooling off, in idle mode, modified to schedule off.""" self.smile_setup = "anna_elga_2_schedule_off" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -391,7 +391,7 @@ async def test_connect_anna_elga_2_cooling(self): """ self.smile_setup = "anna_elga_2_cooling" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -421,7 +421,7 @@ async def test_connect_anna_elga_2_cooling(self): assert result # Simulate a change of season: from cooling to heating after an update_interval - testdata_updated = self.load_testdata( + testdata_updated = await self.load_testdata( SMILE_TYPE, f"{self.smile_setup}_UPDATED_DATA" ) @@ -448,7 +448,7 @@ async def test_connect_anna_loria_heating_idle(self): """Test an Anna with a Loria in heating mode - state idle.""" self.smile_setup = "anna_loria_heating_idle" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -516,7 +516,7 @@ async def test_connect_anna_loria_cooling_active(self): """Test an Anna with a Loria in heating mode - state idle.""" self.smile_setup = "anna_loria_cooling_active" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -540,7 +540,7 @@ async def test_connect_anna_loria_driessens(self): """Test an Anna with a Loria in heating mode - state idle.""" self.smile_setup = "anna_loria_driessens" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" diff --git a/tests/test_init.py b/tests/test_init.py index c80468ec6..ed6be9ece 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -16,6 +16,7 @@ import pytest # Testing +import aiofiles import aiohttp from freezegun import freeze_time from packaging import version @@ -50,7 +51,7 @@ class TestPlugwise: # pylint: disable=attribute-defined-outside-init """Tests for Plugwise Smile.""" - def _write_json(self, call, data): + async def _write_json(self, call, data): """Store JSON data to per-setup files for HA component testing.""" no_fixtures = os.getenv("NO_FIXTURES") == "1" if no_fixtures: @@ -65,8 +66,8 @@ def _write_json(self, call, data): if not os.path.exists(os.path.dirname(datafile)): # pragma: no cover os.mkdir(os.path.dirname(datafile)) - with open(datafile, "w", encoding="utf-8") as fixture_file: - fixture_file.write( + async with aiofiles.open(datafile, "w", encoding="utf-8") as fixture_file: + await fixture_file.write( json.dumps( data, indent=2, @@ -77,15 +78,16 @@ def _write_json(self, call, data): + "\n" ) - def load_testdata( + async def load_testdata( self, smile_type: str = "adam", smile_setup: str = "adam_zone_per_device" ): """Load JSON data from setup, return as object.""" path = os.path.join( os.path.dirname(__file__), f"../tests/data/{smile_type}/{smile_setup}.json" ) - with open(path, encoding="utf-8") as testdata_file: - return json.load(testdata_file) + async with aiofiles.open(path, encoding="utf-8") as testdata_file: + content = await testdata_file.read() + return json.loads(content) async def setup_app( self, @@ -187,8 +189,8 @@ async def smile_appliances(self, request): os.path.dirname(__file__), f"../userdata/{self.smile_setup}/core.appliances.xml", ) - with open(userdata, encoding="utf-8") as filedata: - data = filedata.read() + async with aiofiles.open(userdata, encoding="utf-8") as filedata: + data = await filedata.read() return aiohttp.web.Response(text=data) async def smile_domain_objects(self, request): @@ -197,8 +199,8 @@ async def smile_domain_objects(self, request): os.path.dirname(__file__), f"../userdata/{self.smile_setup}/core.domain_objects.xml", ) - with open(userdata, encoding="utf-8") as filedata: - data = filedata.read() + async with aiofiles.open(userdata, encoding="utf-8") as filedata: + data = await filedata.read() return aiohttp.web.Response(text=data) async def smile_locations(self, request): @@ -207,8 +209,8 @@ async def smile_locations(self, request): os.path.dirname(__file__), f"../userdata/{self.smile_setup}/core.locations.xml", ) - with open(userdata, encoding="utf-8") as filedata: - data = filedata.read() + async with aiofiles.open(userdata, encoding="utf-8") as filedata: + data = await filedata.read() return aiohttp.web.Response(text=data) async def smile_modules(self, request): @@ -217,8 +219,8 @@ async def smile_modules(self, request): os.path.dirname(__file__), f"../userdata/{self.smile_setup}/core.modules.xml", ) - with open(userdata, encoding="utf-8") as filedata: - data = filedata.read() + async with aiofiles.open(userdata, encoding="utf-8") as filedata: + data = await filedata.read() return aiohttp.web.Response(text=data) async def smile_status(self, request): @@ -228,8 +230,8 @@ async def smile_status(self, request): os.path.dirname(__file__), f"../userdata/{self.smile_setup}/system_status_xml.xml", ) - with open(userdata, encoding="utf-8") as filedata: - data = filedata.read() + async with aiofiles.open(userdata, encoding="utf-8") as filedata: + data = await filedata.read() return aiohttp.web.Response(text=data) except OSError as exc: raise aiohttp.web.HTTPNotFound from exc @@ -641,7 +643,7 @@ def test_and_assert(test_dict, data, header): "cooling_state" ] - self._write_json("data", data) + await self._write_json("data", data) if "FIXTURES" in os.environ: _LOGGER.info("Skipping tests: Requested fixtures only") # pragma: no cover diff --git a/tests/test_legacy_anna.py b/tests/test_legacy_anna.py index a3a7b8469..1a2e7e20f 100644 --- a/tests/test_legacy_anna.py +++ b/tests/test_legacy_anna.py @@ -16,7 +16,7 @@ class TestPlugwiseAnna(TestPlugwise): # pylint: disable=attribute-defined-outsi async def test_connect_legacy_anna(self): """Test a legacy Anna device.""" self.smile_setup = "legacy_anna" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_legacy_wrapper() assert smile.smile_hostname == "smile000000" @@ -53,7 +53,7 @@ async def test_connect_legacy_anna_2(self): """Test another legacy Anna device.""" self.smile_setup = "legacy_anna_2" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_legacy_wrapper() assert smile.smile_hostname == "smile000000" diff --git a/tests/test_legacy_p1.py b/tests/test_legacy_p1.py index d569ca52b..52f1d8504 100644 --- a/tests/test_legacy_p1.py +++ b/tests/test_legacy_p1.py @@ -15,7 +15,7 @@ async def test_connect_smile_p1_v2(self): """Test a legacy P1 device.""" self.smile_setup = "smile_p1_v2" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_legacy_wrapper() assert smile.smile_hostname == "smile000000" @@ -39,7 +39,7 @@ async def test_connect_smile_p1_v2_2(self): """Test another legacy P1 device.""" self.smile_setup = "smile_p1_v2_2" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_legacy_wrapper() assert smile.smile_hostname == "smile000000" @@ -56,7 +56,7 @@ async def test_connect_smile_p1_v2_2(self): # Now change some data and change directory reading xml from # emulating reading newer dataset after an update_interval - testdata_updated = self.load_testdata( + testdata_updated = await self.load_testdata( SMILE_TYPE, f"{self.smile_setup}_UPDATED_DATA" ) self.smile_setup = "updated/smile_p1_v2_2" diff --git a/tests/test_legacy_stretch.py b/tests/test_legacy_stretch.py index 021cc385b..13d079020 100644 --- a/tests/test_legacy_stretch.py +++ b/tests/test_legacy_stretch.py @@ -15,7 +15,7 @@ async def test_connect_stretch_v31(self): """Test a legacy Stretch with firmware 3.1 setup.""" self.smile_setup = "stretch_v31" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_legacy_wrapper(stretch=True) assert smile.smile_hostname == "stretch000000" @@ -39,7 +39,7 @@ async def test_connect_stretch_v31(self): # Now change some data and change directory reading xml from # emulating reading newer dataset after an update_interval - testdata_updated = self.load_testdata( + testdata_updated = await self.load_testdata( SMILE_TYPE, f"{self.smile_setup}_UPDATED_DATA" ) self.smile_setup = "updated/stretch_v31" @@ -55,7 +55,7 @@ async def test_connect_stretch_v23(self): """Test a legacy Stretch with firmware 2.3 setup.""" self.smile_setup = "stretch_v23" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_legacy_wrapper(stretch=True) assert smile.smile_hostname == "stretch000000" @@ -94,7 +94,7 @@ async def test_connect_stretch_v27_no_domain(self): # testdata dictionary with key ctrl_id_dev_id => keys:values self.smile_setup = "stretch_v27_no_domain" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_legacy_wrapper(stretch=True) assert smile.smile_hostname == "stretch000000" diff --git a/tests/test_p1.py b/tests/test_p1.py index 562ebc4fd..e379573e6 100644 --- a/tests/test_p1.py +++ b/tests/test_p1.py @@ -15,7 +15,7 @@ async def test_connect_p1v4_442_single(self): """Test a P1 firmware 4.4 single-phase setup.""" self.smile_setup = "p1v4_442_single" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000" @@ -33,7 +33,7 @@ async def test_connect_p1v4_442_single(self): # Now change some data and change directory reading xml from # emulating reading newer dataset after an update_interval - testdata_updated = self.load_testdata( + testdata_updated = await self.load_testdata( SMILE_TYPE, f"{self.smile_setup}_UPDATED_DATA" ) self.smile_setup = "updated/p1v4_442_single" @@ -65,7 +65,7 @@ async def test_connect_p1v4_442_triple(self): """Test a P1 firmware 4 3-phase setup.""" self.smile_setup = "p1v4_442_triple" - testdata = self.load_testdata(SMILE_TYPE, self.smile_setup) + testdata = await self.load_testdata(SMILE_TYPE, self.smile_setup) server, smile, client = await self.connect_wrapper() assert smile.smile_hostname == "smile000000"