Merge lp:~canonical-platform-qa/qakit/test-execution-gathering into lp:qakit

Proposed by Allan LeSage
Status: Merged
Approved by: Christopher Lee
Approved revision: 31
Merged at revision: 22
Proposed branch: lp:~canonical-platform-qa/qakit/test-execution-gathering
Merge into: lp:qakit
Diff against target: 811 lines (+720/-3)
7 files modified
qakit/metrics/practitest/instances.py (+102/-0)
qakit/metrics/practitest/testsets.py (+144/-0)
qakit/metrics/test_execution.py (+131/-0)
qakit/metrics/tests/test_practitest_instances.py (+113/-0)
qakit/metrics/tests/test_practitest_testsets.py (+80/-0)
qakit/metrics/tests/test_test_execution.py (+99/-0)
qakit/practitest/practitest.py (+51/-3)
To merge this branch: bzr merge lp:~canonical-platform-qa/qakit/test-execution-gathering
Reviewer Review Type Date Requested Status
Christopher Lee (community) Approve
Review via email: mp+275778@code.launchpad.net

Commit message

Gather test execution data from PractiTest.

Description of the change

Gather test execution data from PractiTest and deposit in a mongodb database.

To post a comment you must log in.
Revision history for this message
Christopher Lee (veebers) wrote :

Looking good, a couple of inline comments.

review: Needs Fixing
25. By Allan LeSage

De-shebangify.

26. By Allan LeSage

Adjust count insertion.

27. By Allan LeSage

Adjust a warning log message.

28. By Allan LeSage

Adjust a docstring to call out list.

29. By Allan LeSage

Move no last testset logic up a level, some whitespace and naming changes thrown in.

30. By Allan LeSage

Resolve flake8 config conflict, remove NOQA.

31. By Allan LeSage

Just raise.

Revision history for this message
Allan LeSage (allanlesage) wrote :

Addressed in numbered revisions, thanks Chris.

Revision history for this message
Christopher Lee (veebers) wrote :

