diff --git a/CHANGELOG b/CHANGELOG index 761b5a8..b23aca8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +2.61.0 + * Fix error with double slash for some API calls + * Added new test methods 2.60.0 * Added Build API functions 2.43.0 diff --git a/setup.py b/setup.py index c7294c0..3998e91 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ import sys, os -version = '2.60.0' +version = '2.61.0' setup(name='testdroid', version=version, diff --git a/testdroid/__init__.py b/testdroid/__init__.py index f7e4948..4441d77 100755 --- a/testdroid/__init__.py +++ b/testdroid/__init__.py @@ -11,7 +11,7 @@ from optparse import OptionParser from datetime import datetime -__version__ = '2.60.0' +__version__ = '2.61.0' FORMAT = "%(message)s" logging.basicConfig(format=FORMAT) @@ -417,7 +417,7 @@ def upload_test_file(self, project_id, filename): """ def delete_project_parameters(self, project_id, parameter_id): me = self.get_me() - path = "/users/%s/projects/%s/config/parameters/%s" % ( me['id'], project_id, parameter_id ) + path = "users/%s/projects/%s/config/parameters/%s" % ( me['id'], project_id, parameter_id ) return self.delete(path=path) """ Get project parameters @@ -438,7 +438,7 @@ def upload_data_file(self, project_id, filename): def set_project_parameters(self, project_id, parameters): #set key value pair for project. e.g. : {'key' : 'my_key', 'value':'my_value'} me = self.get_me() - path = "/users/%s/projects/%s/config/parameters" % ( me['id'], project_id ) + path = "users/%s/projects/%s/config/parameters" % ( me['id'], project_id ) return self.post(path=path, payload=parameters) """ Get project config @@ -455,7 +455,7 @@ def set_project_config(self, project_id, payload): if isinstance(payload, str): payload=json.loads(payload) me = self.get_me() - path = "/users/%s/projects/%s/config" % ( me['id'], project_id ) + path = "users/%s/projects/%s/config" % ( me['id'], project_id ) return self.post(path=path, payload=payload) """Set project framework based on a framework integer id @@ -514,7 +514,7 @@ def start_test_run(self, project_id, device_group_id=None, device_model_ids=None # actually start the test run me = self.get_me() - path = "/users/%s/projects/%s/runs" % (me['id'], project_id) + path = "users/%s/projects/%s/runs" % (me['id'], project_id) test_run = self.post(path=path, payload=payload) print("Test run id: %s" % test_run['id']) print("Name: %s" % test_run['displayName']) @@ -676,8 +676,6 @@ def get_jobs(self, limit=0): """ def create_job(self, job_name, content, job_type="BUILD"): job = self.post(path="me/jobs", payload={"name": job_name, "content": content, "type": job_type}) - print(job) - logger.info("Job %s: %s (%s) created" % (job['id'], job['name'], job['type'] )) return job @@ -747,6 +745,34 @@ def download_build_output_files(self, job_id, build_id, results_folder="results" logger.info("No files to download") logger.info("") + """ Awaits completion of the given test run + """ + def wait_build(self, job_id, build_id): + if job_id and build_id: + print("Awaiting completion of build with id {}. Will wait forever polling every {}.".format( + build_id, + '{} minutes'.format(self.polling_interval_mins) if self.polling_interval_mins != 1 else 'minute')) + + while True: + time.sleep(self.polling_interval_mins * 6) + if not self.api_key: + self.access_token = None + self.get_token() + buildStatus = self.get_build(job_id, build_id) + if buildStatus and 'state' in buildStatus: + if buildStatus['state'] == "FINISHED": + print("The build with id: %s has FINISHED with status: %s" % (build_id, buildStatus['status'])) + break + elif buildStatus['state'] == "CREATED": + print("[%s] The build with id: %s is awaiting to be scheduled" % (time.strftime("%H:%M:%S"), build_id)) + continue + elif buildStatus['state'] == "BUILDING": + print("[%s] The build with id: %s is running" % (time.strftime("%H:%M:%S"), build_id)) + continue + + print("Couldn't establish the state of the build with id: %s. Aborting" % build_id) + print(buildStatus) + sys.exit(1) """ Downloads test run files to a directory hierarchy """ @@ -879,6 +905,18 @@ def format_epilog(self, formatter): Download test run screenshots. Screenshots will be downloaded to current directory in a structure: [test-run-id]/[device-run-id]-[device-name]/screenshots/... + jobs Get list of your jobs + builds Get list of your builds + create-job Create a new job. Job configuration in Jenkins pipeline format + See the sample of Jenkisfile in http://docs.bitbar.com/build-service/guide.html + update-job + Update existing job + create-build Create a new build job. See https://cloud.testdroid.com/cloud/swagger-ui.html + for details of build configuration + delete-job Delete job and all the builds in it + delete-build Delete build by id + download-builds-files Download all the results of the specific build + wait-build Await completion (polling) of the build """ parser = MyParser(usage=usage, description=description, epilog=epilog, version="%s %s" % ("%prog", __version__)) @@ -929,7 +967,8 @@ def get_commands(self): "create-build": self.create_build, "delete-job": self.delete_job, "delete-build": self.delete_build, - "download-builds-files": self.download_build_output_files + "download-builds-files": self.download_build_output_files, + "wait-build": self.wait_build } return commands diff --git a/testdroid/tests/test_all.py b/testdroid/tests/test_all.py index 6813a76..e8d1c04 100644 --- a/testdroid/tests/test_all.py +++ b/testdroid/tests/test_all.py @@ -6,18 +6,23 @@ import testdroid import responses -URL_BASE = 'https://cloud.bitbar.com' -URL_API = '{}/api/v2'.format(URL_BASE) -URL_API_ME = '{}/me'.format(URL_API) -URL_USERS = '{}/users'.format(URL_BASE) JSON = {'k': 'v'} PROJECT_ID = 2 TEST_RUN_ID = 3 DEVICE_RUN_ID = 4 DEVICE_SESSION_ID = 5 +DEVICE_GROUP_ID = 6 +USER_ID = 7 +PARAM_ID = 8 TAGS = 'tags' LIMIT = 0 + +URL_BASE = 'https://cloud.bitbar.com' +URL_API = '{}/api/v2'.format(URL_BASE) +URL_API_ME = '{}/me'.format(URL_API) +URL_USERS = '{}/users/{}'.format(URL_API,USER_ID) + t = testdroid.Testdroid() @@ -167,7 +172,7 @@ def test_get_device_run_files_without_tags(self): @responses.activate def test_get_device_run_files_with_tags(self): - url = '{}/projects/{}/runs/{}/device-sessions/{}/output-file-set/files?tag[]?={}'.format( + url = '{}/projects/{}/runs/{}/device-sessions/{}/output-file-set/files?tag[]={}'.format( URL_API_ME, PROJECT_ID, TEST_RUN_ID, DEVICE_SESSION_ID, TAGS) responses.add(responses.GET, url, json=JSON, status=200) response = t.get_device_run_files(PROJECT_ID, TEST_RUN_ID, @@ -180,3 +185,40 @@ def test_get_input_files(self): URL_API_ME, LIMIT) responses.add(responses.GET, url, json=JSON, status=200) self.assertEqual(t.get_input_files(LIMIT), JSON) + + @responses.activate + def test_start_test_run(self): + url = '{}/projects/2'.format( + URL_API_ME) + json = { + 'id': USER_ID, + 'name': 'Sample project', + } + responses.add(responses.GET, url, json=json, status=200) + + responses.add(responses.GET, URL_API_ME, json=json, status=200) + url = '{}/projects/{}/runs'.format( + URL_USERS, PROJECT_ID) + json = { + 'id': 12, + 'displayName': "My test run" + } + + + responses.add(responses.POST, url, json=json, status=201) + self.assertEqual(t.start_test_run(PROJECT_ID, DEVICE_GROUP_ID), json['id']) + + @responses.activate + def test_delete_project_parameters(self): + path = 'projects/{}/config/parameters/{}'.format(PROJECT_ID, PARAM_ID) + url = '{}/{}'.format(URL_USERS, path) + + json = { + 'id': USER_ID + } + responses.add(responses.GET, URL_API_ME, json=json, status=200) + responses.add(responses.DELETE, url, json=JSON, status=204) + response = t.delete_project_parameters(PROJECT_ID, PARAM_ID) + self.assertEqual(response.status_code, 204) + +