From a608afcf2c5267f6818cc04fdf625cb22c642396 Mon Sep 17 00:00:00 2001 From: Jussi Vatjus-Anttila Date: Thu, 1 Aug 2024 13:16:59 +0300 Subject: [PATCH 1/5] fail fast if no suitable devices found --- stf_appium_client/StfClient.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/stf_appium_client/StfClient.py b/stf_appium_client/StfClient.py index a71b4c7..e7b4e1f 100644 --- a/stf_appium_client/StfClient.py +++ b/stf_appium_client/StfClient.py @@ -148,11 +148,12 @@ def release(self, device: dict) -> None: device['owner'] = None self.logger.info(f'{serial}: released') - def list_devices(self, requirements: dict, fields: str = "") -> list: + def list_devices(self, requirements: dict, fields: str = "", available_filter: bool = True) -> list: """ Get list of devices filtered by given requirements and optional extra fields :param requirements: filter dictionary :param fields: extra fields to include + :param available_filter: filter only available devices :return: list of objects that represent devices """ req_keys = list(requirements.keys()) @@ -165,15 +166,15 @@ def list_devices(self, requirements: dict, fields: str = "") -> list: fields = uniq(req_keys) predicate = requirements.copy() - - predicate.update( - dict( - present=True, - ready=True, - using=False, - owner=None, - status=3) # 3=Online - ) + if available_filter: + predicate.update( + dict( + present=True, + ready=True, + using=False, + owner=None, + status=3) # 3=Online + ) self.logger.debug( f"Find devices with requirements: {json.dumps(requirements)}, using fields: {','.join(fields)}") @@ -233,6 +234,12 @@ def find_wait_and_allocate(self, :return: device dictionary """ wait_until = time.time() + wait_timeout + + # Fail fast if no suitable devices + suitable_devices = self.list_devices(requirements=requirements, available_filter=False) + if not suitable_devices: + raise DeviceNotFound(f'No suitable devices found ({json.dumps(requirements)})') + print(f'wait_until: {wait_until}') while True: remaining_time = int(wait_until - time.time()) From c5a52176a1e634c4bd7794e481ea6b1cbb9b2ae1 Mon Sep 17 00:00:00 2001 From: Jussi Vatjus-Anttila Date: Thu, 1 Aug 2024 13:37:43 +0300 Subject: [PATCH 2/5] filter online devices before fail fast --- stf_appium_client/StfClient.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/stf_appium_client/StfClient.py b/stf_appium_client/StfClient.py index e7b4e1f..13e4297 100644 --- a/stf_appium_client/StfClient.py +++ b/stf_appium_client/StfClient.py @@ -14,6 +14,8 @@ from stf_client.api.user_api import UserApi from stf_client.api.devices_api import DevicesApi +STATUS_ONLINE = 3 + class StfClient(Logger): DEFAULT_ALLOCATION_TIMEOUT_SECONDS = 900 @@ -173,7 +175,7 @@ def list_devices(self, requirements: dict, fields: str = "", available_filter: b ready=True, using=False, owner=None, - status=3) # 3=Online + status=STATUS_ONLINE) ) self.logger.debug( @@ -183,6 +185,12 @@ def list_devices(self, requirements: dict, fields: str = "", available_filter: b return filter_(devices, predicate) + def list_online_devices(self, requirements: dict): + suitable_devices = self.list_devices(requirements=requirements) + online = filter(lambda device: device.get('status') == STATUS_ONLINE, suitable_devices) + return list(online) + + def find_and_allocate(self, requirements: dict, timeout_seconds: int = DEFAULT_ALLOCATION_TIMEOUT_SECONDS, shuffle: bool = True) -> dict: @@ -198,7 +206,7 @@ def find_and_allocate(self, requirements: dict, :raises DeviceNotFound: suitable device not found or all devices are allocated already """ NotConnectedError.invariant(self._client, 'Not connected') - suitable_devices = self.list_devices(requirements=requirements) + suitable_devices = self.list_online_devices(requirements=requirements) DeviceNotFound.invariant(len(suitable_devices), 'no suitable devices found') if shuffle: random.shuffle(suitable_devices) @@ -239,7 +247,7 @@ def find_wait_and_allocate(self, suitable_devices = self.list_devices(requirements=requirements, available_filter=False) if not suitable_devices: raise DeviceNotFound(f'No suitable devices found ({json.dumps(requirements)})') - + print(f'wait_until: {wait_until}') while True: remaining_time = int(wait_until - time.time()) From 31677bfdf4e253441cb6dfca2ebfe96fa82ad1fb Mon Sep 17 00:00:00 2001 From: Jussi Vatjus-Anttila Date: Thu, 1 Aug 2024 14:26:41 +0300 Subject: [PATCH 3/5] fix one test --- test/test_StfClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_StfClient.py b/test/test_StfClient.py index 7170098..4f25c34 100644 --- a/test/test_StfClient.py +++ b/test/test_StfClient.py @@ -205,7 +205,7 @@ def test_allocation_context_first_success(self): @patch('time.sleep', side_effect=MagicMock()) def test_allocation_context_wait_success(self, mock_sleep): dev1 = {'serial': '123', 'present': True, 'ready': True, 'using': False, 'owner': None, 'status': 3} - self.client.get_devices = MagicMock(side_effect=[[], [dev1]]) + self.client.get_devices = MagicMock(side_effect=[[dev1], [], [dev1]]) url = '123' with self.client.allocation_context({"serial": '123'}) as device: From a7b977fd95f2e78ea07ad37bb877e8fdd558e052 Mon Sep 17 00:00:00 2001 From: Jussi Vatjus-Anttila Date: Thu, 1 Aug 2024 14:29:06 +0300 Subject: [PATCH 4/5] add simple test --- test/test_StfClient.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/test_StfClient.py b/test/test_StfClient.py index 4f25c34..ba5a7e1 100644 --- a/test/test_StfClient.py +++ b/test/test_StfClient.py @@ -107,6 +107,14 @@ class MockResp: with self.assertRaises(DeviceNotFound): self.client.find_and_allocate({}) + def test_fail_fast(self): + + class MockResp: + devices = [{'serial': 123, 'present': True, 'ready': True, 'using': False, 'owner': None, 'status': 1}] + self.DevicesApi.return_value.get_devices = MagicMock(return_value=MockResp()) + with self.assertRaises(DeviceNotFound): + self.client.find_and_allocate({}) + def test_list_devices(self): available = {'serial': 123, 'present': True, 'ready': True, 'using': False, 'owner': None, 'status': 3} self.client.get_devices = MagicMock(return_value=[available]) From 39fbdd359000931d7c3a36a1d49e3343c9ca5d11 Mon Sep 17 00:00:00 2001 From: Jussi Vatjus-Anttila Date: Fri, 2 Aug 2024 08:05:34 +0300 Subject: [PATCH 5/5] drop py3.7 from CI and update node to 20 --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c19d80e..a386159 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] - python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11' ] + python-version: [ '3.8', '3.9', '3.10', '3.11' ] name: ${{ matrix.os }}-Python-${{ matrix.python-version }} steps: - uses: actions/checkout@v4 @@ -32,7 +32,7 @@ jobs: if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10' uses: actions/setup-node@v4 with: - node-version: '16' + node-version: '20' - name: Install appium and adb if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10'