aboutsummaryrefslogtreecommitdiffstats
path: root/libbe/bugdir.py
diff options
context:
space:
mode:
Diffstat (limited to 'libbe/bugdir.py')
-rw-r--r--libbe/bugdir.py811
1 files changed, 433 insertions, 378 deletions
diff --git a/libbe/bugdir.py b/libbe/bugdir.py
index 427ed38..7e4cf3e 100644
--- a/libbe/bugdir.py
+++ b/libbe/bugdir.py
@@ -16,13 +16,17 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import os
import os.path
-import cmdutil
import errno
-import names
-import mapfile
import time
+import copy
+import unittest
+import doctest
+
+import mapfile
+import bug
import utility
-from rcs import rcs_by_name
+import rcs
+
class NoBugDir(Exception):
def __init__(self, path):
@@ -30,48 +34,6 @@ class NoBugDir(Exception):
Exception.__init__(self, msg)
self.path = path
-
-def iter_parent_dirs(cur_dir):
- cur_dir = os.path.realpath(cur_dir)
- old_dir = None
- while True:
- yield cur_dir
- old_dir = cur_dir
- cur_dir = os.path.normpath(os.path.join(cur_dir, '..'))
- if old_dir == cur_dir:
- break;
-
-
-def tree_root(dir, old_version=False):
- for rootdir in iter_parent_dirs(dir):
- versionfile=os.path.join(rootdir, ".be", "version")
- if os.path.exists(versionfile):
- if not old_version:
- test_version(versionfile)
- return BugDir(os.path.join(rootdir, ".be"))
- elif not os.path.exists(rootdir):
- raise NoRootEntry(rootdir)
- old_rootdir = rootdir
- rootdir=os.path.join('..', rootdir)
-
- raise NoBugDir(dir)
-
-class BadTreeVersion(Exception):
- def __init__(self, version):
- Exception.__init__(self, "Unsupported tree version: %s" % version)
- self.version = version
-
-def test_version(path):
- tree_version = file(path, "rb").read()
- if tree_version != TREE_VERSION_STRING:
- raise BadTreeVersion(tree_version)
-
-def set_version(path, rcs):
- rcs.set_file_contents(os.path.join(path, "version"), TREE_VERSION_STRING)
-
-
-TREE_VERSION_STRING = "Bugs Everywhere Tree 1 0\n"
-
class NoRootEntry(Exception):
def __init__(self, path):
self.path = path
@@ -83,363 +45,456 @@ class AlreadyInitialized(Exception):
Exception.__init__(self,
"Specified root is already initialized: %s" % path)
-def create_bug_dir(path, rcs):
- """
- >>> import no_rcs, tests
- >>> create_bug_dir('/highly-unlikely-to-exist', no_rcs)
- Traceback (most recent call last):
- NoRootEntry: Specified root does not exist: /highly-unlikely-to-exist
- >>> test_dir = os.path.dirname(tests.bug_arch_dir().dir)
- >>> try:
- ... create_bug_dir(test_dir, no_rcs)
- ... except AlreadyInitialized, e:
- ... print "Already Initialized"
- Already Initialized
- """
- root = os.path.join(path, ".be")
- try:
- rcs.mkdir(root, paranoid=True)
- except OSError, e:
- if e.errno == errno.ENOENT:
- raise NoRootEntry(path)
- elif e.errno == errno.EEXIST:
- raise AlreadyInitialized(path)
- else:
- raise
- rcs.mkdir(os.path.join(root, "bugs"))
- set_version(root, rcs)
- map_save(rcs, os.path.join(root, "settings"), {"rcs_name": rcs.name})
- return BugDir(os.path.join(path, ".be"))
-
-
-def setting_property(name, valid=None):
- def getter(self):
- value = self.settings.get(name)
- if valid is not None:
- if value not in valid:
- raise InvalidValue(name, value)
- return value
-
- def setter(self, value):
- if valid is not None:
- if value not in valid and value is not None:
- raise InvalidValue(name, value)
- if value is None:
- del self.settings[name]
- else:
- self.settings[name] = value
- self.save_settings()
- return property(getter, setter)
-
-
-class BugDir:
- def __init__(self, dir):
- self.dir = dir
- self.bugs_path = os.path.join(self.dir, "bugs")
- try:
- self.settings = map_load(os.path.join(self.dir, "settings"))
- except NoSuchFile:
- self.settings = {"rcs_name": "None"}
-
- rcs_name = setting_property("rcs_name", ("None", "bzr", "git", "Arch", "hg"))
- _rcs = None
-
- target = setting_property("target")
-
- def save_settings(self):
- map_save(self.rcs, os.path.join(self.dir, "settings"), self.settings)
-
- def get_rcs(self):
- if self._rcs is not None and self.rcs_name == self._rcs.name:
- return self._rcs
- self._rcs = rcs_by_name(self.rcs_name)
- return self._rcs
-
- rcs = property(get_rcs)
-
- def get_reference_bugdir(self, spec):
- return BugDir(self.rcs.path_in_reference(self.dir, spec))
-
- def list(self):
- for uuid in self.list_uuids():
- yield self.get_bug(uuid)
-
- def bug_map(self):
- bugs = {}
- for bug in self.list():
- bugs[bug.uuid] = bug
- return bugs
-
- def get_bug(self, uuid):
- return Bug(self.bugs_path, uuid, self.rcs_name)
-
- def list_uuids(self):
- for uuid in os.listdir(self.bugs_path):
- if (uuid.startswith('.')):
- continue
- yield uuid
-
- def new_bug(self, uuid=None):
- if uuid is None:
- uuid = names.uuid()
- path = os.path.join(self.bugs_path, uuid)
- self.rcs.mkdir(path)
- bug = Bug(self.bugs_path, None, self.rcs_name)
- bug.uuid = uuid
- return bug
-
-class InvalidValue(Exception):
+class InvalidValue(ValueError):
def __init__(self, name, value):
msg = "Cannot assign value %s to %s" % (value, name)
Exception.__init__(self, msg)
self.name = name
self.value = value
+class MultipleBugMatches(ValueError):
+ def __init__(self, shortname, matches):
+ msg = ("More than one bug matches %s. "
+ "Please be more specific.\n%s" % shortname, matches)
+ ValueError.__init__(self, msg)
+ self.shortname = shortnamename
+ self.matches = matches
-def checked_property(name, valid):
- def getter(self):
- value = self.__getattribute__("_"+name)
- if value not in valid:
- raise InvalidValue(name, value)
- return value
- def setter(self, value):
- if value not in valid:
- raise InvalidValue(name, value)
- return self.__setattr__("_"+name, value)
- return property(getter, setter)
+TREE_VERSION_STRING = "Bugs Everywhere Tree 1 0\n"
-severity_levels = ("wishlist", "minor", "serious", "critical", "fatal")
-active_status = ("open", "in-progress", "waiting", "new", "verified")
-inactive_status = ("closed", "disabled", "fixed", "wontfix", "waiting")
-severity_value = {}
-for i in range(len(severity_levels)):
- severity_value[severity_levels[i]] = i
+def setting_property(name, valid=None, doc=None):
+ def getter(self):
+ value = self.settings.get(name)
+ if valid is not None:
+ if value not in valid and value != None:
+ raise InvalidValue(name, value)
+ return value
+
+ def setter(self, value):
+ if value != getter(self):
+ if value is None:
+ del self.settings[name]
+ else:
+ self.settings[name] = value
+ self._save_settings(self.get_path("settings"), self.settings)
+
+ return property(getter, setter, doc=doc)
-class Bug(object):
- status = checked_property("status", (None,)+active_status+inactive_status)
- severity = checked_property("severity", (None, "wishlist", "minor",
- "serious", "critical", "fatal"))
- def __init__(self, path, uuid, rcs_name):
- self.path = path
- self.uuid = uuid
- if uuid is not None:
- dict = map_load(self.get_path("values"))
+class BugDir (list):
+ """
+ File-system access:
+ When rooted in non-bugdir directory, BugDirs live completely in
+ memory until the first call to .save(). This creates a '.be'
+ sub-directory containing configurations options, bugs, comments,
+ etc. Once this sub-directory has been created (possibly by
+ another BugDir instance) any changes to the BugDir in memory will
+ be flushed to the file system automatically. However, the BugDir
+ will only load information from the file system when it loads new
+ bugs/comments that it doesn't already have in memory, or when it
+ explicitly asked to do so (e.g. .load() or __init__(from_disk=True)).
+ """
+ def __init__(self, root=None, sink_to_existing_root=True,
+ assert_new_BugDir=False, allow_rcs_init=False,
+ from_disk=False, rcs=None):
+ list.__init__(self)
+ self._save_user_id = False
+ self.settings = {}
+ if root == None:
+ root = os.getcwd()
+ if sink_to_existing_root == True:
+ self.root = self._find_root(root)
else:
- dict = {}
-
- self.rcs_name = rcs_name
-
- self.summary = dict.get("summary")
- self.creator = dict.get("creator")
- self.target = dict.get("target")
- self.status = dict.get("status")
- self.severity = dict.get("severity")
- self.assigned = dict.get("assigned")
- self.time = dict.get("time")
- if self.time is not None:
- self.time = utility.str_to_time(self.time)
-
- def __repr__(self):
- return "Bug(uuid=%r)" % self.uuid
-
- def get_path(self, file):
- return os.path.join(self.path, self.uuid, file)
+ if not os.path.exists(root):
+ raise NoRootEntry(root)
+ self.root = root
+ if from_disk == True:
+ self.load()
+ else:
+ if assert_new_BugDir == True:
+ if os.path.exists(self.get_path()):
+ raise AlreadyInitialized, self.get_path()
+ if rcs == None:
+ rcs = self._guess_rcs(allow_rcs_init)
+ self.rcs = rcs
+ user_id = self.rcs.get_user_id()
+
+ def _find_root(self, path):
+ """
+ Search for an existing bug database dir and it's ancestors and
+ return a BugDir rooted there.
+ """
+ if not os.path.exists(path):
+ raise NoRootEntry(path)
+ versionfile = utility.search_parent_directories(path, os.path.join(".be", "version"))
+ if versionfile != None:
+ beroot = os.path.dirname(versionfile)
+ root = os.path.dirname(beroot)
+ return root
+ else:
+ beroot = utility.search_parent_directories(path, ".be")
+ if beroot == None:
+ raise NoBugDir(path)
+ return beroot
+
+ def get_version(self, path=None):
+ if self.rcs_name == None:
+ # Use a temporary RCS to check the version for the first time
+ RCS = rcs.rcs_by_name("None")
+ RCS.root(self.root)
+ else:
+ RCS = self.rcs
- def _get_active(self):
- return self.status in active_status
+ if path == None:
+ path = self.get_path("version")
+ tree_version = RCS.get_file_contents(path)
+ return tree_version
- active = property(_get_active)
+ def set_version(self):
+ self.rcs.set_file_contents(self.get_path("version"),
+ TREE_VERSION_STRING)
- def add_attr(self, map, name):
- value = self.__getattribute__(name)
- if value is not None:
- map[name] = value
+ rcs_name = setting_property("rcs_name",
+ ("None", "bzr", "git", "Arch", "hg"),
+ doc=
+"""The name of the current RCS. Kept seperate to make saving/loading
+settings easy. Don't set this attribute. Set .rcs instead, and
+.rcs_name will be automatically adjusted.""")
- def save(self):
- map = {}
- self.add_attr(map, "assigned")
- self.add_attr(map, "summary")
- self.add_attr(map, "creator")
- self.add_attr(map, "target")
- self.add_attr(map, "status")
- self.add_attr(map, "severity")
- if self.time is not None:
- map["time"] = utility.time_to_str(self.time)
- path = self.get_path("values")
- map_save(rcs_by_name(self.rcs_name), path, map)
+ _rcs = None
def _get_rcs(self):
- return rcs_by_name(self.rcs_name)
-
- rcs = property(_get_rcs)
-
- def new_comment(self):
- if not os.path.exists(self.get_path("comments")):
- self.rcs.mkdir(self.get_path("comments"))
- comm = Comment(None, self)
- comm.uuid = names.uuid()
- return comm
-
- def get_comment(self, uuid):
- return Comment(uuid, self)
-
- def iter_comment_ids(self):
- path = self.get_path("comments")
- if not os.path.isdir(path):
- return
- try:
- for uuid in os.listdir(path):
- if (uuid.startswith('.')):
- continue
- yield uuid
- except OSError, e:
- if e.errno != errno.ENOENT:
- raise
- return
+ return self._rcs
- def list_comments(self):
- comments = [Comment(id, self) for id in self.iter_comment_ids()]
- comments.sort(cmp_date)
- return comments
-
-def cmp_date(comm1, comm2):
- return cmp(comm1.date, comm2.date)
-
-def new_bug(dir, uuid=None):
- bug = dir.new_bug(uuid)
- bug.creator = names.creator()
- bug.severity = "minor"
- bug.status = "open"
- bug.time = time.time()
- return bug
-
-def new_comment(bug, body=None):
- comm = bug.new_comment()
- comm.From = names.creator()
- comm.date = time.time()
- comm.body = body
- return comm
-
-def add_headers(obj, map, names):
- map_names = {}
- for name in names:
- map_names[name] = pyname_to_header(name)
- add_attrs(obj, map, names, map_names)
-
-def add_attrs(obj, map, names, map_names=None):
- if map_names is None:
- map_names = {}
- for name in names:
- map_names[name] = name
-
- for name in names:
- value = obj.__getattribute__(name)
- if value is not None:
- map[map_names[name]] = value
-
-
-class Comment(object):
- def __init__(self, uuid, bug):
- object.__init__(self)
- self.uuid = uuid
- self.bug = bug
- if self.uuid is not None and self.bug is not None:
- mapfile = map_load(self.get_path("values"))
- self.date = utility.str_to_time(mapfile["Date"])
- self.From = mapfile["From"]
- self.in_reply_to = mapfile.get("In-reply-to")
- self.content_type = mapfile.get("Content-type", "text/plain")
- self.body = file(self.get_path("body")).read().decode("utf-8")
+ def _set_rcs(self, new_rcs):
+ if new_rcs == None:
+ new_rcs = rcs.rcs_by_name("None")
+ self._rcs = new_rcs
+ new_rcs.root(self.root)
+ self.rcs_name = new_rcs.name
+
+ rcs = property(_get_rcs, _set_rcs,
+ doc="A revision control system (RCS) instance")
+
+ _user_id = setting_property("user-id", doc=
+"""The user's prefered name. Kept seperate to make saving/loading
+settings easy. Don't set this attribute. Set .user_id instead,
+and ._user_id will be automatically adjusted. This setting is
+only saved if ._save_user_id == True""")
+
+ def _get_user_id(self):
+ if self._user_id == None and self.rcs != None:
+ self._user_id = self.rcs.get_user_id()
+ return self._user_id
+
+ def _set_user_id(self, user_id):
+ if self.rcs != None:
+ self.rcs.user_id = user_id
+ self._user_id = user_id
+
+ user_id = property(_get_user_id, _set_user_id, doc=
+"""The user's prefered name, e.g 'John Doe <jdoe@example.com>'. Note
+that the Arch RCS backend *enforces* ids with this format.""")
+
+ target = setting_property("target",
+ doc="The current project development target")
+
+ def save_user_id(self, user_id=None):
+ if user_id == None:
+ user_id = self.user_id
+ self._save_user_id = True
+ self.user_id = user_id
+
+ def get_path(self, *args):
+ my_dir = os.path.join(self.root, ".be")
+ if len(args) == 0:
+ return my_dir
+ assert args[0] in ["version", "settings", "bugs"], str(args)
+ return os.path.join(my_dir, *args)
+
+ def _guess_rcs(self, allow_rcs_init=False):
+ deepdir = self.get_path()
+ if not os.path.exists(deepdir):
+ deepdir = os.path.dirname(deepdir)
+ new_rcs = rcs.detect_rcs(deepdir)
+ install = False
+ if new_rcs.name == "None":
+ if allow_rcs_init == True:
+ new_rcs = rcs.installed_rcs()
+ new_rcs.init(self.root)
+ self.rcs = new_rcs
+ return new_rcs
+
+ def load(self):
+ version = self.get_version()
+ if version != TREE_VERSION_STRING:
+ raise NotImplementedError, \
+ "BugDir cannot handle version '%s' yet." % version
else:
- self.date = None
- self.From = None
- self.in_reply_to = None
- self.content_type = "text/plain"
- self.body = None
-
- def save(self):
- map_file = {"Date": utility.time_to_str(self.date)}
- add_headers(self, map_file, ("From", "in_reply_to", "content_type"))
- if not os.path.exists(self.get_path(None)):
- self.bug.rcs.mkdir(self.get_path(None))
- map_save(self.bug.rcs, self.get_path("values"), map_file)
- self.bug.rcs.set_file_contents(self.get_path("body"),
- self.body.encode('utf-8'))
+ if not os.path.exists(self.get_path()):
+ raise NoBugDir(self.get_path())
+ self.settings = self._get_settings(self.get_path("settings"))
+ self.rcs = rcs.rcs_by_name(self.rcs_name)
+ if self._user_id != None: # was a user name in the settings file
+ self.save_user_id()
+
+ self._bug_map_gen()
- def get_path(self, name):
- my_dir = os.path.join(self.bug.get_path("comments"), self.uuid)
- if name is None:
- return my_dir
- return os.path.join(my_dir, name)
-
-
-def thread_comments(comments):
- child_map = {}
- top_comments = []
- for comment in comments:
- child_map[comment.uuid] = []
- for comment in comments:
- if comment.in_reply_to is None or comment.in_reply_to not in child_map:
- top_comments.append(comment)
- continue
- child_map[comment.in_reply_to].append(comment)
-
- def recurse_children(comment):
- child_list = []
- for child in child_map[comment.uuid]:
- child_list.append(recurse_children(child))
- return (comment, child_list)
- return [recurse_children(c) for c in top_comments]
-
-
-def pyname_to_header(name):
- return name.capitalize().replace('_', '-')
-
-
-def map_save(rcs, path, map):
- """Save the map as a mapfile to the specified path"""
- add = not os.path.exists(path)
- output = file(path, "wb")
- mapfile.generate(output, map)
- if add:
- rcs.add_id(path)
-
-class NoSuchFile(Exception):
- def __init__(self, pathname):
- Exception.__init__(self, "No such file: %s" % pathname)
-
-
-def map_load(path):
- try:
- return mapfile.parse(file(path, "rb"))
- except IOError, e:
- if e.errno != errno.ENOENT:
- raise e
- raise NoSuchFile(path)
+ def load_all_bugs(self):
+ "Warning: this could take a while."
+ self._clear_bugs()
+ for uuid in self.list_uuids():
+ self._load_bug(uuid)
+ def save(self):
+ self.rcs.mkdir(self.get_path())
+ self.set_version()
+ self._save_settings(self.get_path("settings"), self.settings)
+ self.rcs.mkdir(self.get_path("bugs"))
+ for bug in self:
+ bug.save()
+
+ def _get_settings(self, settings_path):
+ if self.rcs_name == None:
+ # Use a temporary RCS to loading settings the first time
+ RCS = rcs.rcs_by_name("None")
+ RCS.root(self.root)
+ else:
+ RCS = self.rcs
+
+ allow_no_rcs = not RCS.path_in_root(settings_path)
+ # allow_no_rcs=True should only be for the special case of
+ # configuring duplicate bugdir settings
+
+ try:
+ settings = mapfile.map_load(RCS, settings_path, allow_no_rcs)
+ except rcs.NoSuchFile:
+ settings = {"rcs_name": "None"}
+ return settings
+
+ def _save_settings(self, settings_path, settings):
+ this_dir_path = os.path.realpath(self.get_path("settings"))
+ if os.path.realpath(settings_path) == this_dir_path:
+ if not os.path.exists(self.get_path()):
+ # don't save settings until the bug directory has been
+ # initialized. this initialization happens the first time
+ # a bug directory is saved (BugDir.save()). If the user
+ # is just working with a BugDir in memory, we don't want
+ # to go cluttering up his file system with settings files.
+ return
+ if self._save_user_id == False:
+ if "user-id" in settings:
+ settings = copy.copy(settings)
+ del settings["user-id"]
+ allow_no_rcs = not self.rcs.path_in_root(settings_path)
+ # allow_no_rcs=True should only be for the special case of
+ # configuring duplicate bugdir settings
+ mapfile.map_save(self.rcs, settings_path, settings, allow_no_rcs)
+
+ def duplicate_bugdir(self, revision):
+ duplicate_path = self.rcs.duplicate_repo(revision)
+
+ # setup revision RCS as None, since the duplicate may not be
+ # initialized for versioning
+ duplicate_settings_path = os.path.join(duplicate_path,
+ ".be", "settings")
+ duplicate_settings = self._get_settings(duplicate_settings_path)
+ if "rcs_name" in duplicate_settings:
+ duplicate_settings["rcs_name"] = "None"
+ duplicate_settings["user-id"] = self.user_id
+ self._save_settings(duplicate_settings_path, duplicate_settings)
+
+ return BugDir(duplicate_path, from_disk=True)
+
+ def remove_duplicate_bugdir(self):
+ self.rcs.remove_duplicate_repo()
+
+ def _bug_map_gen(self):
+ map = {}
+ for bug in self:
+ map[bug.uuid] = bug
+ for uuid in self.list_uuids():
+ if uuid not in map:
+ map[uuid] = None
+ self._bug_map = map
-class MockBug:
- def __init__(self, severity):
- self.severity = severity
+ def list_uuids(self):
+ uuids = []
+ if os.path.exists(self.get_path()):
+ # list the uuids on disk
+ for uuid in os.listdir(self.get_path("bugs")):
+ if not (uuid.startswith('.')):
+ uuids.append(uuid)
+ yield uuid
+ # and the ones that are still just in memory
+ for bug in self:
+ if bug.uuid not in uuids:
+ uuids.append(bug.uuid)
+ yield bug.uuid
+
+ def _clear_bugs(self):
+ while len(self) > 0:
+ self.pop()
+
+ def _load_bug(self, uuid):
+ bg = bug.Bug(bugdir=self, uuid=uuid, from_disk=True)
+ self.append(bg)
+ self._bug_map_gen()
+ return bg
+
+ def new_bug(self, uuid=None, summary=None):
+ bg = bug.Bug(bugdir=self, uuid=uuid, summary=summary)
+ self.append(bg)
+ self._bug_map_gen()
+ return bg
+
+ def remove_bug(self, bug):
+ self.remove(bug)
+ bug.remove()
+
+ def bug_shortname(self, bug):
+ """
+ Generate short names from uuids. Picks the minimum number of
+ characters (>=3) from the beginning of the uuid such that the
+ short names are unique.
+
+ Obviously, as the number of bugs in the database grows, these
+ short names will cease to be unique. The complete uuid should be
+ used for long term reference.
+ """
+ chars = 3
+ for uuid in self._bug_map.keys():
+ if bug.uuid == uuid:
+ continue
+ while (bug.uuid[:chars] == uuid[:chars]):
+ chars+=1
+ return bug.uuid[:chars]
+
+ def bug_from_shortname(self, shortname):
+ """
+ >>> bd = simple_bug_dir()
+ >>> bug_a = bd.bug_from_shortname('a')
+ >>> print type(bug_a)
+ <class 'libbe.bug.Bug'>
+ >>> print bug_a
+ a:om: Bug A
+ """
+ matches = []
+ self._bug_map_gen()
+ for uuid in self._bug_map.keys():
+ if uuid.startswith(shortname):
+ matches.append(uuid)
+ if len(matches) > 1:
+ raise MultipleBugMatches(shortname, matches)
+ if len(matches) == 1:
+ return self.bug_from_uuid(matches[0])
+ raise KeyError("No bug matches %s" % shortname)
+
+ def bug_from_uuid(self, uuid):
+ if not self.has_bug(uuid):
+ raise KeyError("No bug matches %s\n bug map: %s\n root: %s" \
+ % (uuid, self._bug_map, self.root))
+ if self._bug_map[uuid] == None:
+ self._load_bug(uuid)
+ return self._bug_map[uuid]
+
+ def has_bug(self, bug_uuid):
+ if bug_uuid not in self._bug_map:
+ self._bug_map_gen()
+ if bug_uuid not in self._bug_map:
+ return False
+ return True
+
-def cmp_severity(bug_1, bug_2):
+def simple_bug_dir():
"""
- Compare the severity levels of two bugs, with more sever bugs comparing
- as less.
-
- >>> cmp_severity(MockBug(None), MockBug(None))
- 0
- >>> cmp_severity(MockBug("wishlist"), MockBug(None)) < 0
- True
- >>> cmp_severity(MockBug(None), MockBug("wishlist")) > 0
- True
- >>> cmp_severity(MockBug("critical"), MockBug("wishlist")) < 0
- True
+ For testing
+ >>> bugdir = simple_bug_dir()
+ >>> ls = list(bugdir.list_uuids())
+ >>> ls.sort()
+ >>> print ls
+ ['a', 'b']
"""
- val_1 = severity_value.get(bug_1.severity)
- val_2 = severity_value.get(bug_2.severity)
- return -cmp(val_1, val_2)
+ dir = utility.Dir()
+ assert os.path.exists(dir.path)
+ bugdir = BugDir(dir.path, sink_to_existing_root=False, allow_rcs_init=True)
+ bugdir._dir_ref = dir # postpone cleanup since dir.__del__() removes dir.
+ bug_a = bugdir.new_bug("a", summary="Bug A")
+ bug_a.creator = "John Doe <jdoe@example.com>"
+ bug_a.time = 0
+ bug_b = bugdir.new_bug("b", summary="Bug B")
+ bug_b.creator = "Jane Doe <jdoe@example.com>"
+ bug_b.time = 0
+ bug_b.status = "closed"
+ bugdir.save()
+ return bugdir
+
+
+class BugDirTestCase(unittest.TestCase):
+ def __init__(self, *args, **kwargs):
+ unittest.TestCase.__init__(self, *args, **kwargs)
+ def setUp(self):
+ self.dir = utility.Dir()
+ self.bugdir = BugDir(self.dir.path, sink_to_existing_root=False,
+ allow_rcs_init=True)
+ self.rcs = self.bugdir.rcs
+ def tearDown(self):
+ self.rcs.cleanup()
+ self.dir.cleanup()
+ def fullPath(self, path):
+ return os.path.join(self.dir.path, path)
+ def assertPathExists(self, path):
+ fullpath = self.fullPath(path)
+ self.failUnless(os.path.exists(fullpath)==True,
+ "path %s does not exist" % fullpath)
+ self.assertRaises(AlreadyInitialized, BugDir,
+ self.dir.path, assertNewBugDir=True)
+ def versionTest(self):
+ if self.rcs.versioned == False:
+ return
+ original = self.bugdir.rcs.commit("Began versioning")
+ bugA = self.bugdir.bug_from_uuid("a")
+ bugA.status = "fixed"
+ self.bugdir.save()
+ new = self.rcs.commit("Fixed bug a")
+ dupdir = self.bugdir.duplicate_bugdir(original)
+ self.failUnless(dupdir.root != self.bugdir.root,
+ "%s, %s" % (dupdir.root, self.bugdir.root))
+ bugAorig = dupdir.bug_from_uuid("a")
+ self.failUnless(bugA != bugAorig,
+ "\n%s\n%s" % (bugA.string(), bugAorig.string()))
+ bugAorig.status = "fixed"
+ self.failUnless(bug.cmp_status(bugA, bugAorig)==0,
+ "%s, %s" % (bugA.status, bugAorig.status))
+ self.failUnless(bug.cmp_severity(bugA, bugAorig)==0,
+ "%s, %s" % (bugA.severity, bugAorig.severity))
+ self.failUnless(bug.cmp_assigned(bugA, bugAorig)==0,
+ "%s, %s" % (bugA.assigned, bugAorig.assigned))
+ self.failUnless(bug.cmp_time(bugA, bugAorig)==0,
+ "%s, %s" % (bugA.time, bugAorig.time))
+ self.failUnless(bug.cmp_creator(bugA, bugAorig)==0,
+ "%s, %s" % (bugA.creator, bugAorig.creator))
+ self.failUnless(bugA == bugAorig,
+ "\n%s\n%s" % (bugA.string(), bugAorig.string()))
+ self.bugdir.remove_duplicate_bugdir()
+ self.failUnless(os.path.exists(dupdir.root)==False, str(dupdir.root))
+ def testRun(self):
+ self.bugdir.new_bug(uuid="a", summary="Ant")
+ self.bugdir.new_bug(uuid="b", summary="Cockroach")
+ self.bugdir.new_bug(uuid="c", summary="Praying mantis")
+ length = len(self.bugdir)
+ self.failUnless(length == 3, "%d != 3 bugs" % length)
+ uuids = list(self.bugdir.list_uuids())
+ self.failUnless(len(uuids) == 3, "%d != 3 uuids" % len(uuids))
+ self.failUnless(uuids == ["a","b","c"], str(uuids))
+ bugA = self.bugdir.bug_from_uuid("a")
+ bugAprime = self.bugdir.bug_from_shortname("a")
+ self.failUnless(bugA == bugAprime, "%s != %s" % (bugA, bugAprime))
+ self.bugdir.save()
+ self.versionTest()
+
+unitsuite = unittest.TestLoader().loadTestsFromTestCase(BugDirTestCase)
+suite = unittest.TestSuite([unitsuite])#, doctest.DocTestSuite()])