Merge lp:~tsimonq2/ubuntu-qa-website/python-library-merge into lp:ubuntu-qa-website

Proposed by Simon Quigley on 2016-03-01
Status: Merged
Merged at revision: 427
Proposed branch: lp:~tsimonq2/ubuntu-qa-website/python-library-merge
Merge into: lp:ubuntu-qa-website
Diff against target: 1271 lines (+1210/-0)
12 files modified
python-library/qatracker.py (+535/-0)
python-library/tests/run (+35/-0)
python-library/tests/setup.sql (+4/-0)
python-library/tests/test_bug.py (+17/-0)
python-library/tests/test_build.py (+98/-0)
python-library/tests/test_milestone.py (+56/-0)
python-library/tests/test_product.py (+51/-0)
python-library/tests/test_rebuilds.py (+69/-0)
python-library/tests/test_result.py (+173/-0)
python-library/tests/test_rpc.py (+27/-0)
python-library/tests/test_series.py (+77/-0)
python-library/tests/test_testcase.py (+68/-0)
To merge this branch: bzr merge lp:~tsimonq2/ubuntu-qa-website/python-library-merge
Reviewer Review Type Date Requested Status
Nicholas Skaggs 2016-03-01 Pending
Review via email: mp+287711@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'python-library'
2=== added file 'python-library/qatracker.py'
3--- python-library/qatracker.py 1970-01-01 00:00:00 +0000
4+++ python-library/qatracker.py 2016-03-01 21:32:25 +0000
5@@ -0,0 +1,535 @@
6+#!/usr/bin/python3
7+# -*- coding: utf-8 -*-
8+
9+# Copyright (C) 2011, 2012 Canonical Ltd.
10+# Author: St├ęphane Graber <stgraber@ubuntu.com>
11+
12+# This library is free software; you can redistribute it and/or
13+# modify it under the terms of the GNU Lesser General Public
14+# License as published by the Free Software Foundation; either
15+# version 2.1 of the License, or (at your option) any later version.
16+
17+# This library is distributed in the hope that it will be useful,
18+# but WITHOUT ANY WARRANTY; without even the implied warranty of
19+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20+# Lesser General Public License for more details.
21+
22+# You should have received a copy of the GNU Lesser General Public
23+# License along with this library; if not, write to the Free Software
24+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
25+# USA
26+
27+try:
28+ import xmlrpc.client as xmlrpclib
29+except ImportError:
30+ import xmlrpclib
31+
32+import base64
33+from datetime import datetime
34+
35+# Taken from qatracker/qatracker.modules (PHP code)
36+# cat qatracker.module | grep " = array" | sed -e 's/^\$//g' \
37+# -e 's/array(/[/g' -e 's/);/]/g' -e "s/t('/\"/g" -e "s/')/\"/g"
38+### AUTO-GENERATED ->
39+qatracker_build_milestone_status = ["Active", "Re-building", "Disabled",
40+ "Superseded", "Ready"]
41+qatracker_milestone_notify = ["No", "Yes"]
42+qatracker_milestone_autofill = ["No", "Yes"]
43+qatracker_milestone_status = ["Testing", "Released", "Archived"]
44+qatracker_milestone_series_status = ["Active", "Disabled"]
45+qatracker_milestone_series_manifest_status = ["Active", "Disabled"]
46+qatracker_product_status = ["Active", "Disabled"]
47+qatracker_product_type = ["iso", "package", "hardware"]
48+qatracker_product_download_type = ["HTTP", "RSYNC", "ZSYNC",
49+ "GPG signature", "MD5 checksum", "Comment",
50+ "Torrent"]
51+qatracker_testsuite_testcase_status = ["Mandatory", "Disabled", "Run-once",
52+ "Optional"]
53+qatracker_result_result = ["Failed", "Passed", "In progress"]
54+qatracker_result_status = ["Active", "Disabled"]
55+qatracker_rebuild_status = ["Requested", "Queued", "Building", "Built",
56+ "Published", "Canceled"]
57+### <- AUTO-GENERATED
58+
59+
60+class QATrackerRPCObject():
61+ """Base class for objects received over XML-RPC"""
62+
63+ CONVERT_BOOL = []
64+ CONVERT_DATE = []
65+ CONVERT_INT = []
66+
67+ def __init__(self, tracker, rpc_dict):
68+ # Convert the dict we get from the API into an object
69+
70+ for key in rpc_dict:
71+ if key in self.CONVERT_INT:
72+ try:
73+ setattr(self, key, int(rpc_dict[key]))
74+ except ValueError:
75+ setattr(self, key, None)
76+ elif key in self.CONVERT_BOOL:
77+ setattr(self, key, rpc_dict[key] == "true")
78+ elif key in self.CONVERT_DATE:
79+ try:
80+ setattr(self, key, datetime.strptime(rpc_dict[key],
81+ '%Y-%m-%d %H:%M:%S'))
82+ except ValueError:
83+ setattr(self, key, None)
84+ else:
85+ setattr(self, key, str(rpc_dict[key]))
86+
87+ self.tracker = tracker
88+
89+ def __repr__(self):
90+ return "%s: %s" % (self.__class__.__name__, self.title)
91+
92+
93+class QATrackerBug(QATrackerRPCObject):
94+ """A bug entry"""
95+
96+ CONVERT_INT = ['bugnumber', 'count']
97+ CONVERT_DATE = ['earliest_report', 'latest_report']
98+
99+ def __repr__(self):
100+ return "%s: %s" % (self.__class__.__name__, self.bugnumber)
101+
102+
103+class QATrackerBuild(QATrackerRPCObject):
104+ """A build entry"""
105+
106+ CONVERT_INT = ['id', 'productid', 'userid', 'status']
107+ CONVERT_DATE = ['date']
108+
109+ def __repr__(self):
110+ return "%s: %s" % (self.__class__.__name__, self.id)
111+
112+ def add_result(self, testcase, result, comment='', hardware='', bugs={}):
113+ """Add a result to the build"""
114+
115+ if (self.tracker.access not in ("user", "admin") and
116+ self.tracker.access is not None):
117+ raise Exception("Access denied, you need 'user' but are '%s'" %
118+ self.tracker.access)
119+
120+ build_testcase = None
121+
122+ # FIXME: Supporting 'str' containing the testcase name would be nice
123+ if isinstance(testcase, QATrackerTestcase):
124+ build_testcase = testcase.id
125+ elif isinstance(testcase, int):
126+ build_testcase = testcase
127+
128+ if not build_testcase:
129+ raise IndexError("Couldn't find testcase: %s" % (testcase,))
130+
131+ if isinstance(result, list):
132+ raise TypeError("result must be a string or an integer")
133+
134+ build_result = self.tracker._get_valid_id_list(qatracker_result_result,
135+ result)
136+
137+ if not isinstance(bugs, dict):
138+ raise TypeError("bugs must be a dict")
139+
140+ for bug in bugs:
141+ if not isinstance(bug, int) or bug <= 0:
142+ raise ValueError("A bugnumber must be a number >= 0")
143+
144+ if not isinstance(bugs[bug], int) or bugs[bug] not in (0, 1):
145+ raise ValueError("A bugimportance must be in (0,1)")
146+
147+ resultid = int(self.tracker.tracker.results.add(self.id,
148+ build_testcase,
149+ build_result[0],
150+ str(comment),
151+ str(hardware),
152+ bugs))
153+ if resultid == -1:
154+ raise Exception("Couldn't post your result.")
155+
156+ new_result = None
157+ for entry in self.get_results(build_testcase, 0):
158+ if entry.id == resultid:
159+ new_result = entry
160+ break
161+
162+ return new_result
163+
164+ def get_results(self, testcase, status=qatracker_result_status):
165+ """Get a list of results for the given build and testcase"""
166+
167+ build_testcase = None
168+
169+ # FIXME: Supporting 'str' containing the testcase name would be nice
170+ if isinstance(testcase, QATrackerTestcase):
171+ build_testcase = testcase.id
172+ elif isinstance(testcase, int):
173+ build_testcase = testcase
174+
175+ if not build_testcase:
176+ raise IndexError("Couldn't find testcase: %s" % (testcase,))
177+
178+ record_filter = self.tracker._get_valid_id_list(
179+ qatracker_result_status,
180+ status)
181+
182+ if len(record_filter) == 0:
183+ return []
184+
185+ results = []
186+ for entry in self.tracker.tracker.results.get_list(
187+ self.id, build_testcase, list(record_filter)):
188+ results.append(QATrackerResult(self.tracker, entry))
189+
190+ return results
191+
192+
193+class QATrackerMilestone(QATrackerRPCObject):
194+ """A milestone entry"""
195+
196+ CONVERT_INT = ['id', 'status', 'series']
197+ CONVERT_BOOL = ['notify']
198+
199+ def get_bugs(self):
200+ """Returns a list of all bugs linked to this milestone"""
201+
202+ bugs = []
203+ for entry in self.tracker.tracker.bugs.get_list(self.id):
204+ bugs.append(QATrackerBug(self.tracker, entry))
205+
206+ return bugs
207+
208+ def add_build(self, product, version, note="", notify=True):
209+ """Add a build to the milestone"""
210+
211+ if self.status != 0:
212+ raise TypeError("Only active milestones are accepted")
213+
214+ if self.tracker.access != "admin" and self.tracker.access is not None:
215+ raise Exception("Access denied, you need 'admin' but are '%s'" %
216+ self.tracker.access)
217+
218+ if not isinstance(notify, bool):
219+ raise TypeError("notify must be a boolean")
220+
221+ build_product = None
222+
223+ if isinstance(product, QATrackerProduct):
224+ build_product = product
225+ else:
226+ valid_products = self.tracker.get_products(0)
227+
228+ for entry in valid_products:
229+ if (entry.title.lower() == str(product).lower() or
230+ entry.id == product):
231+ build_product = entry
232+ break
233+
234+ if not build_product:
235+ raise IndexError("Couldn't find product: %s" % product)
236+
237+ if build_product.status != 0:
238+ raise TypeError("Only active products are accepted")
239+
240+ self.tracker.tracker.builds.add(build_product.id, self.id,
241+ str(version), str(note), notify)
242+
243+ new_build = None
244+ for entry in self.get_builds(0):
245+ if (entry.productid == build_product.id
246+ and entry.version == str(version)):
247+ new_build = entry
248+ break
249+
250+ return new_build
251+
252+ def get_builds(self, status=qatracker_build_milestone_status):
253+ """Get a list of builds for the milestone"""
254+
255+ record_filter = self.tracker._get_valid_id_list(
256+ qatracker_build_milestone_status, status)
257+
258+ if len(record_filter) == 0:
259+ return []
260+
261+ builds = []
262+ for entry in self.tracker.tracker.builds.get_list(self.id,
263+ list(record_filter)):
264+ builds.append(QATrackerBuild(self.tracker, entry))
265+
266+ return builds
267+
268+
269+class QATrackerProduct(QATrackerRPCObject):
270+ CONVERT_INT = ['id', 'type', 'status']
271+
272+ def get_testcases(self, series,
273+ status=qatracker_testsuite_testcase_status):
274+ """Get a list of testcases associated with the product"""
275+
276+ record_filter = self.tracker._get_valid_id_list(
277+ qatracker_testsuite_testcase_status, status)
278+
279+ if len(record_filter) == 0:
280+ return []
281+
282+ if isinstance(series, QATrackerMilestone):
283+ seriesid = series.series
284+ elif isinstance(series, int):
285+ seriesid = series
286+ else:
287+ raise TypeError("series needs to be a valid QATrackerMilestone"
288+ " instance or an integer")
289+
290+ testcases = []
291+ for entry in self.tracker.tracker.testcases.get_list(
292+ self.id, seriesid, list(record_filter)):
293+ testcases.append(QATrackerTestcase(self.tracker, entry))
294+
295+ return testcases
296+
297+
298+class QATrackerRebuild(QATrackerRPCObject):
299+ CONVERT_INT = ['id', 'seriesid', 'productid', 'milestoneid', 'requestedby',
300+ 'changedby', 'status']
301+ CONVERT_DATE = ['requestedat', 'changedat']
302+
303+ def __repr__(self):
304+ return "%s: %s" % (self.__class__.__name__, self.id)
305+
306+ def save(self):
307+ """Save any change that happened on this entry.
308+ NOTE: At the moment only supports the status field."""
309+
310+ if (self.tracker.access != "admin" and
311+ self.tracker.access is not None):
312+ raise Exception("Access denied, you need 'admin' but are '%s'" %
313+ self.tracker.access)
314+
315+ retval = self.tracker.tracker.rebuilds.update_status(self.id,
316+ self.status)
317+ if retval is not True:
318+ raise Exception("Failed to update rebuild")
319+
320+ return retval
321+
322+
323+class QATrackerResult(QATrackerRPCObject):
324+ CONVERT_INT = ['id', 'reporterid', 'revisionid', 'result', 'changedby',
325+ 'status']
326+ CONVERT_DATE = ['date', 'lastchange']
327+ __deleted = False
328+
329+ def __repr__(self):
330+ return "%s: %s" % (self.__class__.__name__, self.id)
331+
332+ def delete(self):
333+ """Remove the result from the tracker"""
334+
335+ if (self.tracker.access not in ("user", "admin") and
336+ self.tracker.access is not None):
337+ raise Exception("Access denied, you need 'user' but are '%s'" %
338+ self.tracker.access)
339+
340+ if self.__deleted:
341+ raise IndexError("Result has already been removed")
342+
343+ retval = self.tracker.tracker.results.delete(self.id)
344+ if retval is not True:
345+ raise Exception("Failed to remove result")
346+
347+ self.status = 1
348+ self.__deleted = True
349+
350+ def save(self):
351+ """Save any change that happened on this entry"""
352+
353+ if (self.tracker.access not in ("user", "admin") and
354+ self.tracker.access is not None):
355+ raise Exception("Access denied, you need 'user' but are '%s'" %
356+ self.tracker.access)
357+
358+ if self.__deleted:
359+ raise IndexError("Result no longer exists")
360+
361+ retval = self.tracker.tracker.results.update(self.id, self.result,
362+ self.comment,
363+ self.hardware,
364+ self.bugs)
365+ if retval is not True:
366+ raise Exception("Failed to update result")
367+
368+ return retval
369+
370+
371+class QATrackerSeries(QATrackerRPCObject):
372+ CONVERT_INT = ['id', 'status']
373+
374+ def get_manifest(self, status=qatracker_milestone_series_manifest_status):
375+ """Get a list of products in the series' manifest"""
376+
377+ record_filter = self.tracker._get_valid_id_list(
378+ qatracker_milestone_series_manifest_status, status)
379+
380+ if len(record_filter) == 0:
381+ return []
382+
383+ manifest_entries = []
384+ for entry in self.tracker.tracker.series.get_manifest(
385+ self.id, list(record_filter)):
386+ manifest_entries.append(QATrackerSeriesManifest(
387+ self.tracker, entry))
388+
389+ return manifest_entries
390+
391+
392+class QATrackerSeriesManifest(QATrackerRPCObject):
393+ CONVERT_INT = ['id', 'productid', 'status']
394+
395+ def __repr__(self):
396+ return "%s: %s" % (self.__class__.__name__, self.product_title)
397+
398+
399+class QATrackerTestcase(QATrackerRPCObject):
400+ CONVERT_INT = ['id', 'status', 'weight', 'suite']
401+
402+
403+class QATracker():
404+ def __init__(self, url, username=None, password=None):
405+ class AuthTransport(xmlrpclib.Transport):
406+ def set_auth(self, auth):
407+ self.auth = auth
408+
409+ def get_host_info(self, host):
410+ host, extra_headers, x509 = \
411+ xmlrpclib.Transport.get_host_info(self, host)
412+ if extra_headers is None:
413+ extra_headers = []
414+ extra_headers.append(('Authorization', 'Basic %s' % auth))
415+ return host, extra_headers, x509
416+
417+ if username and password:
418+ try:
419+ auth = str(base64.b64encode(
420+ bytes('%s:%s' % (username, password), 'utf-8')),
421+ 'utf-8')
422+ except TypeError:
423+ auth = base64.b64encode('%s:%s' % (username, password))
424+
425+ transport = AuthTransport()
426+ transport.set_auth(auth)
427+ drupal = xmlrpclib.ServerProxy(url, transport=transport)
428+ else:
429+ drupal = xmlrpclib.ServerProxy(url)
430+
431+ # Call listMethods() so if something is wrong we know it immediately
432+ drupal.system.listMethods()
433+
434+ # Get our current access
435+ self.access = drupal.qatracker.get_access()
436+
437+ self.tracker = drupal.qatracker
438+
439+ def _get_valid_id_list(self, status_list, status):
440+ """ Get a list of valid keys and a list or just a single
441+ entry of input to check against the list of valid keys.
442+ The function looks for valid indexes and content, doing
443+ case insensitive checking for strings and returns a list
444+ of indexes for the list of valid keys. """
445+
446+ def process(status_list, status):
447+ valid_status = [entry.lower() for entry in status_list]
448+
449+ if isinstance(status, int):
450+ if status < 0 or status >= len(valid_status):
451+ raise IndexError("Invalid status: %s" % status)
452+ return int(status)
453+
454+ if isinstance(status, str):
455+ status = status.lower()
456+ if status not in valid_status:
457+ raise IndexError("Invalid status: %s" % status)
458+ return valid_status.index(status)
459+
460+ raise TypeError("Invalid status type: %s (expected str or int)" %
461+ type(status))
462+
463+ record_filter = set()
464+
465+ if isinstance(status, list):
466+ for entry in status:
467+ record_filter.add(process(status_list, entry))
468+ else:
469+ record_filter.add(process(status_list, status))
470+
471+ return list(record_filter)
472+
473+ def get_bugs(self):
474+ """Get a list of all bugs reported on the site"""
475+
476+ bugs = []
477+ for entry in self.tracker.bugs.get_list(0):
478+ bugs.append(QATrackerBug(self, entry))
479+
480+ return bugs
481+
482+ def get_milestones(self, status=qatracker_milestone_status):
483+ """Get a list of all milestones"""
484+
485+ record_filter = self._get_valid_id_list(qatracker_milestone_status,
486+ status)
487+
488+ if len(record_filter) == 0:
489+ return []
490+
491+ milestones = []
492+ for entry in self.tracker.milestones.get_list(list(record_filter)):
493+ milestones.append(QATrackerMilestone(self, entry))
494+
495+ return milestones
496+
497+ def get_products(self, status=qatracker_product_status):
498+ """Get a list of all products"""
499+
500+ record_filter = self._get_valid_id_list(qatracker_product_status,
501+ status)
502+
503+ if len(record_filter) == 0:
504+ return []
505+
506+ products = []
507+ for entry in self.tracker.products.get_list(list(record_filter)):
508+ products.append(QATrackerProduct(self, entry))
509+
510+ return products
511+
512+ def get_rebuilds(self, status=qatracker_rebuild_status):
513+ """Get a list of all rebuilds"""
514+
515+ record_filter = self._get_valid_id_list(
516+ qatracker_rebuild_status, status)
517+
518+ if len(record_filter) == 0:
519+ return []
520+
521+ rebuilds = []
522+ for entry in self.tracker.rebuilds.get_list(list(record_filter)):
523+ rebuilds.append(QATrackerRebuild(self, entry))
524+
525+ return rebuilds
526+
527+ def get_series(self, status=qatracker_milestone_series_status):
528+ """Get a list of all series"""
529+
530+ record_filter = self._get_valid_id_list(
531+ qatracker_milestone_series_status, status)
532+
533+ if len(record_filter) == 0:
534+ return []
535+
536+ series = []
537+ for entry in self.tracker.series.get_list(list(record_filter)):
538+ series.append(QATrackerSeries(self, entry))
539+
540+ return series
541
542=== added directory 'python-library/tests'
543=== added file 'python-library/tests/run'
544--- python-library/tests/run 1970-01-01 00:00:00 +0000
545+++ python-library/tests/run 2016-03-01 21:32:25 +0000
546@@ -0,0 +1,35 @@
547+#!/usr/bin/python3
548+import unittest
549+import os
550+import re
551+import shutil
552+import sys
553+
554+coverage = True
555+try:
556+ from coverage import coverage
557+ cov = coverage()
558+ cov.start()
559+except ImportError:
560+ print("No coverage report, make sure python-coverage is installed")
561+ coverage = False
562+
563+sys.path.insert(0, '.')
564+
565+if len(sys.argv) > 1:
566+ test_filter = sys.argv[1]
567+else:
568+ test_filter = ''
569+
570+tests = [t[:-3] for t in os.listdir('tests')
571+ if t.startswith('test_') and t.endswith('.py') and
572+ re.search(test_filter, t)]
573+tests.sort()
574+suite = unittest.TestLoader().loadTestsFromNames(tests)
575+res = unittest.TextTestRunner(verbosity=2).run(suite)
576+
577+if coverage:
578+ if os.path.exists('tests/coverage'):
579+ shutil.rmtree('tests/coverage')
580+ cov.stop()
581+ cov.html_report(include=["qatracker.py"], directory='tests/coverage')
582
583=== added file 'python-library/tests/setup.sql'
584--- python-library/tests/setup.sql 1970-01-01 00:00:00 +0000
585+++ python-library/tests/setup.sql 2016-03-01 21:32:25 +0000
586@@ -0,0 +1,4 @@
587+INSERT INTO users (uid, name, data, status) VALUES (99999, 'user', 'a:1:{s:17:"qatracker_api_key";s:4:"user";}', 1);
588+INSERT INTO users (uid, name, data, status) VALUES (99998, 'admin', 'a:1:{s:17:"qatracker_api_key";s:5:"admin";}', 1);
589+INSERT INTO users_roles (uid, rid) VALUES (99999, 2);
590+INSERT INTO users_roles (uid, rid) VALUES (99998, 4);
591
592=== added file 'python-library/tests/test_bug.py'
593--- python-library/tests/test_bug.py 1970-01-01 00:00:00 +0000
594+++ python-library/tests/test_bug.py 2016-03-01 21:32:25 +0000
595@@ -0,0 +1,17 @@
596+import unittest
597+import qatracker
598+import datetime
599+
600+URL = 'http://iso.qa.dev.stgraber.org/xmlrpc.php'
601+
602+
603+class BugTests(unittest.TestCase):
604+ def setUp(self):
605+ self.instance = qatracker.QATracker(URL)
606+
607+ def test_all_bugs(self):
608+ bugs = self.instance.get_bugs()
609+ if bugs:
610+ self.assertIsInstance(bugs[0].bugnumber, int)
611+ self.assertIsInstance(bugs[0].earliest_report, datetime.datetime)
612+ self.assertTrue(str(bugs[0]).startswith('QATrackerBug: '))
613
614=== added file 'python-library/tests/test_build.py'
615--- python-library/tests/test_build.py 1970-01-01 00:00:00 +0000
616+++ python-library/tests/test_build.py 2016-03-01 21:32:25 +0000
617@@ -0,0 +1,98 @@
618+import unittest
619+import qatracker
620+
621+URL = 'http://iso.qa.dev.stgraber.org/xmlrpc.php'
622+
623+
624+class BuildTests(unittest.TestCase):
625+ def setUp(self):
626+ self.instance = qatracker.QATracker(URL, 'admin', 'admin')
627+ self.milestone = self.instance.get_milestones('testing')[0]
628+
629+ def test_build_lower(self):
630+ self.assertIsInstance(self.milestone.get_builds('active'), list)
631+
632+ def test_build_mixed(self):
633+ self.assertIsInstance(self.milestone.get_builds('DisAblEd'), list)
634+
635+ def test_build_upper(self):
636+ self.assertIsInstance(self.milestone.get_builds('RE-BUILDING'), list)
637+
638+ def test_build_id(self):
639+ self.assertIsInstance(self.milestone.get_builds(3), list)
640+
641+ def test_build_list(self):
642+ self.assertIsInstance(self.milestone.get_builds(['Re-Building', 0,
643+ 'DISABLED',
644+ 'SuperSeded']), list)
645+
646+ def test_build_empty_list(self):
647+ self.assertEquals(self.milestone.get_builds([]), [])
648+
649+ def test_build_all(self):
650+ self.assertIsInstance(self.milestone.get_builds(), list)
651+
652+ def test_build_type(self):
653+ build = self.milestone.get_builds(0)[0]
654+ self.assertIsInstance(build, qatracker.QATrackerBuild)
655+ self.assertIsInstance(build.status, int)
656+ self.assertIsInstance(build.version, str)
657+
658+ def test_build_repr(self):
659+ build = self.milestone.get_builds(0)[0]
660+ self.assertTrue(str(build).startswith('QATrackerBuild: '))
661+
662+ def test_build_add_by_name(self):
663+ product = self.instance.get_products(0)[0]
664+ self.milestone.add_build(product.title, '1234', '', False)
665+
666+ def test_build_add_by_id(self):
667+ product = self.instance.get_products(0)[0]
668+ build = self.milestone.add_build(product.id, '1234', '', False)
669+ self.assertEquals(build.productid, product.id)
670+ self.assertEquals(build.version, '1234')
671+ self.assertEquals(build.note, '')
672+
673+ def test_build_invalid_add_disabled_product_by_id(self):
674+ product = self.instance.get_products(1)[0]
675+ self.assertRaises(IndexError, self.milestone.add_build,
676+ product=product.id, version='1234')
677+
678+ def test_build_invalid_add_disabled_product_by_object(self):
679+ product = self.instance.get_products(1)[0]
680+ self.assertRaises(TypeError, self.milestone.add_build,
681+ product=product, version='1234')
682+
683+ def test_build_invalid_add_disabled_milestone_by_object(self):
684+ milestone = self.instance.get_milestones('archived')[0]
685+ product = self.instance.get_products(1)[0]
686+ self.assertRaises(TypeError, milestone.add_build,
687+ product=product, version='1234')
688+
689+ def test_build_invalid_add_invalid_by_name(self):
690+ self.assertRaises(IndexError, self.milestone.add_build,
691+ product='bmaifno', version='1234')
692+
693+ def test_build_invalid_add_invalid_by_id(self):
694+ self.assertRaises(IndexError, self.milestone.add_build,
695+ product=-1, version='1234')
696+
697+ def test_build_invalid_add_invalid_notify(self):
698+ product = self.instance.get_products(0)[0]
699+ self.assertRaises(TypeError, self.milestone.add_build,
700+ product=product.id, version='1234', notify=object())
701+
702+ def test_build_invalid_add_invalid_no_access(self):
703+ instance = qatracker.QATracker(URL, 'user', 'user')
704+ milestone = instance.get_milestones('testing')[0]
705+ self.assertRaises(Exception, milestone.add_build, product=0,
706+ version='1234')
707+
708+ def test_build_invalid_id(self):
709+ self.assertRaises(IndexError, self.milestone.get_builds, (-1))
710+
711+ def test_build_invalid_string(self):
712+ self.assertRaises(IndexError, self.milestone.get_builds, ('bla'))
713+
714+ def test_build_invalid_type(self):
715+ self.assertRaises(TypeError, self.milestone.get_builds, (object()))
716
717=== added file 'python-library/tests/test_milestone.py'
718--- python-library/tests/test_milestone.py 1970-01-01 00:00:00 +0000
719+++ python-library/tests/test_milestone.py 2016-03-01 21:32:25 +0000
720@@ -0,0 +1,56 @@
721+import unittest
722+import qatracker
723+
724+URL = 'http://iso.qa.dev.stgraber.org/xmlrpc.php'
725+
726+
727+class MilestoneTests(unittest.TestCase):
728+ def setUp(self):
729+ self.instance = qatracker.QATracker(URL)
730+
731+ def test_milestone_lower(self):
732+ milestones = self.instance.get_milestones('testing')
733+ self.assertIsInstance(milestones, list)
734+
735+ def test_milestone_mixed(self):
736+ milestones = self.instance.get_milestones('RelEaSed')
737+ self.assertIsInstance(milestones, list)
738+
739+ def test_milestone_upper(self):
740+ milestones = self.instance.get_milestones('ARCHIVED')
741+ self.assertIsInstance(milestones, list)
742+
743+ def test_milestone_id(self):
744+ milestones = self.instance.get_milestones(2)
745+ self.assertIsInstance(milestones, list)
746+
747+ def test_milestone_list(self):
748+ milestones = self.instance.get_milestones(['testing', 2, 'archIved'])
749+ self.assertIsInstance(milestones, list)
750+
751+ def test_milestone_empty_list(self):
752+ self.assertEquals(self.instance.get_milestones([]), [])
753+
754+ def test_milestone_all(self):
755+ self.assertIsInstance(self.instance.get_milestones(), list)
756+
757+ def test_milestone_type(self):
758+ milestone = self.instance.get_milestones(2)[10]
759+ self.assertIsInstance(milestone, qatracker.QATrackerMilestone)
760+ self.assertIsInstance(milestone.notify, bool)
761+ self.assertIsInstance(milestone.status, int)
762+ self.assertIsInstance(milestone.title, str)
763+ self.assertIsInstance(milestone.get_bugs(), list)
764+
765+ def test_milestone_repr(self):
766+ milestone = self.instance.get_milestones(2)[0]
767+ self.assertTrue(str(milestone).startswith('QATrackerMilestone: '))
768+
769+ def test_milestone_invalid_id(self):
770+ self.assertRaises(IndexError, self.instance.get_milestones, (-1))
771+
772+ def test_milestone_invalid_string(self):
773+ self.assertRaises(IndexError, self.instance.get_milestones, ('bla'))
774+
775+ def test_milestone_invalid_type(self):
776+ self.assertRaises(TypeError, self.instance.get_milestones, (object()))
777
778=== added file 'python-library/tests/test_product.py'
779--- python-library/tests/test_product.py 1970-01-01 00:00:00 +0000
780+++ python-library/tests/test_product.py 2016-03-01 21:32:25 +0000
781@@ -0,0 +1,51 @@
782+import unittest
783+import qatracker
784+
785+URL = 'http://iso.qa.dev.stgraber.org/xmlrpc.php'
786+
787+
788+class ProductTests(unittest.TestCase):
789+ def setUp(self):
790+ self.instance = qatracker.QATracker(URL)
791+
792+ def test_product_lower(self):
793+ self.assertIsInstance(self.instance.get_products('active'), list)
794+
795+ def test_product_mixed(self):
796+ self.assertIsInstance(self.instance.get_products('DisAblEd'), list)
797+
798+ def test_product_upper(self):
799+ self.assertIsInstance(self.instance.get_products('ACTIVE'), list)
800+
801+ def test_product_id(self):
802+ self.assertIsInstance(self.instance.get_products(1), list)
803+
804+ def test_product_list(self):
805+ self.assertIsInstance(self.instance.get_products(['ActivE', 1,
806+ 'DISABLED',
807+ 'disabled']), list)
808+
809+ def test_product_empty_list(self):
810+ self.assertEquals(self.instance.get_products([]), [])
811+
812+ def test_product_all(self):
813+ self.assertIsInstance(self.instance.get_products(), list)
814+
815+ def test_product_type(self):
816+ product = self.instance.get_products(0)[0]
817+ self.assertIsInstance(product, qatracker.QATrackerProduct)
818+ self.assertIsInstance(product.status, int)
819+ self.assertIsInstance(product.title, str)
820+
821+ def test_product_repr(self):
822+ product = self.instance.get_products(0)[0]
823+ self.assertTrue(str(product).startswith('QATrackerProduct: '))
824+
825+ def test_product_invalid_id(self):
826+ self.assertRaises(IndexError, self.instance.get_products, (-1))
827+
828+ def test_product_invalid_string(self):
829+ self.assertRaises(IndexError, self.instance.get_products, ('bla'))
830+
831+ def test_product_invalid_type(self):
832+ self.assertRaises(TypeError, self.instance.get_products, (object()))
833
834=== added file 'python-library/tests/test_rebuilds.py'
835--- python-library/tests/test_rebuilds.py 1970-01-01 00:00:00 +0000
836+++ python-library/tests/test_rebuilds.py 2016-03-01 21:32:25 +0000
837@@ -0,0 +1,69 @@
838+import unittest
839+import qatracker
840+from datetime import datetime
841+
842+URL = 'http://iso.qa.dev.stgraber.org/xmlrpc.php'
843+
844+
845+class ProductTests(unittest.TestCase):
846+ def setUp(self):
847+ self.instance = qatracker.QATracker(URL, 'admin', 'admin')
848+
849+ def test_rebuilds_lower(self):
850+ self.assertIsInstance(self.instance.get_rebuilds('requested'), list)
851+
852+ def test_rebuilds_mixed(self):
853+ self.assertIsInstance(self.instance.get_rebuilds('BuIlDing'), list)
854+
855+ def test_rebuilds_upper(self):
856+ self.assertIsInstance(self.instance.get_rebuilds('PUBLISHED'), list)
857+
858+ def test_rebuilds_id(self):
859+ self.assertIsInstance(self.instance.get_rebuilds(3), list)
860+
861+ def test_rebuilds_list(self):
862+ self.assertIsInstance(self.instance.get_rebuilds(['reQuested', 1,
863+ 'PUBLISHED',
864+ 'canceled']), list)
865+
866+ def test_rebuilds_empty_list(self):
867+ self.assertEquals(self.instance.get_rebuilds([]), [])
868+
869+ def test_rebuilds_all(self):
870+ self.assertIsInstance(self.instance.get_rebuilds(), list)
871+
872+ def test_rebuilds_type(self):
873+ rebuild = self.instance.get_rebuilds()[0]
874+ self.assertIsInstance(rebuild, qatracker.QATrackerRebuild)
875+ self.assertIsInstance(rebuild.id, int)
876+ self.assertIsInstance(rebuild.product_title, str)
877+ self.assertIsInstance(rebuild.requestedat, datetime)
878+
879+ def test_rebuilds_repr(self):
880+ rebuild = self.instance.get_rebuilds()[0]
881+ self.assertTrue(str(rebuild).startswith('QATrackerRebuild: '))
882+
883+ def test_rebuilds_invalid_id(self):
884+ self.assertRaises(IndexError, self.instance.get_rebuilds, -1)
885+
886+ def test_rebuilds_invalid_string(self):
887+ self.assertRaises(IndexError, self.instance.get_rebuilds, 'bla')
888+
889+ def test_rebuilds_invalid_type(self):
890+ self.assertRaises(TypeError, self.instance.get_rebuilds, object())
891+
892+ def test_rebuilds_invalid_save_no_access(self):
893+ instance = qatracker.QATracker(URL)
894+ rebuild = instance.get_rebuilds()[0]
895+ rebuild.status = 2
896+ self.assertRaises(Exception, rebuild.save)
897+
898+ def test_rebuilds_valid_save(self):
899+ rebuild = self.instance.get_rebuilds()[0]
900+ rebuild.status = 2
901+ self.assertTrue(rebuild.save())
902+
903+ def test_rebuilds_invalid_status_save(self):
904+ rebuild = self.instance.get_rebuilds()[0]
905+ rebuild.status = 99
906+ self.assertRaises(Exception, rebuild.save)
907
908=== added file 'python-library/tests/test_result.py'
909--- python-library/tests/test_result.py 1970-01-01 00:00:00 +0000
910+++ python-library/tests/test_result.py 2016-03-01 21:32:25 +0000
911@@ -0,0 +1,173 @@
912+import unittest
913+import qatracker
914+import datetime
915+
916+URL = 'http://iso.qa.dev.stgraber.org/xmlrpc.php'
917+
918+
919+class ResultTests(unittest.TestCase):
920+ def setUp(self):
921+ self.instance = qatracker.QATracker(URL, 'user', 'user')
922+ self.milestone = self.instance.get_milestones('testing')[0]
923+ self.builds = self.milestone.get_builds('active')
924+
925+ def test_result_by_object(self):
926+ results = []
927+ for product in self.instance.get_products('active'):
928+ builds = [build for build in self.builds
929+ if build.productid == product.id]
930+ if len(builds) == 0:
931+ continue
932+
933+ for testcase in product.get_testcases(self.milestone, 'mandatory'):
934+ build = builds[0]
935+ results += build.get_results(testcase)
936+ if len(results) > 0:
937+ break
938+ else:
939+ continue
940+ break
941+
942+ result = results[0]
943+ self.assertIsInstance(result, qatracker.QATrackerResult)
944+ self.assertIsInstance(result.id, int)
945+ self.assertIsInstance(result.date, datetime.datetime)
946+ self.assertIsInstance(result.comment, str)
947+ self.assertTrue(str(result).startswith('QATrackerResult: '))
948+
949+ def test_result_by_id(self):
950+ results = []
951+ for product in self.instance.get_products('active'):
952+ builds = [build for build in self.builds
953+ if build.productid == product.id]
954+ if len(builds) == 0:
955+ continue
956+
957+ for testcase in product.get_testcases(self.milestone, 'mandatory'):
958+ build = builds[0]
959+ results += build.get_results(testcase.id)
960+ if len(results) > 0:
961+ break
962+ else:
963+ continue
964+ break
965+
966+ result = results[0]
967+ self.assertIsInstance(result, qatracker.QATrackerResult)
968+ self.assertIsInstance(result.id, int)
969+ self.assertIsInstance(result.date, datetime.datetime)
970+ self.assertIsInstance(result.comment, str)
971+ self.assertTrue(str(result).startswith('QATrackerResult: '))
972+
973+ def test_result_add_by_object(self):
974+ testcase = None
975+ build = None
976+
977+ for product in self.instance.get_products('active'):
978+ builds = [build for build in self.builds
979+ if build.productid == product.id]
980+ if len(builds) == 0:
981+ continue
982+
983+ build = builds[0]
984+ testcase = product.get_testcases(self.milestone, 'mandatory')[0]
985+ break
986+
987+ result = build.add_result(testcase, 'failed')
988+ self.assertIsInstance(result, qatracker.QATrackerResult)
989+ self.assertIsInstance(result.id, int)
990+ self.assertIsInstance(result.date, datetime.datetime)
991+ self.assertIsInstance(result.comment, str)
992+ self.assertEquals(result.result, 0)
993+ self.assertTrue(str(result).startswith('QATrackerResult: '))
994+
995+ result.comment = "edited"
996+ result.save()
997+ result.delete()
998+
999+ self.assertRaises(Exception, result.save)
1000+
1001+ def test_result_add_by_id(self):
1002+ testcase = None
1003+ build = None
1004+
1005+ for product in self.instance.get_products('active'):
1006+ builds = [build for build in self.builds
1007+ if build.productid == product.id]
1008+ if len(builds) == 0:
1009+ continue
1010+
1011+ build = builds[0]
1012+ testcase = product.get_testcases(self.milestone, 'mandatory')[0]
1013+ break
1014+
1015+ result = build.add_result(testcase.id, 2)
1016+ self.assertIsInstance(result, qatracker.QATrackerResult)
1017+ self.assertIsInstance(result.id, int)
1018+ self.assertIsInstance(result.date, datetime.datetime)
1019+ self.assertIsInstance(result.comment, str)
1020+ self.assertEquals(result.result, 2)
1021+ self.assertTrue(str(result).startswith('QATrackerResult: '))
1022+
1023+ result.delete()
1024+
1025+ self.assertRaises(Exception, result.delete)
1026+
1027+ def test_result_invalid_remove(self):
1028+ result = qatracker.QATrackerResult(self.instance, {'id': '0'})
1029+ self.assertRaises(Exception, result.delete)
1030+
1031+ def test_result_invalid_remove_no_access(self):
1032+ instance = qatracker.QATracker(URL)
1033+ result = qatracker.QATrackerResult(instance, {'id': '0'})
1034+ self.assertRaises(Exception, result.delete)
1035+
1036+ def test_result_invalid_save(self):
1037+ result = qatracker.QATrackerResult(self.instance, {'id': '0',
1038+ 'comment': '',
1039+ 'result': '0',
1040+ 'hardware': '',
1041+ 'bugs': {}})
1042+ self.assertRaises(Exception, result.save)
1043+
1044+ def test_result_invalid_save_no_access(self):
1045+ instance = qatracker.QATracker(URL)
1046+ result = qatracker.QATrackerResult(instance, {'id': '0'})
1047+ self.assertRaises(Exception, result.save)
1048+
1049+ def test_result_invalid_add_invalid_testcase(self):
1050+ self.assertRaises(IndexError, self.builds[0].add_result,
1051+ testcase=object(), result='in progress')
1052+
1053+ def test_result_invalid_add_invalid_result(self):
1054+ self.assertRaises(TypeError, self.builds[0].add_result,
1055+ testcase=1, result=['in progress'])
1056+
1057+ def test_result_invalid_add_invalid_bugs(self):
1058+ self.assertRaises(TypeError, self.builds[0].add_result,
1059+ testcase=1, result='in progress', bugs="bla")
1060+
1061+ def test_result_invalid_add_invalid_bugnumber(self):
1062+ self.assertRaises(ValueError, self.builds[0].add_result,
1063+ testcase=1, result='in progress', bugs={-1: 0})
1064+
1065+ def test_result_invalid_add_invalid_bugimportance(self):
1066+ self.assertRaises(ValueError, self.builds[0].add_result,
1067+ testcase=1, result='in progress', bugs={123: 'a'})
1068+
1069+ def test_result_invalid_add_invalid_response(self):
1070+ self.assertRaises(Exception, self.builds[0].add_result,
1071+ testcase=-1, result='in progress')
1072+
1073+ def test_result_invalid_add_invalid_no_access(self):
1074+ instance = qatracker.QATracker(URL)
1075+ milestone = instance.get_milestones('testing')[0]
1076+ builds = milestone.get_builds('active')
1077+ self.assertRaises(Exception, builds[0].add_result,
1078+ testcase=10, result='in progress')
1079+
1080+ def test_result_invalid_id(self):
1081+ self.assertRaises(IndexError, self.builds[0].get_results, (object()))
1082+
1083+ def test_result_invalid_no_status(self):
1084+ self.assertEquals(self.builds[0].get_results(1234, []), [])
1085
1086=== added file 'python-library/tests/test_rpc.py'
1087--- python-library/tests/test_rpc.py 1970-01-01 00:00:00 +0000
1088+++ python-library/tests/test_rpc.py 2016-03-01 21:32:25 +0000
1089@@ -0,0 +1,27 @@
1090+import unittest
1091+import qatracker
1092+
1093+URL = 'http://iso.qa.dev.stgraber.org/xmlrpc.php'
1094+
1095+try:
1096+ import xmlrpc.client as xmlrpclib
1097+except ImportError:
1098+ import xmlrpclib
1099+
1100+
1101+class RPCTests(unittest.TestCase):
1102+ def test_rpc_anonymous(self):
1103+ tracker = qatracker.QATracker(URL)
1104+ self.assertEquals(tracker.access, 'public')
1105+
1106+ def test_rpc_authenticated_user(self):
1107+ tracker = qatracker.QATracker(URL, 'user', 'user')
1108+ self.assertEquals(tracker.access, 'user')
1109+
1110+ def test_rpc_authenticated_admin(self):
1111+ tracker = qatracker.QATracker(URL, 'admin', 'admin')
1112+ self.assertEquals(tracker.access, 'admin')
1113+
1114+ def test_rpc_invalid_target(self):
1115+ self.assertRaises(xmlrpclib.ProtocolError, qatracker.QATracker,
1116+ ("%s.invalid" % URL))
1117
1118=== added file 'python-library/tests/test_series.py'
1119--- python-library/tests/test_series.py 1970-01-01 00:00:00 +0000
1120+++ python-library/tests/test_series.py 2016-03-01 21:32:25 +0000
1121@@ -0,0 +1,77 @@
1122+import unittest
1123+import qatracker
1124+
1125+URL = 'http://iso.qa.dev.stgraber.org/xmlrpc.php'
1126+
1127+
1128+class ProductTests(unittest.TestCase):
1129+ def setUp(self):
1130+ self.instance = qatracker.QATracker(URL)
1131+
1132+ def test_series_lower(self):
1133+ self.assertIsInstance(self.instance.get_series('active'), list)
1134+
1135+ def test_series_mixed(self):
1136+ self.assertIsInstance(self.instance.get_series('DisAblEd'), list)
1137+
1138+ def test_series_upper(self):
1139+ self.assertIsInstance(self.instance.get_series('ACTIVE'), list)
1140+
1141+ def test_series_id(self):
1142+ self.assertIsInstance(self.instance.get_series(1), list)
1143+
1144+ def test_series_list(self):
1145+ self.assertIsInstance(self.instance.get_series(['AcTiVe', 1,
1146+ 'DISABLED',
1147+ 'active']), list)
1148+
1149+ def test_series_empty_list(self):
1150+ self.assertEquals(self.instance.get_series([]), [])
1151+
1152+ def test_series_all(self):
1153+ self.assertIsInstance(self.instance.get_series(), list)
1154+
1155+ def test_series_type(self):
1156+ series = self.instance.get_series()[0]
1157+ self.assertIsInstance(series, qatracker.QATrackerSeries)
1158+ self.assertIsInstance(series.id, int)
1159+ self.assertIsInstance(series.title, str)
1160+
1161+ def test_series_repr(self):
1162+ series = self.instance.get_series()[0]
1163+ self.assertTrue(str(series).startswith('QATrackerSeries: '))
1164+
1165+ def test_series_invalid_id(self):
1166+ self.assertRaises(IndexError, self.instance.get_series, -1)
1167+
1168+ def test_series_invalid_string(self):
1169+ self.assertRaises(IndexError, self.instance.get_series, 'bla')
1170+
1171+ def test_series_invalid_type(self):
1172+ self.assertRaises(TypeError, self.instance.get_series, object())
1173+
1174+ def test_series_manifest_all(self):
1175+ self.assertIsInstance(self.instance.get_series()[0].get_manifest(),
1176+ list)
1177+
1178+ def test_series_manifest_none(self):
1179+ self.assertIsInstance(self.instance.get_series()[0].get_manifest([]),
1180+ list)
1181+
1182+ def test_series_manifest_type(self):
1183+ for series in self.instance.get_series():
1184+ manifest = series.get_manifest()
1185+ if manifest:
1186+ self.assertIsInstance(manifest[0],
1187+ qatracker.QATrackerSeriesManifest)
1188+ self.assertIsInstance(manifest[0].productid, int)
1189+ self.assertIsInstance(manifest[0].product_title, str)
1190+ break
1191+
1192+ def test_series_manifest_repr(self):
1193+ for series in self.instance.get_series():
1194+ manifest = series.get_manifest()
1195+ if manifest:
1196+ self.assertTrue(str(manifest[0]).startswith(
1197+ 'QATrackerSeriesManifest: '))
1198+ break
1199
1200=== added file 'python-library/tests/test_testcase.py'
1201--- python-library/tests/test_testcase.py 1970-01-01 00:00:00 +0000
1202+++ python-library/tests/test_testcase.py 2016-03-01 21:32:25 +0000
1203@@ -0,0 +1,68 @@
1204+import unittest
1205+import qatracker
1206+
1207+URL = 'http://iso.qa.dev.stgraber.org/xmlrpc.php'
1208+
1209+
1210+class TestcaseTests(unittest.TestCase):
1211+ def setUp(self):
1212+ self.instance = qatracker.QATracker(URL)
1213+ self.product = self.instance.get_products(0)[0]
1214+ self.milestone = self.instance.get_milestones()[0]
1215+
1216+ def test_testcase_lower(self):
1217+ self.assertIsInstance(self.product.get_testcases(self.milestone,
1218+ 'mandatory'), list)
1219+
1220+ def test_testcase_mixed(self):
1221+ self.assertIsInstance(self.product.get_testcases(self.milestone,
1222+ 'DisAblEd'), list)
1223+
1224+ def test_testcase_upper(self):
1225+ self.assertIsInstance(self.product.get_testcases(self.milestone,
1226+ 'RUN-ONCE'), list)
1227+
1228+ def test_testcase_id(self):
1229+ self.assertIsInstance(self.product.get_testcases(self.milestone,
1230+ 3), list)
1231+
1232+ def test_testcase_seriesid(self):
1233+ self.assertIsInstance(self.product.get_testcases(self.milestone.series,
1234+ 3), list)
1235+
1236+ def test_testcase_list(self):
1237+ self.assertIsInstance(self.product.get_testcases(self.milestone,
1238+ ['Run-once', 0,
1239+ 'DISABLED',
1240+ 'optional']), list)
1241+
1242+ def test_testcase_empty_list(self):
1243+ self.assertEquals(self.product.get_testcases(self.milestone, []), [])
1244+
1245+ def test_testcase_all(self):
1246+ self.assertIsInstance(self.product.get_testcases(self.milestone), list)
1247+
1248+ def test_testcase_type(self):
1249+ testcase = self.product.get_testcases(self.milestone, 0)[0]
1250+ self.assertIsInstance(testcase, qatracker.QATrackerTestcase)
1251+ self.assertIsInstance(testcase.status, int)
1252+ self.assertIsInstance(testcase.title, str)
1253+
1254+ def test_testcase_repr(self):
1255+ testcase = self.product.get_testcases(self.milestone, 0)[0]
1256+ self.assertTrue(str(testcase).startswith('QATrackerTestcase: '))
1257+
1258+ def test_testcase_invalid_id(self):
1259+ self.assertRaises(IndexError, self.product.get_testcases,
1260+ self.milestone, -1)
1261+
1262+ def test_testcase_invalid_string(self):
1263+ self.assertRaises(IndexError, self.product.get_testcases,
1264+ self.milestone, 'bla')
1265+
1266+ def test_testcase_invalid_type(self):
1267+ self.assertRaises(TypeError, self.product.get_testcases,
1268+ self.milestone, object())
1269+
1270+ def test_testcase_invalid_seriesid(self):
1271+ self.assertRaises(TypeError, self.product.get_testcases, object(), 3)

Subscribers

People subscribed via source and target branches