Skip to content

Commit 311a796

Browse files
authored
fail fast if no suitable devices found (#34)
* fail fast if no suitable devices found * drop py3.7 from CI and update node to 20
1 parent 1df94f6 commit 311a796

File tree

3 files changed

+37
-14
lines changed

3 files changed

+37
-14
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
fail-fast: false
1919
matrix:
2020
os: [ ubuntu-latest, macos-latest, windows-latest ]
21-
python-version: [ '3.7', '3.8', '3.9', '3.10', '3.11' ]
21+
python-version: [ '3.8', '3.9', '3.10', '3.11' ]
2222
name: ${{ matrix.os }}-Python-${{ matrix.python-version }}
2323
steps:
2424
- uses: actions/checkout@v4
@@ -32,7 +32,7 @@ jobs:
3232
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10'
3333
uses: actions/setup-node@v4
3434
with:
35-
node-version: '16'
35+
node-version: '20'
3636

3737
- name: Install appium and adb
3838
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.10'

stf_appium_client/StfClient.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
from stf_client.api.user_api import UserApi
1515
from stf_client.api.devices_api import DevicesApi
1616

17+
STATUS_ONLINE = 3
18+
1719

1820
class StfClient(Logger):
1921
DEFAULT_ALLOCATION_TIMEOUT_SECONDS = 900
@@ -148,11 +150,12 @@ def release(self, device: dict) -> None:
148150
device['owner'] = None
149151
self.logger.info(f'{serial}: released')
150152

151-
def list_devices(self, requirements: dict, fields: str = "") -> list:
153+
def list_devices(self, requirements: dict, fields: str = "", available_filter: bool = True) -> list:
152154
"""
153155
Get list of devices filtered by given requirements and optional extra fields
154156
:param requirements: filter dictionary
155157
:param fields: extra fields to include
158+
:param available_filter: filter only available devices
156159
:return: list of objects that represent devices
157160
"""
158161
req_keys = list(requirements.keys())
@@ -165,15 +168,15 @@ def list_devices(self, requirements: dict, fields: str = "") -> list:
165168
fields = uniq(req_keys)
166169

167170
predicate = requirements.copy()
168-
169-
predicate.update(
170-
dict(
171-
present=True,
172-
ready=True,
173-
using=False,
174-
owner=None,
175-
status=3) # 3=Online
176-
)
171+
if available_filter:
172+
predicate.update(
173+
dict(
174+
present=True,
175+
ready=True,
176+
using=False,
177+
owner=None,
178+
status=STATUS_ONLINE)
179+
)
177180

178181
self.logger.debug(
179182
f"Find devices with requirements: {json.dumps(requirements)}, using fields: {','.join(fields)}")
@@ -182,6 +185,12 @@ def list_devices(self, requirements: dict, fields: str = "") -> list:
182185

183186
return filter_(devices, predicate)
184187

188+
def list_online_devices(self, requirements: dict):
189+
suitable_devices = self.list_devices(requirements=requirements)
190+
online = filter(lambda device: device.get('status') == STATUS_ONLINE, suitable_devices)
191+
return list(online)
192+
193+
185194
def find_and_allocate(self, requirements: dict,
186195
timeout_seconds: int = DEFAULT_ALLOCATION_TIMEOUT_SECONDS,
187196
shuffle: bool = True) -> dict:
@@ -197,7 +206,7 @@ def find_and_allocate(self, requirements: dict,
197206
:raises DeviceNotFound: suitable device not found or all devices are allocated already
198207
"""
199208
NotConnectedError.invariant(self._client, 'Not connected')
200-
suitable_devices = self.list_devices(requirements=requirements)
209+
suitable_devices = self.list_online_devices(requirements=requirements)
201210
DeviceNotFound.invariant(len(suitable_devices), 'no suitable devices found')
202211
if shuffle:
203212
random.shuffle(suitable_devices)
@@ -233,6 +242,12 @@ def find_wait_and_allocate(self,
233242
:return: device dictionary
234243
"""
235244
wait_until = time.time() + wait_timeout
245+
246+
# Fail fast if no suitable devices
247+
suitable_devices = self.list_devices(requirements=requirements, available_filter=False)
248+
if not suitable_devices:
249+
raise DeviceNotFound(f'No suitable devices found ({json.dumps(requirements)})')
250+
236251
print(f'wait_until: {wait_until}')
237252
while True:
238253
remaining_time = int(wait_until - time.time())

test/test_StfClient.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ class MockResp:
107107
with self.assertRaises(DeviceNotFound):
108108
self.client.find_and_allocate({})
109109

110+
def test_fail_fast(self):
111+
112+
class MockResp:
113+
devices = [{'serial': 123, 'present': True, 'ready': True, 'using': False, 'owner': None, 'status': 1}]
114+
self.DevicesApi.return_value.get_devices = MagicMock(return_value=MockResp())
115+
with self.assertRaises(DeviceNotFound):
116+
self.client.find_and_allocate({})
117+
110118
def test_list_devices(self):
111119
available = {'serial': 123, 'present': True, 'ready': True, 'using': False, 'owner': None, 'status': 3}
112120
self.client.get_devices = MagicMock(return_value=[available])
@@ -205,7 +213,7 @@ def test_allocation_context_first_success(self):
205213
@patch('time.sleep', side_effect=MagicMock())
206214
def test_allocation_context_wait_success(self, mock_sleep):
207215
dev1 = {'serial': '123', 'present': True, 'ready': True, 'using': False, 'owner': None, 'status': 3}
208-
self.client.get_devices = MagicMock(side_effect=[[], [dev1]])
216+
self.client.get_devices = MagicMock(side_effect=[[dev1], [], [dev1]])
209217
url = '123'
210218

211219
with self.client.allocation_context({"serial": '123'}) as device:

0 commit comments

Comments
 (0)