diff --git a/test/appium/tests/conftest.py b/test/appium/tests/conftest.py index 7c5e0b3f5d4..23598023da1 100644 --- a/test/appium/tests/conftest.py +++ b/test/appium/tests/conftest.py @@ -1,7 +1,7 @@ import os import re import signal -import urllib.request +import requests from contextlib import contextmanager from dataclasses import dataclass from datetime import datetime @@ -161,6 +161,57 @@ def is_uploaded(): return True +@contextmanager +def _upload_time_limit(seconds): + def signal_handler(signum, frame): + raise TimeoutError("Apk upload took more than %s seconds" % seconds) + + signal.signal(signal.SIGALRM, signal_handler) + signal.alarm(seconds) + try: + yield + finally: + signal.alarm(0) + +class UploadApkException(Exception): + pass + +def _upload_and_check_response(apk_file_path): + with _upload_time_limit(600): + with open(apk_file_path, 'rb') as f: + resp = sauce.storage._session.request('post', '/v1/storage/upload', files={'payload': f}) + + try: + if resp['item']['name'] != test_suite_data.apk_name: + raise UploadApkException("Incorrect apk was uploaded to Sauce storage, response:\n%s" % resp) + except KeyError: + raise UploadApkException("Error when uploading apk to Sauce storage, response:\n%s" % resp) + +def _upload_and_check_response_with_retries(apk_file_path, retries=3): + for _ in range(retries): + try: + _upload_and_check_response(apk_file_path) + break + except (ConnectionError, RemoteDisconnected): + sleep(10) + +def _download_apk(url): + # Absolute path adde to handle CI runs. + apk_path = os.path.join(os.path.dirname(__file__), test_suite_data.apk_name) + + print('Downloading: %s' % url) + try: + resp = requests.get(url) + resp.raise_for_status() + except requests.RequestException as err: + print(resp.text) + raise err + + with open(apk_path, 'wb') as f: + f.write(resp.content) + + return apk_path + def pytest_configure(config): global option option = config.option @@ -180,75 +231,49 @@ def pytest_configure(config): apibase = 'eu-central-1.saucelabs.com' else: raise NotImplementedError("Unknown SauceLabs datacenter") + global sauce sauce = SauceLab('https://api.' + apibase + '/', sauce_username, sauce_access_key) if config.getoption('log_steps'): import logging logging.basicConfig(level=logging.INFO) - if config.getoption('env') != 'api': - test_suite_data.apk_name = ([i for i in [i for i in config.getoption('apk').split('/') - if '.apk' in i]])[0] - if is_master(config): - pr_number = config.getoption('pr_number') - if config.getoption('testrail_report'): - if pr_number: - run_number = len(testrail_report.get_runs(pr_number)) + 1 - run_name = 'PR-%s run #%s' % (pr_number, run_number) - else: - run_name = test_suite_data.apk_name - testrail_report.add_run(run_name) - if pr_number: - from github import Github - repo = Github(github_token).get_user('status-im').get_repo('status-mobile') - pull = repo.get_pull(int(pr_number)) - pull.get_commits()[0].create_status(state='pending', context='Mobile e2e tests', - description='e2e tests are running') - if config.getoption('env') == 'sauce': - if not is_uploaded(): - def _upload_and_check_response(apk_file_path): - @contextmanager - def _upload_time_limit(seconds): - def signal_handler(signum, frame): - raise TimeoutError("Apk upload took more than %s seconds" % seconds) - - signal.signal(signal.SIGALRM, signal_handler) - signal.alarm(seconds) - try: - yield - finally: - signal.alarm(0) - - with _upload_time_limit(600): - class UploadApkException(Exception): - pass - - with open(apk_file_path, 'rb') as f: - resp = sauce.storage._session.request('post', '/v1/storage/upload', - files={'payload': f}) - try: - if resp['item']['name'] != test_suite_data.apk_name: - raise UploadApkException( - "Incorrect apk was uploaded to Sauce storage, response:\n%s" % resp) - except KeyError: - raise UploadApkException( - "Error when uploading apk to Sauce storage, response:\n%s" % resp) - - if 'http' in config.getoption('apk'): - # it works with just a file_name, but I've added full path because not sure how it'll behave on the remote run (Jenkins) - file_path, to_remove = os.path.join(os.path.dirname(__file__), test_suite_data.apk_name), True - urllib.request.urlretrieve(config.getoption('apk'), - filename=file_path) # if url is not valid it raises an error - else: - file_path, to_remove = config.getoption('apk'), False - - for _ in range(3): - try: - _upload_and_check_response(apk_file_path=file_path) - break - except (ConnectionError, RemoteDisconnected): - sleep(10) - if to_remove: - os.remove(file_path) + if config.getoption('env') == 'api': + return + + test_suite_data.apk_name = ([i for i in [i for i in config.getoption('apk').split('/') + if '.apk' in i]])[0] + if not is_master(config): + return + + pr_number = config.getoption('pr_number') + if config.getoption('testrail_report'): + if pr_number: + run_number = len(testrail_report.get_runs(pr_number)) + 1 + run_name = 'PR-%s run #%s' % (pr_number, run_number) + else: + run_name = test_suite_data.apk_name + testrail_report.add_run(run_name) + + if pr_number: + from github import Github + repo = Github(github_token).get_user('status-im').get_repo('status-mobile') + pull = repo.get_pull(int(pr_number)) + pull.get_commits()[0].create_status( + state='pending', + context='Mobile e2e tests', + description='e2e tests are running' + ) + + if config.getoption('env') == 'sauce' and not is_uploaded(): + apk_src = config.getoption('apk') + if apk_src.startswith('http'): + apk_path = _download_apk(apk_src) + else: + apk_path = apk_src + + _upload_and_check_response_with_retries(apk_path) + if apk_src.startswith('http'): + os.remove(apk_path) def pytest_unconfigure(config):