LGTM, thanks for the changes.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'qakit/metrics/practitest'
=== added file 'qakit/metrics/practitest/instances.py'
--- qakit/metrics/practitest/instances.py 1970-01-01 00:00:00 +0000
+++ qakit/metrics/practitest/instances.py 2015-10-29 22:05:12 +0000
@@ -0,0 +1,102 @@
1# UESQA Metrics
2# Copyright (C) 2015 Canonical
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17
18import logging
19
20import qakit.practitest.practitest as practitest
21
22logger = logging.getLogger(__name__)
23
24
25def append_testset_id(instance):
26 """Append TestSet ID to the given PractiTest instance dict.
27
28 This makes querying much easier later, else it has to be parsed
29 out of a string.
30
31 :param instance: a PractiTest instance
32
33 """
34 instance['testset_id'] = int(instance['id'].split(':')[0])
35 return instance
36
37
38def append_data_to_instance_dict(instance):
39 """Append TestSet ID and last run datetime to the given instance.
40
41 :param instance: a PractiTest instance to which to append data
42
43 """
44 instance = practitest.append_last_run_datetime(instance)
45 instance = append_testset_id(instance)
46 return instance
47
48
49def update_testset_instances(db, session, testset_id):
50 """Retrieve instances from PractiTest for the given TestSet ID.
51
52 NOTE that old runs get crushed on the PractiTest side: e.g. if a
53 test fails and it's re-run, the failure is lost to the API.
54
55 :param db: MongoClient db in which to store PractiTest results
56 :param practitest_session: PractitestSession
57 :param testset_id: the PractiTest TestSet for which to retrieve instances.
58
59 """
60 logger.info("Retrieving instances for TestSet {}.".format(testset_id))
61 try:
62 instances = session.get_instances(testset_id)
63 except ValueError as e:
64 logger.warn('TestSet not found for id: {}: '.format(
65 testset_id), str(e))
66 return 0
67 count = 0
68 for instance in instances:
69 instance = append_data_to_instance_dict(instance)
70 result = db.instances.update({'system_id': instance['system_id']},
71 instance,
72 upsert=True)
73 count += result['n']
74 logger.info("Retrieved {} instances for TestSet {}.".format(count,
75 testset_id))
76 return count
77
78
79def update_testsets_instances(db, practitest_session, testset_ids=None):
80 """Update instances for the given testsets, finding new instances.
81
82 An instance represents a test execution in PractiTest. Note that if a
83 test is executed more than once, only the last execution is reported via
84 the API: e.g. if a test fails and then is run again and passes, the
85 failure is lost to us.
86
87 :param db: a MongoClient db in which to store PractiTest instances
88 :param practitest_session: a PractitestSession
89 :param testset_ids: list of PractiTest TestSet ids for which to get
90 instances: numbers appear in header of TestSet webpage. If no ids are
91 given, retrieve instances for all *known* TestSets (i.e. all already
92 in the given database).
93
94 """
95 logger.info("Updating TestSets with new instance runs.")
96 if testset_ids is None:
97 testset_ids = [testset['id'] for testset in db.testsets.find()]
98 count = 0
99 for testset_id in testset_ids:
100 count += update_testset_instances(
101 db, practitest_session, testset_id)
102 logger.info("Updated {} instances.".format(count))
0103
=== added file 'qakit/metrics/practitest/testsets.py'
--- qakit/metrics/practitest/testsets.py 1970-01-01 00:00:00 +0000
+++ qakit/metrics/practitest/testsets.py 2015-10-29 22:05:12 +0000
@@ -0,0 +1,144 @@
1# UESQA Metrics
2# Copyright (C) 2015 Canonical
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17import logging
18import pymongo
19from qakit.practitest.practitest import append_last_run_datetime
20
21logger = logging.getLogger(__name__)
22
23
24def retrieve_testset(db, practitest_session, testset_id):
25 """Retrieve a testset of the given ID, storing it in our database.
26
27 :param db: a MongoClient collection in which testsets are stored
28 :param practitest_session: a PractiTestSession
29 :param testset_id: the ID of the PractiTest testset to retrieve
30 :raises ValueError: if testset_id is not found in PractiTest
31
32 """
33 try:
34 testset = practitest_session.get_testset(testset_id)
35 except ValueError:
36 logger.warn("Testset {} not found!".format(testset_id))
37 raise
38 testset = append_last_run_datetime(testset)
39 result = db.testsets.update(
40 {'system_id': testset['system_id']},
41 testset,
42 upsert=True)
43 if result['updatedExisting']:
44 logger.info("Updated testset {}.".format(testset_id))
45 else:
46 logger.info("Inserted testset {}.".format(testset_id))
47 return result
48
49
50def _get_last_known_testset_id(db):
51 """Get the last known testset ID from a db.
52
53 :param db: a MongoClient collection in which testsets are stored
54
55 """
56 testsets = db.testsets.find()
57 try:
58 return testsets.sort('id', pymongo.DESCENDING).limit(1)[0]['id']
59 except IndexError:
60 raise ValueError("No testsets found in db.")
61
62
63def update_testsets(db, practitest_session, testset_ids=None):
64 """Update PractiTest testsets with new data.
65
66 Note that we update testsets themselves only--instances (i.e. test
67 executions) should be updated separately.
68
69 :param db: a MongoClient collection in which testsets are stored
70 :param practitest_session: a PractiTestSession
71 :param testset_ids: a list of testsets to retrieve; if not specified,
72 all known testsets in the given db are updated
73
74 """
75 logger.info("Updating existing testsets.")
76 if testset_ids is None:
77 testset_ids = [testset['id'] for testset in db.testsets.find()]
78 count = 0
79 for testset_id in testset_ids:
80 try:
81 result = retrieve_testset(db, practitest_session, testset_id)
82 count += result['n']
83 except ValueError:
84 logger.warn(
85 "Testset {} not found, assume deleted.".format(testset_id))
86 logger.info("Updated {} testsets.".format(count))
87
88
89def _scan_for_testsets(db, practitest_session, testset_id_range):
90 """Scan a range of testset IDs, retrieving each if it exists in PractiTest.
91
92 Use this to scout ahead while discovering new testsets.
93
94 :param db: a MongoClient collection in which testsets are stored
95 :param practitest_session: a PractiTestSession
96 :param testset_id_range: a range of testset IDs
97
98 """
99 count = 0
100 for testset_id in testset_id_range:
101 try:
102 result = retrieve_testset(db, practitest_session, testset_id)
103 count += result['n']
104 except ValueError:
105 logger.debug("Testset {} not found.".format(testset_id))
106 return count
107
108
109def discover_new_testsets(db, practitest_session, increment=50):
110 """Discover and retrieve new testsets in PractiTest.
111
112 PractiTest API won't give a list of IDs of testsets belonging to a
113 project. IDs are assigned incrementally and occasionally deleted;
114 scan a given number of IDs ahead and attempt to find a testset at
115 each ID, continue until a full scan comes up empty.
116
117 :param db: a MongoClient collection in which testsets are stored
118 :param practitest_session: a PractiTestSession
119 :param increment: the increment by which to scan ahead, defaults to 50
120
121 """
122 logger.info("Discovering new testsets in PractiTest.")
123 try:
124 logger.info("Last known testset is {}.".format(last_testset_id))
125 last_testset_id = _get_last_known_testset_id(db)
126 except ValueError as e:
127 logger.info("No testsets in db.")
128 last_testset_id = 0
129 total_count = 0
130 testsets_found = True
131 next_testset_id = last_testset_id + 1
132 while testsets_found:
133 testset_id_range = range(next_testset_id, next_testset_id+increment)
134 scan_count = _scan_for_testsets(
135 db,
136 practitest_session,
137 testset_id_range)
138 if scan_count is 0:
139 testsets_found = False
140 else:
141 next_testset_id = testset_id_range[-1] + 1
142 total_count += scan_count
143 logger.info("Retrieved {} testsets.".format(total_count))
144 return total_count
0145
=== added file 'qakit/metrics/test_execution.py'
--- qakit/metrics/test_execution.py 1970-01-01 00:00:00 +0000
+++ qakit/metrics/test_execution.py 2015-10-29 22:05:12 +0000
@@ -0,0 +1,131 @@
1#!/usr/bin/python3
2# UESQA Metrics
3# Copyright (C) 2015 Canonical
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18
19import argparse
20import configparser
21import logging
22import sys
23
24import pymongo
25
26import qakit.config as qakit_config
27import qakit.metrics.practitest.instances as instances
28import qakit.metrics.practitest.testsets as testsets
29import qakit.metrics.util as util
30import qakit.practitest.practitest as practitest
31
32logger = logging.getLogger(__name__)
33
34
35def update_testsets(db, practitest_session, testset_ids=None):
36 """Discover new and update existing PractiTest testsets.
37
38 :param db: a MongoClient collection in which PractiTest data is stored
39 :param practitest_session: a PractiTestSession
40 :param testsets: a list of PractiTest testset ids to update; if not
41 specified, new testsets are discovered and all existing testsets are
42 updated
43
44 """
45 if not testset_ids:
46 testset_ids = [testset['id'] for testset in db.testsets.find()]
47 # (newly-discovered testsets will be updated upon discovery)
48 testsets.discover_new_testsets(db, practitest_session)
49 testsets.update_testsets(db, practitest_session, testset_ids=testset_ids)
50
51
52def update_instances(db, practitest_session, testset_ids=None):
53 """Discover new and update existing instances for the given testset IDs.
54
55 :param db: a MongoClient collection in which PractiTest data is stored
56 :param practitest_session: a PractiTestSession
57 :param testsets: a list of PractiTest testset ids to update; if not
58 specified, all known testsets are updated
59
60 """
61 if not testset_ids:
62 testset_ids = [testset['id'] for testset in db.testsets.find()]
63 instances.update_testsets_instances(
64 db,
65 practitest_session,
66 testset_ids=testset_ids)
67
68
69def update_test_execution_data(db, practitest_session, testset_ids=None):
70 """Update PractiTest test execution data: testsets and instances.
71
72 :param db: a MongoClient collection in which PractiTest data is stored
73 :param practitest_session: a PractiTestSession
74 :param testsets: a list of PractiTest testset ids to update; if not
75 specified, new testsets are discovered and all existing testsets are
76 updated
77
78 """
79 update_testsets(db, practitest_session, testset_ids=testset_ids)
80 update_instances(db, practitest_session, testset_ids=testset_ids)
81
82
83def _read_config(config_filepath):
84 """Parse the config at the given filepath, returning a config dict.
85
86 :param config_filepath: the filepath to a configuration file
87
88 """
89 config_file = configparser.ConfigParser()
90 config_file.read(config_filepath)
91 config = {}
92 for var_name in ('PRACTITEST_PROJECT_ID',
93 'PRACTITEST_API_KEY',
94 'PRACTITEST_API_SECRET_KEY'):
95 new_var_name = var_name.lower().replace('practitest_', '')
96 config[new_var_name] = config_file['DEFAULT'][var_name]
97 return config
98
99
100def _parse_arguments():
101 parser = argparse.ArgumentParser(
102 "Retrieve PractiTest results, storing them in a MongoDB database.")
103 parser.add_argument(
104 nargs='*',
105 dest='testsets',
106 help='TestSet from which to retrieve instances',
107 type=int)
108 return parser.parse_args()
109
110
111def main():
112 util.setup_logging()
113 config_dict = _read_config(qakit_config.get_config_file_location())
114 args = _parse_arguments()
115 conn = pymongo.MongoClient()
116 db = conn.metrics
117 practitest_session = practitest.PractitestSession(
118 config_dict['project_id'],
119 config_dict['api_key'],
120 config_dict['api_secret_key'])
121 update_test_execution_data(
122 db,
123 practitest_session,
124 testset_ids=args.testsets)
125
126
127if __name__ == '__main__':
128 try:
129 sys.exit(main())
130 except Exception as e:
131 logger.error(e, exc_info=True)
0132
=== added file 'qakit/metrics/tests/test_practitest_instances.py'
--- qakit/metrics/tests/test_practitest_instances.py 1970-01-01 00:00:00 +0000
+++ qakit/metrics/tests/test_practitest_instances.py 2015-10-29 22:05:12 +0000
@@ -0,0 +1,113 @@
1# UESQA Metrics
2# Copyright (C) 2015 Canonical
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17import unittest
18from unittest import mock
19
20from bson import ObjectId
21
22import qakit.metrics.practitest.instances as instances
23
24
25INSTANCE = {
26 "_id": ObjectId("5590253dca21169c04a38b51"),
27 "tester": {
28 "value": "iahmad",
29 "name": "Tester"
30 },
31 "last_run_week_number": 20,
32 "name": "Complete Edges Intro",
33 "last_run": {
34 "value": "21-May-2015 20:59",
35 "name": "Last Run"
36 },
37 "___f_10578": {
38 "value": None,
39 "name": "Build #"
40 },
41 "id": "10:1",
42 "system_id": 1393092,
43 "run_status": {
44 "value": "FAILED",
45 "name": "Run Status"
46 }
47}
48
49
50TESTSETS = [
51 {'id': 761},
52 {'id': 762},
53 {'id': 763},
54 {'id': 764},
55 {'id': 765},
56 {'id': 766},
57 {'id': 767}
58]
59
60
61class AppendTestSetIdTestCase(unittest.TestCase):
62
63 def test_testset_id_appended_correctly(self):
64 instance = instances.append_testset_id(INSTANCE)
65 self.assertEqual(10, instance['testset_id'])
66
67
68class AppendDataToInstanceDictTestCase(unittest.TestCase):
69
70 @mock.patch('qakit.practitest.practitest.append_last_run_datetime')
71 @mock.patch('qakit.metrics.practitest.instances.append_testset_id')
72 def test_appends_called(self,
73 mock_append_testset_id,
74 mock_append_last_run_datetime):
75 # I agree, this should never fail :)
76 mock_append_last_run_datetime.return_value = 'fake-instance'
77 instances.append_data_to_instance_dict(INSTANCE)
78 mock_append_last_run_datetime.assert_called_with(INSTANCE)
79 mock_append_testset_id.assert_called_with('fake-instance')
80
81
82@mock.patch('qakit.metrics.practitest.instances.update_testset_instances')
83class UpdateTestSetInstancesTestCase(unittest.TestCase):
84
85 def test_no_testset_ids_specified(self, mock_update_testset_instances):
86 db = mock.Mock(
87 testsets=mock.Mock(
88 find=mock.Mock(return_value=TESTSETS)))
89 instances.update_testsets_instances(
90 db,
91 'fake-practitest-session',
92 None)
93 self.assertEqual(7, mock_update_testset_instances.call_count)
94
95 def test_one_testset_specified(self, mock_update_testset_instances):
96 instances.update_testsets_instances(
97 'fake-db',
98 'fake-practitest-session',
99 [761])
100 self.assertEqual(1, mock_update_testset_instances.call_count)
101 mock_update_testset_instances.assert_called_with(
102 'fake-db', 'fake-practitest-session', 761)
103
104 def test_several_testsets_specified(self, mock_update_testset_instances):
105 testset_ids = [761, 762, 763]
106 instances.update_testsets_instances(
107 'fake-db',
108 'fake-practitest-session',
109 testset_ids)
110 self.assertEqual(3, mock_update_testset_instances.call_count)
111 for testset_id in testset_ids:
112 mock_update_testset_instances.assert_any_call(
113 'fake-db', 'fake-practitest-session', testset_id)
0114
=== added file 'qakit/metrics/tests/test_practitest_testsets.py'
--- qakit/metrics/tests/test_practitest_testsets.py 1970-01-01 00:00:00 +0000
+++ qakit/metrics/tests/test_practitest_testsets.py 2015-10-29 22:05:12 +0000
@@ -0,0 +1,80 @@
1# UESQA Metrics
2# Copyright (C) 2015 Canonical
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17import unittest
18from unittest import mock
19
20import qakit.metrics.practitest.testsets as testsets
21
22TESTSETS = [
23 {'id': 761},
24 {'id': 762},
25 {'id': 763},
26 {'id': 764},
27 {'id': 765},
28 {'id': 766},
29 {'id': 767}
30]
31
32
33class GetLastKnownTestsetIdTestCase(unittest.TestCase):
34
35 def testset_exists(self):
36 db = mock.Mock(
37 testsets=mock.Mock(
38 find=mock.Mock(
39 return_value=mock.Mock(
40 sort=mock.Mock(
41 return_value=mock.Mock(
42 limit=mock.Mock(
43 return_value=TESTSETS[:1])))))))
44 self.assertEqual(
45 761,
46 testsets._get_last_known_testset_id(db))
47
48 def test_no_testsets_raises_valueerror(self):
49 testsets_ = mock.MagicMock()
50 get_item = testsets_.sort.return_value.limit.return_value.__getitem__
51 get_item.side_effect = IndexError
52 db = mock.Mock(
53 testsets=mock.Mock(
54 find=mock.Mock(
55 return_value=testsets_)))
56 with self.assertRaises(ValueError):
57 testsets._get_last_known_testset_id(db)
58
59
60@mock.patch('qakit.metrics.practitest.testsets.retrieve_testset')
61class UpdateTestsetsTestCase(unittest.TestCase):
62
63 def test_no_testsets_specified(self, mock_retrieve_testset):
64 db = mock.Mock(
65 testsets=mock.Mock(
66 find=mock.Mock(
67 return_value=TESTSETS)))
68 mock_retrieve_testset.return_value = {'n': 1}
69 testsets.update_testsets(db, 'fake-practitest-session')
70 for testset_id in [testset['id'] for testset in TESTSETS]:
71 mock_retrieve_testset.assert_any_call(
72 db, 'fake-practitest-session', testset_id)
73
74 def test_testsets_specified(self, mock_retrieve_testset):
75 mock_retrieve_testset.return_value = {'n': 1}
76 testsets_ = [testset['id'] for testset in TESTSETS]
77 testsets.update_testsets('fake-db', 'fake-practitest-session', testsets_)
78 for testset_id in [testset['id'] for testset in TESTSETS]:
79 mock_retrieve_testset.assert_any_call(
80 'fake-db', 'fake-practitest-session', testset_id)
081
=== added file 'qakit/metrics/tests/test_test_execution.py'
--- qakit/metrics/tests/test_test_execution.py 1970-01-01 00:00:00 +0000
+++ qakit/metrics/tests/test_test_execution.py 2015-10-29 22:05:12 +0000
@@ -0,0 +1,99 @@
1# UESQA Metrics
2# Copyright (C) 2015 Canonical
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17import unittest
18from unittest import mock
19
20import qakit.metrics.test_execution as test_execution
21
22
23TESTSETS = [
24 {'id': 761},
25 {'id': 762},
26 {'id': 763},
27 {'id': 764},
28 {'id': 765},
29 {'id': 766},
30 {'id': 767}
31]
32
33
34@mock.patch('qakit.metrics.test_execution.update_testsets')
35@mock.patch('qakit.metrics.test_execution.update_instances')
36class UpdateTestExecutionDataTestCase(unittest.TestCase):
37
38 def test_update_both_testsets_and_instances(
39 self,
40 mock_update_instances,
41 mock_update_testsets):
42 test_execution.update_test_execution_data(
43 'fake-db', 'fake-practitest-session', 'fake-testset-ids')
44 for f in mock_update_instances, mock_update_testsets:
45 f.assert_called_with(
46 'fake-db',
47 'fake-practitest-session',
48 testset_ids='fake-testset-ids')
49
50
51@mock.patch('qakit.metrics.practitest.testsets.update_testsets')
52class UpdateTestSetsTestCase(unittest.TestCase):
53
54 def test_testset_ids_specified(self, mock_update_testsets):
55 test_execution.update_testsets(
56 'fake-db', 'fake-practitest-session', 'fake-testset-ids')
57 mock_update_testsets.assert_called_with(
58 'fake-db',
59 'fake-practitest-session',
60 testset_ids='fake-testset-ids')
61
62 @mock.patch('qakit.metrics.practitest.testsets.discover_new_testsets')
63 def test_no_testset_ids_specified(
64 self,
65 mock_discover_new_testsets,
66 mock_update_testsets):
67 db = mock.Mock(
68 testsets=mock.Mock(
69 find=mock.Mock(return_value=TESTSETS)))
70 test_execution.update_testsets(
71 db, 'fake-practitest-session', testset_ids=None)
72 mock_discover_new_testsets.assert_called_with(
73 db, 'fake-practitest-session')
74 testset_ids = [testset['id'] for testset in TESTSETS]
75 mock_update_testsets.assert_called_with(
76 db, 'fake-practitest-session', testset_ids=testset_ids)
77
78
79@mock.patch('qakit.metrics.practitest.instances.update_testsets_instances')
80class UpdateInstancesTestCase(unittest.TestCase):
81
82 def test_testset_ids_specified(self, mock_update_testsets_instances):
83 test_execution.update_instances(
84 'fake-db', 'fake-practitest-session', 'fake-testset-ids')
85 mock_update_testsets_instances.assert_called_with(
86 'fake-db',
87 'fake-practitest-session',
88 testset_ids='fake-testset-ids')
89
90 def test_no_testset_ids_specified(self, mock_update_testsets_instances):
91 db = mock.Mock(
92 testsets=mock.Mock(
93 find=mock.Mock(return_value=TESTSETS)))
94 test_execution.update_instances(db, 'fake-practitest-session', None)
95 testset_ids = [testset['id'] for testset in TESTSETS]
96 mock_update_testsets_instances.assert_called_with(
97 db,
98 'fake-practitest-session',
99 testset_ids=testset_ids)
0100
=== modified file 'qakit/practitest/practitest.py'
--- qakit/practitest/practitest.py 2015-09-03 13:32:10 +0000
+++ qakit/practitest/practitest.py 2015-10-29 22:05:12 +0000
@@ -15,6 +15,7 @@
15# You should have received a copy of the GNU General Public License15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.16# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
18import datetime
18import json19import json
19import logging20import logging
20import requests21import requests
@@ -28,6 +29,35 @@
28PROD_PRACTITEST = 'https://prod.practitest.com/api'29PROD_PRACTITEST = 'https://prod.practitest.com/api'
2930
3031
32def practitest_time_to_datetime(practitest_time):
33 """Convert from PractiTest time to Python datetime.
34
35 PractiTest times look like '13-Apr-2015 22:02'.
36
37 """
38 return datetime.datetime.strptime(
39 practitest_time, '%d-%b-%Y %H:%M')
40
41
42def append_last_run_datetime(practitest_dict):
43 """Append a Python datetime for 'last_run' to a given PractiTest dict.
44
45 PractiTest gives us its datetimes as strings; translating to a Python
46 datetime makes querying, sorting, etc. much easier.
47
48 :param practitest_dict: a PractiTest JSON dict such as an instance or a
49 testset
50
51 """
52 try:
53 practitest_dict['last_run_datetime'] = practitest_time_to_datetime(
54 practitest_dict['last_run']['value'])
55 except ValueError:
56 # no last_run, no harm done
57 pass
58 return practitest_dict
59
60
31class PractitestSession:61class PractitestSession:
3262
33 def __init__(self, project_id, api_key, api_secret_key, user_email=None):63 def __init__(self, project_id, api_key, api_secret_key, user_email=None):
@@ -54,12 +84,14 @@
54 def _get(self, url, params={}):84 def _get(self, url, params={}):
55 """Get the given url, retrying on failure."""85 """Get the given url, retrying on failure."""
56 params['project_id'] = self.project_id86 params['project_id'] = self.project_id
57 return requests.get(87 response = requests.get(
58 url=url,88 url=url,
59 headers=auth.compose_headers(89 headers=auth.compose_headers(
60 self.api_key,90 self.api_key,
61 self.api_secret_key),91 self.api_secret_key),
62 data=json.dumps(params))92 data=json.dumps(params))
93 response.raise_for_status()
94 return response
6395
6496
65 def _get_all(self, url, params={}):97 def _get_all(self, url, params={}):
@@ -69,6 +101,8 @@
69 return int(pagination['total_entities']) - total_read > 0101 return int(pagination['total_entities']) - total_read > 0
70102
71 json_data = []103 json_data = []
104 response = None
105 params['page'] = 1
72 while True:106 while True:
73 response = self._get(url, params)107 response = self._get(url, params)
74 if not response.ok:108 if not response.ok:
@@ -79,6 +113,7 @@
79 params['page'] = str(pagination['page'] + 1)113 params['page'] = str(pagination['page'] + 1)
80 else:114 else:
81 break115 break
116 response.raise_for_status()
82 return json_data117 return json_data
83118
84119
@@ -159,7 +194,14 @@
159194
160 """195 """
161 url = PROD_PRACTITEST + '/sets/{}.json'.format(id)196 url = PROD_PRACTITEST + '/sets/{}.json'.format(id)
162 return self._get(url).json()197 try:
198 return self._get(url).json()
199 except requests.exceptions.HTTPError as e:
200 if "TestSet was not found" in e.response.text:
201 raise ValueError("TestSet {} not found!".format(id))
202 else:
203 raise
204
163205
164 def create_testset(self, name, priority, device, 206 def create_testset(self, name, priority, device,
165 level, release, build, buildinfo):207 level, release, build, buildinfo):
@@ -187,7 +229,13 @@
187229
188 """230 """
189 url = PROD_PRACTITEST + '/sets/{}/instances.json'.format(id)231 url = PROD_PRACTITEST + '/sets/{}/instances.json'.format(id)
190 return self._get_all(url)232 try:
233 return self._get_all(url)
234 except requests.exceptions.HTTPError as e:
235 if "TestSet was not found" in e.response.text:
236 raise ValueError("TestSet {} not found!".format(id))
237 else:
238 raise
191239
192 def create_instances(self, set_id, test_ids):240 def create_instances(self, set_id, test_ids):
193 """241 """

Subscribers

People subscribed via source and target branches

to all changes: