Commits

Diego Búrigo Zacarão committed fcfad35

Better handling of VCS exceptions - first shot (#158)

- Now Tx shows at the UI what's going on with the
related VCS backend whenever something goes wrong.
- No longer Tx fallback to setup_repo() whenever the
init_repo() fails.
- The basic support for #191 is in place, but it still
need to be implemented.
- Now all the VCS operations are covered with unittests.
- User messages might need improvements.

  • Participants
  • Parent commits 5a4ac38

Comments (0)

Files changed (18)

File transifex/projects/handlers/types/pot.py

 import itertools
 from django.contrib.contenttypes.models import ContentType
 from django.dispatch import Signal
-from codebases.lib import BrowserError
 from translations.lib.types.pot import POTManager
 from translations.models import POFile
 from languages.models import Language
 from txcommon import rst
 from projects.models import Component
 from projects.signals import pre_set_stats, post_set_stats
+from vcs.lib.exceptions import RevisionRepoError
 
 class POTHandler:
     """
         if hasattr(self.component, 'get_rev'):
             try:
                 rev = self.component.get_rev(filename)
-            except BrowserError:
+            except RevisionRepoError:
                 pass
 
             if rev and rev == s.rev:

File transifex/projects/views/component.py

 from txcommon.log import logger
 from txcommon.models import exclusive_fields, get_profile_or_user
 from txcommon.views import json_result, json_error
+from vcs.lib.exceptions import BaseVCSError
 
 # Cache the current site
 current_site = Site.objects.get_current()
                                   project__slug=project_slug)
     logger.debug("Requested stats calc for component %s" % component.full_name)
     if component.should_calculate:
-        # Checkout
-        component.prepare()
         # Calculate statistics
         try:
+            # Checkout
+            component.prepare()
             pre_refresh_cache.send(sender=None, component=component)
             component.trans.set_stats()
         except FileFilterError:
                 "directory named in the set of files of this Publican like "
                 "component. Maybe its file filter is not allowing access to it."))
 
+        except BaseVCSError, e:
+            if e.notify_admins:
+                pass
+            request.user.message_set.create(message=e.get_user_message())
+
     else:
         logger.debug("Statistics calculation is disabled for the '%s' component."
                      % component)
         except AddonError, err:
             logger.debug("An addon encountered exception: %s" % err)
             request.user.message_set.create(message=str(err))
+        except BaseVCSError, e:
+            if e.notify_admins:
+                pass
+            request.user.message_set.create(message=e.get_user_message())
         except StandardError, e:
             logger.debug("Error submiting translation file %s"
                          " for %s component: %s" % (filename,

File transifex/vcs/lib/exceptions/__init__.py

+# -*- coding: utf-8 -*-
+from django.utils.translation import ugettext as _
+from txcommon.log import logger
+
+class BaseVCSError(Exception):
+    """
+    Base Exception for all the VCS related exceptions that should be handled
+    by the system and shown at the user interface in a clear way.
+
+    This Exception is logged by default using logger.error().
+
+    Attributes:
+        original_exception -- Object/String related to the original exception.
+    """
+    # Some global settings
+    message = "Gereric VCS error"
+    user_message = _("Your request could not be completed. The admins have "
+        "been notified to take a closer look at it. Please try again in a "
+        "few minutes")
+    notify_admins = True
+    notify_maintainers = False
+
+    def __init__(self, original_exception):
+        self.original_exception = original_exception
+        logger.error(self.__str__())
+
+    def __str__(self):
+        return repr('%s: %s' % (self.message, self.original_exception))
+
+    def get_user_message(self, with_details=True):
+        if with_details:
+            return "%s Details from the VCS backend: '%s'." % (self.user_message, self.original_exception)
+        else:
+            return user_message
+
+# General exceptions for errors of all VCSs
+class SetupRepoError(BaseVCSError):
+    message = "Local repository setup failed"
+    user_message = _("Setup of the repository locally could not be done "
+        "successfully.")
+    notify_admins = False
+
+
+class InitRepoError(BaseVCSError):
+    message = "Local repository initialization failed"
+    user_message = _("The local repository has not been setup yet. Please, "
+        "first do a check-out of the repository.")
+    notify_admins = False
+
+
+class UpdateRepoError(BaseVCSError):
+    message = "Update/Pull from remote repository failed"
+    user_message = _("Unable to pull data from the remote repository. Is the"
+        "remote host up?")
+    notify_maintainers = True
+
+
+class CleanupRepoError(BaseVCSError):
+    message = "Cleanup of repository failed"
+    user_message = _("Cleanup of the repository failed. The admins "
+        "have been notified to take a closer look at it.")
+
+
+class CommitRepoError(BaseVCSError):
+    message = "Commit to repository failed"
+    user_message = _("Commit to the repository failed.")
+
+
+class PushRepoError(BaseVCSError):
+    message = "Push to remote repository failed"
+    user_message = _("Pushing to the remote repository failed.")
+    notify_maintainers = True
+
+
+class RevisionRepoError(BaseVCSError):
+    message = "Given file does not have a revision"
+    user_message = _("Given file does not have a revision. The admins "
+        "have been notified to take a closer look at it.")
+

File transifex/vcs/lib/types/bzr.py

 
 from django.conf import settings
 from codebases.lib import BrowserMixin, BrowserError
+from vcs.lib.exceptions import *
 from vcs.lib.types import need_repo
 from txcommon.log import logger
 
         Commands used:
         bzr checkout --lightweight <remote_path> <self.path>
         """
-        remote_work_tree, self.repo = bzrdir.BzrDir.open_tree_or_branch(
-            self.remote_path)
-        self.work_tree = self.repo.create_checkout(
-            self.path, lightweight=True, accelerator_tree=remote_work_tree)
-
+        try:
+            remote_work_tree, self.repo = bzrdir.BzrDir.open_tree_or_branch(
+                self.remote_path)
+            self.work_tree = self.repo.create_checkout(
+                self.path, lightweight=True, accelerator_tree=remote_work_tree)
+        except Exception, e:
+            raise SetupRepoError(e)
 
     def init_repo(self):
         """
             # Check that the path is a checkout
             self.work_tree, self.repo = bzrdir.BzrDir.open_tree_or_branch(
                 self.path)
-        except NotBranchError:
-            # Else create a lightweight checkout there.
-            self.setup_repo()
-            
+        except Exception, e :
+            raise InitRepoError(e)
+
     def _clean_dir(self):
         """
         Clean the local working directory.
             # want to import files that were left over from another run by mistake.
             clean_tree.clean_tree(self.path, unknown=True, ignored=True,
                               detritus=True, no_prompt=True)
-        except:
-            pass
+        except Exception, e:
+            raise CleanupRepoError(e)
 
     @need_repo
     def update(self):
         """
         # Note: If we used a branch instead of a checkout, we'd want to use
         # bzr pull.
-        self._clean_dir()
-        self.work_tree.update()
+        try:
+            self._clean_dir()
+            self.work_tree.update()
+        except Exception, e:
+            raise UpdateRepoError(e)
 
     @need_repo
     def get_rev(self, obj=None):
                     self.repo.last_revision())
                 i = t.inventory[t.path2id(obj)]
                 return m[i.revision]
-        # TODO: Make it more specific
-        except:
-            logger.error(traceback.format_exc())
-            raise BrowserError()
+        except Exception, e:
+            raise RevisionRepoError(e)
 
     @need_repo
     def submit(self, files, msg, user):
         committer = '%s <%s>' % (settings.COMMITTER_NAME,
                                  settings.COMMITTER_EMAIL)
 
-        self.work_tree.commit(message=msg, committer=committer,
-                              specific_files=filenames,
-                              revprops={'author': user})
+        try:
+            self.work_tree.commit(message=msg, committer=committer,
+                                specific_files=filenames,
+                                revprops={'author': user})
+        except Exception, e:
+            raise PushRepoError(e)

File transifex/vcs/lib/types/cvs.py

 import time
 import traceback
 import os.path
-
 from django.conf import settings
-from vcs.lib import RepoError
 from codebases.lib import BrowserMixin, BrowserError
+#from vcs.lib import RepoError
+from vcs.lib.exceptions import *
+from vcs.lib.support.cvs import repository, checkout
 from vcs.lib.types import need_repo
-from vcs.lib.support.cvs import repository, checkout
+from txcommon.commands import CommandError
 from txcommon.log import logger
 
 REPO_PATH = settings.REPO_PATHS['cvs']
     def setup_repo(self):
         """
         Initialize repository for the first time.
-        
+
         Commands used:
         cvs -d checkout 
-       
         """
         branch = None
         if self.branch != 'HEAD':
             branch = self.branch
-        repo = checkout(root=self.root, module=self.module,
-                        dest=self.path, branch=branch)
-        return repo
-
+        try:
+            self.repo = checkout(root=self.root, module=self.module,
+                dest=self.path, branch=branch)
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            self.teardown_repo()
+            raise SetupRepoError(e)
 
     def init_repo(self):
         """
         Initialize the ``repo`` variable on the browser.
-        
+
         If local repo exists, use that. If not, check it out.
         """
-        
         try:
             self.repo = repository(self.path)
-        except RepoError:
-            self.repo = self.setup_repo()
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            raise InitRepoError(e)
 
     @need_repo
     def update(self):
         """
         cvs up -PdC
         """
-        self.repo.up(P=True, d=True, C=True)
-
+        try:
+            self.repo.up(P=True, d=True, C=True)
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            raise UpdateRepoError(e)
 
     def _clean_dir(self):
         """
         """
         try:
             self.update()
-        except:
-            pass
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            raise CleanupRepoError(e)
 
     @need_repo
     def get_rev(self, obj=None):
-        """
-        Get the current revision of a file within the repository.
-        
-        Commands used:
-        none
-        """
+        """Get the current revision of a file within the repository."""
         try:
             if not obj:
                 raise ValueError('CVS repos do not have a global revision')
             p = os.path.join(self.path, obj)
             if not os.path.exists(p):
-                return None
+                raise ValueError('File does not exist in the repository')
             if not os.path.isfile(p):
                 raise ValueError('Only files have a revision in CVS')
             d, b = os.path.split(p)
                 else:
                     rev = None
                 ef.close()
-            except IOError:
-                return None
+                if rev==None:
+                    raise Exception('Could not find a revision')
+            except IOError, e:
+                raise Exception(str(e))
             return tuple(int(p) for p in rev.split('.'))
-        # TODO: Make it more specific
-        except:
-            logger.error(traceback.format_exc())
-            raise BrowserError()
+        except Exception, e:
+            raise RevisionRepoError(e)
 
     @need_repo
     def submit(self, files, msg, user):
 
         files = ' '.join(filenames).encode('utf-8')
 
-        # cvs ci files
-        self.repo.commit(files, m=msg.encode('utf-8'))
-        self.update()
+        try:
+            self.repo.commit(files, m=msg.encode('utf-8'))
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            raise CommitRepoError(e)
+
+        try:
+            self.update()
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            raise PushRepoError(e)

File transifex/vcs/lib/types/git.py

 import traceback
 
 from django.conf import settings
+from codebases.lib import BrowserMixin, BrowserError
 from vcs.lib import RepoError
-from codebases.lib import BrowserMixin, BrowserError
+from vcs.lib.exceptions import *
 from vcs.lib.types import need_repo
 from vcs.lib.support.git import repository, clone
+from txcommon.commands import CommandError
 from txcommon.log import logger
 
 REPO_PATH = settings.REPO_PATHS['git']
 
 class GitBrowser(BrowserMixin):
-
     """
     A browser class for Git repositories.
-    
+
     Git homepage: http://git.or.cz/
 
     >>> b = GitBrowser(root='http://git.fedorahosted.org/git/elections.git',
     Traceback (most recent call last):
       ...
     AssertionError: Unit checkout path outside of nominal repo checkout path.
-    
     """
-
-
     def __init__(self, root, name=None, branch='master'):
         # If name isn't given, let's take the last part of the root
         # Eg. root = 'http://example.com/foo/baz' -> name='baz'
             [self.path, REPO_PATH]) == REPO_PATH, (
             "Unit checkout path outside of nominal repo checkout path.")
 
-
     @property
     def remote_path(self):
         """Return remote path for cloning."""
         return str(self.root)
 
 
-
     def setup_repo(self):
         """
         Initialize repository for the first time.
-        
+
         Commands used:
         git clone <remote_path> <self.path>
         if branch != master:
         git branch <branch> <remote_branch>
         git co <branch>
-        
         """
 
-        repo = clone(self.remote_path, self.path)
+        try:
+            repo = clone(self.remote_path, self.path)
 
-        # Non master branches need more work:
-        if self.branch != u'master':
-            remote_branch = 'origin/%s' % self.branch
-    
-            repo.branch(self.branch, remote_branch)
-            repo.checkout(self.branch)
-        
-        return repo
+            # Non master branches need more work:
+            if self.branch != u'master':
+                remote_branch = 'origin/%s' % self.branch
+
+                repo.branch(self.branch, remote_branch)
+                repo.checkout(self.branch)
+
+            self.repo = repo
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            self.teardown_repo()
+            raise SetupRepoError(e)
 
 
     def init_repo(self):
         """
         Initialize the ``repo`` variable on the browser.
-        
+
         If local repo exists, use that. If not, clone it.
         """
-        
         try:
             self.repo = repository(self.path)
-        except RepoError:
-            self.repo = self.setup_repo()
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            raise InitRepoError(e)
 
     def _clean_dir(self):
         """
         Clean the local working directory.
-        
+
         Reset any pending changes.
 
         Commands used:
         """
         try:
             self.repo.reset('--hard')
-        except:
-            logger.error(traceback.format_exc())
-            pass
-        
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            raise CleanupRepoError(e)
+
     @need_repo
     def update(self):
         """
         Fully update the local repository.
-        
+
         Commands used:
         git fetch origin
         git reset --hard <revspec>
-        
         """
-        revspec = 'origin/%s' % self.branch
-        self.repo.fetch('origin')
-        self.repo.reset(revspec, hard=True)
+        try:
+            revspec = 'origin/%s' % self.branch
+            self.repo.fetch('origin')
+            self.repo.reset(revspec, hard=True)
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            raise UpdateRepoError(e)
 
     @need_repo
     def get_rev(self, obj=None):
                 return (int(rev, 16),)
             else:
                 return (0,)
-        # TODO: Make it more specific
-        except:
-            logger.error(traceback.format_exc())
-            raise BrowserError()
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            raise RevisionRepoError(e)
 
     @need_repo
     def submit(self, files, msg, user):
                 uploadedfile)
 
             self.repo.add(uploadedfile.targetfile)
-        
+
         user = self._get_user(user).encode('utf-8')
         files = ' '.join(filenames).encode('utf-8')
 
-        self.repo.commit(files, m=msg.encode('utf-8'), author=user)
+        try:
+            self.repo.commit(files, m=msg.encode('utf-8'), author=user)
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            raise CommitRepoError(e)
 
-        self.repo.push('origin', self.branch)
+        try:
+            self.repo.push('origin', self.branch)
+        except Exception, e:
+            if hasattr(e, 'stderr'):
+                e = e.stderr
+            raise PushRepoError(e)
 

File transifex/vcs/lib/types/hg.py

 
 from django.conf import settings
 from codebases.lib import BrowserMixin, BrowserError
+from vcs.lib.exceptions import *
 from vcs.lib.types import need_repo
 from txcommon.log import logger
 
         hg update <branch>
         
         """
-
         try:
             remote_repo, repo = hg.clone(ui, self.remote_path, self.path,
                                          update=True)
-        except RepoError, e:
-            # Remote repo error
-            logger.error(traceback.format_exc())
-            raise BrowserError, e
-
-        return (remote_repo, repo)
+        except Exception, e:
+            raise SetupRepoError(e)
 
 
     def init_repo(self):
         
         try:
             self.repo = hg.repository(ui, self.path)
-            self.remote_repo = hg.repository(ui, self.remote_path)
-        except RepoError:
-            self.remote_repo, self.repo = self.setup_repo()
+        except Exception, e:
+            raise InitRepoError(e)
 
     def _clean_dir(self):
         """
         """
         try:
             hg.clean(self.repo, self.branch, show_stats=False)
-        except:
-            pass
+        except Exception, e:
+            raise CleanupRepoError(e)
 
     @need_repo
     def update(self):
         """
         try:
             commands.pull(self.repo.ui, self.repo, force=True, update=True) 
-        except RepoError, e:
-            logger.error(traceback.format_exc())
-            raise BrowserError, e
+        except Exception, e:
+            raise UpdateRepoError(e)
 
     @need_repo
     def get_rev(self, obj=None):
                 node = ctx.node()
             return (int(node.encode('hex'), 16),)
         except LookupError, e:
-            raise BrowserError(e)
+            raise RevisionRepoError(e)
 
     @need_repo
     def submit(self, files, msg, user):
 
         # Ensure of committing only the right files
         match = cmdutil.match(self.repo, filenames, default=self.path)
-        
-        self.repo.commit(msg.encode('utf-8'), user=user, match=match)
 
-        commands.push(ui, self.repo, self.root, force=False, branch=self.branch)
+        try:
+            self.repo.commit(msg.encode('utf-8'), user=user, match=match)
+        except Exception, e:
+            raise CommitRepoError(e)
 
+        try:
+            # Push might fail without raising an exception and also return
+            # True if it fails. WTF?!
+            # TODO: Discuss it with the mercurial guys.
+            if bool(commands.push(ui, self.repo, self.root,
+                branch=self.branch)):
+                raise Exception("Could not push to the remote repository "
+                    "due a strange bug on Mercurial.")
+        except Exception, e:
+            raise PushRepoError(e)

File transifex/vcs/lib/types/svn.py

 from django.conf import settings
 from vcs.lib import RepoError
 from codebases.lib import BrowserMixin, BrowserError
+from vcs.lib.exceptions import *
 from txcommon.log import logger
 from txcommon.models import Profile
 
     """
     stre = str(e)
     if 'File not found' in stre or 'path not found' in stre:
-        msg = "File not found in repo!"
+        msg = "File not found in repo"
         logger.error(msg)
-        raise RepoError(msg)
+        return RepoError(msg)
     elif 'callback_ssl_server_trust_prompt required' in stre:
         home = os.path.expanduser('~')
         msg = ('HTTPS certificate not accepted. Please ensure that '
             'the proper certificate exists in %s/.subversion/auth '
             'for the user that Transifex is running as.' % home)
         logger.error(msg)
-        raise RepoError('HTTPS certificate not accepted.')
+        return RepoError('HTTPS certificate not accepted')
     elif 'callback_get_login required' in stre:
-        msg = 'Login to the SCM server failed.'
+        msg = 'Login to the SCM server failed'
         logger.error(msg)
-        raise RepoError(msg)
+        return RepoError(msg)
     else:
         logger.error(traceback.format_exc())
-        raise RepoError(default_message or stre)
+        return RepoError(default_message or stre)
 
 class SvnBrowser(BrowserMixin):
-    
     """
     A browser class for Subversion repositories.
-    
+
     Note, that compared to the other Browsers, this one is stateless: It
     doesn't require a self.repo object or something, since each command
     can execute without any preparation. For this reason, init_repo is
     not doing much.
-   
+
     Subversion homepage: http://subversion.tigris.org/
 
     >>> b = SvnBrowser(name='test-svn', branch='trunk',
     Traceback (most recent call last):
     ...
     AssertionError: Unit checkout path outside of nominal repo checkout path.
-    
     """
 
     # We are using the pysvn module.
         self.root = root
         self.name = name
         self.branch = branch
-        
+
         self.path = os.path.normpath(os.path.join(REPO_PATH, name))
         self.path = os.path.abspath(self.path)
         #Test for possible directory traversal
             "Unit checkout path outside of nominal repo checkout path.")
         self.client = pysvn.Client()
         self.client.callback_ssl_server_trust_prompt = _ssl_server_trust_prompt
-        
+        self.client.set_interactive(False)
+
+
     def _authenticate(self):
         """
         Authentication for SVN repositories.
-        
-        Used primarly for https:// repos, which require a username and password
-        for write
-        operations, taken from the configuration settings.
+
+        Used primarly for https:// repos, which require a username and 
+        password for write operations, taken from the configuration settings.
         """
         domain = domain_from_hostname(self.root)
         username, passwd = SVN_CREDENTIALS.get(domain, (None, None))
         self.client.set_default_username(username)
         self.client.set_default_password(passwd)
 
+
     @property
     def remote_path(self):
         """Calculate remote path using the standard svn layout."""
     def setup_repo(self):
         """
         Initialize repository for the first time.
-        
+
         Commands used:
         svn co <remote_path> <self.path>
         """
             self.client.checkout(self.remote_path, self.path,
                 ignore_externals=True)
         except Exception, e:
-            _exception_handler(e, "Checkout from remote repository failed.")
+            raise SetupRepoError(_exception_handler(e))
 
 
     @need_auth
     def init_repo(self):
         """
         A browser repo initialization method, for compatibility reasons.
-        
+
         pysvn runs commands in a stateless fashion, so we don't require an
         initialization phase. The local repo existence check is handled by
         the ``need_repo`` decorator.
     def _clean_dir(self):
         """
         Clean the local working directory.
-        
+
         Commands used:
         svn revert -R .
-        
         """
         try:
             self.client.revert(self.path, recurse=True)
-        except:
-            pass
+        except Exception, e:
+            raise CleanupRepoError(e)
 
     @need_repo
     def update(self):
         """
         Fully update the local repository.
-        
+
         Commands used:
         clean dir
         svn update
         """
-        self._clean_dir()
-        self.client.update(self.path)
+        try:
+            self._clean_dir()
+            self.client.update(self.path)
+        except Exception, e:
+            raise UpdateRepoError(e)
 
     @need_repo
     def get_rev(self, obj=None):
         """
         Get the current revision of the repository or a specific
         object.
-        
+
         Commands used:
         svn info
         """
             else:
                 entry = self.client.info(os.path.join(self.path, obj))
             return (entry.commit_revision.number,)
-        # TODO: Make it more specific
-        except:
-            logger.error(traceback.format_exc())
-            raise BrowserError()
+        except Exception, e:
+            raise RevisionRepoError(e)
 
     @need_repo
     @need_auth
             if not self.client.status(filename)[0]['is_versioned']:
                 self.client.add(filename)
 
-        username = user.username
-
         try:
             # svn ci files
             r = self.client.checkin(absolute_filenames, msg.encode('utf-8'))
+        except Exception, e:
+            raise CommitRepoError(_exception_handler(e))
 
-            try:
-                # Set the author property for the revision
-                self.client.revpropset("svn:author", username, self.root, r)
-            except pysvn.ClientError, e:
-                logger.info("Could not set author property for a svn commit:\n"
-                    "%s" % str(e))
+        try:
+            username = user.username
+            # Set the author property for the revision
+            self.client.revpropset("svn:author", username, self.root, r)
+        except Exception, e:
+            logger.info("Could not set author property for a svn commit:\n"
+                "%s" % str(e))
 
+        try:
             self.update()
-        except pysvn.ClientError, e:
-            _exception_handler(e)
+        except Exception, e:
+            raise PushRepoError(_exception_handler(e))

File transifex/vcs/models.py

     @need_browser
     def prepare(self):
         """Abstraction for the vcsunit.browser.update."""
-        Signal.send(pre_prepare_repo, sender=VcsUnit,
-            instance=self.component)
-        try:
+        Signal.send(pre_prepare_repo, sender=VcsUnit, instance=self.component)
+        if self.last_checkout:
             logger.debug("Preparing repo for vcsunit %s" % self.name)
             self.browser.update()
-            self.last_checkout = datetime.now()
-            self.save()
-        except:
-            logger.debug("Repo update failed. Let's clean up and try again.")
-            # Try once again with a clean local repo.
-            self.teardown()
+        else:
+            logger.debug("Repo is not created yet. Creating it!")
             self.browser.setup_repo()
-            #TODO: Do something if this fails.
-            self.browser.update()
-        Signal.send(post_prepare_repo, sender=VcsUnit,
-            instance=self.component)
+        self.last_checkout = datetime.now()
+        self.save()
+        Signal.send(post_prepare_repo, sender=VcsUnit, instance=self.component)
 
     @need_browser
     def get_files(self, file_filter):

File transifex/vcs/tests/test_bzr.py

 import os
 import unittest
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.core.files.uploadedfile import SimpleUploadedFile
+from vcs.lib.exceptions import *
+from vcs.lib.types.bzr import REPO_PATH
 from vcs.models import VcsUnit
 
-class BzrTestCase(unittest.TestCase):
-    """Test Bzr VCS support.
-    
+class BzrVCS(unittest.TestCase):
+    """
+    Test Bzr VCS support.
+
     Supplementary tests, in addition to doctests.   
     """
+    def setUp(self):
+        self.unittest_scratchdir = os.path.join(settings.SCRATCH_DIR, 
+            'unittest/repos/')
+        self.source_root = '%s/test_repo/bzr' % os.path.split(__file__)[0]
+        self.root = '%s/bzr' % self.unittest_scratchdir
+        os.system('mkdir -p %s; cd %s; cp -rf %s ./' % (
+            self.unittest_scratchdir, self.unittest_scratchdir, 
+            self.source_root))
+        # Needed to be able to commit things
+        os.system("mkdir -p %s/.bzr/repository/upload/" % self.root)
 
-    #TODO: Run the init stuff only when needed.
-    def setUp(self):
-        self.unit = VcsUnit.objects.create(type='bzr',
-            root='%s/test_repo/bzr' % os.path.split(__file__)[0],)
+        self.unit = VcsUnit.objects.create(name='Bzr-Test', type='bzr',
+            root=self.root)
         self.unit._init_browser()
-        self.unit.browser.init_repo()
-        self.unit.browser.update()
-        
+        self.unit.browser.setup_repo()
+
     def tearDown(self):
+        self.unit.browser.teardown_repo()
         self.unit.delete()
-        # Until we use a local repo, let's not delete it after the first run:
-        # self.unit.browser.teardown_repo()
+        os.system('rm -rf %s' % self.unittest_scratchdir)
 
     def test_repo_init(self):
-        """Test correct Bzr repo initialization."""
-        from os import path
-        from vcs.lib.types.bzr import REPO_PATH
-        local_unit_path = path.join(REPO_PATH, self.unit.name)
-        self.assertTrue(path.isdir(local_unit_path))
+        """Test Bzr repo initialization."""
+        local_unit_path = os.path.join(REPO_PATH, self.unit.name)
+        self.assertTrue(os.path.isdir(local_unit_path))
+
+        self.unit.browser.teardown_repo()
+        self.unit.root = '%s/norepo/' % self.unit.root
+        self.unit._init_browser()
+
+        # Trying to setup/checkout a non-existing repository
+        self.assertRaises(SetupRepoError, self.unit.browser.setup_repo)
 
     def test_get_file_contents(self):
-        """Test that Bzr get_file_contents returns correct file size."""
-        #FIXME: This is not the best way to test something like this!
+        """Test get_file_contents checking for the correct file size."""
         self.assertEquals(len(self.unit.browser.get_file_contents(
                           'po/test_repo.pot')), 594)
+
+    def test_get_revision(self):
+        """Test get_rev output."""
+        # Get revision for existing file. It must be a tuple.
+        self.assertEquals(type(self.unit.browser.get_rev('po/pt_BR.po')), 
+            tuple)
+
+        # Get revision for non-existing file
+        self.assertRaises(RevisionRepoError, self.unit.browser.get_rev,
+            'po/foo.po')
+
+    def test_update(self):
+        """Test repository update/pull."""
+        # Pulling from an available repository
+        self.assertTrue(self.unit.browser.update)
+
+        os.system('rm -rf %s/.bzr' % self.root)
+        # Pulling from a non-existing/unavailable repository
+        self.assertRaises(UpdateRepoError, self.unit.browser.update)
+
+    def test_submit(self):
+        """Test submission and pushing."""
+        user, created = User.objects.get_or_create(username='unittest_user')
+        filename = 'po/pt_BR.po'
+        po_content = self.unit.browser.get_file_contents(filename)
+        message = "Testing Submission"
+
+        new_content = "#%s\n%s" % ( message, po_content)
+        submitted_file = SimpleUploadedFile(filename, new_content)
+        submitted_file.targetfile = filename
+        file_dict = {'submitted_file':submitted_file}
+
+        # Committing to the default branch
+        self.unit.browser.submit(file_dict, message, user)
+

File transifex/vcs/tests/test_cvs.py

 import os
 import unittest
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.core.files.uploadedfile import SimpleUploadedFile
+from vcs.lib.exceptions import *
+from vcs.lib.types.cvs import REPO_PATH
 from vcs.models import VcsUnit
 
+class CvsVCS(unittest.TestCase):
+    """
+    Test CVS VCS support.
 
-class CvsTestCase(unittest.TestCase):
-    """Test CVS VCS support.
-    
-    Supplementary tests, in addition to doctests.   
+    Supplementary tests, in addition to doctests.
     """
+    def setUp(self):
+        self.unittest_scratchdir = os.path.join(settings.SCRATCH_DIR,
+            'unittest/repos/')
+        self.source_root = '%s/test_repo/cvs' % os.path.split(__file__)[0]
+        self.root = '%scvs/Test-CVS' % self.unittest_scratchdir
+        os.system('mkdir -p %s; cd %s; cp -rf %s ./' % (
+            self.unittest_scratchdir, self.unittest_scratchdir, 
+            self.source_root))
 
-    #TODO: Run the init stuff only when needed.
-    def setUp(self):
+        self.unit = VcsUnit.objects.create(name="Test-CVS", root=self.root,
+            type='cvs', branch='HEAD')
+        self.unit._init_browser()
+        self.unit.browser.setup_repo()
 
-        self.unit = VcsUnit.objects.create(
-            name="Test-CVS",
-            root='%s/test_repo/cvs/Test-CVS' % os.path.split(__file__)[0],
-            type='cvs')
     def tearDown(self):
+        self.unit.browser.teardown_repo()
         self.unit.delete()
-        # Until we use a local repo, let's not delete it after the first run:
-        # self.unit.browser.teardown_repo()
 
     def test_repo_init(self):
-        """Test correct CVS repo initialization."""
-        from os import path
-        from vcs.lib.types.cvs import REPO_PATH
+        """Test CVS repo initialization."""
+        local_unit_path = os.path.join(REPO_PATH, self.unit.name)
+        self.assertTrue(os.path.isdir(local_unit_path))
+
+        self.unit.browser.teardown_repo()
+        self.unit.root = '%s/norepo/' % self.unit.root
         self.unit._init_browser()
-        self.unit.browser.init_repo()
-        local_unit_path = path.join(REPO_PATH, self.unit.name)
-        self.assertTrue(path.isdir(local_unit_path))
+
+        # Trying to setup/checkout a non-existing repository
+        self.assertRaises(SetupRepoError, self.unit.browser.setup_repo)
 
     def test_get_file_contents(self):
-        """Test that CVS get_file_contents returns correct file size."""
-        #FIXME: This is not the best way to test something like this!
-        self.unit._init_browser()
-        self.unit.browser.init_repo()
-        self.assertEquals(len(self.unit.browser.get_file_contents('po/test_repo.pot')),
-                          594)
+        """Test get_file_contents checking for the correct file size."""
+        self.assertEquals(len(self.unit.browser.get_file_contents(
+            'po/test_repo.pot')), 594)
+
+    def test_get_revision(self):
+        """Test get_rev output."""
+        # Get revision for existing file. It must be a tuple.
+        self.assertEquals(type(self.unit.browser.get_rev('po/pt_BR.po')), 
+            tuple)
+
+        # Get revision for non-existing file
+        self.assertRaises(RevisionRepoError, self.unit.browser.get_rev,
+            'po/foo.po')
+
+    def test_update(self):
+        """Test repository update/pull."""
+        # Pulling from an available repository
+        self.assertTrue(self.unit.browser.update)
+
+    def test_submit(self):
+        """Test submission and pushing."""
+        user, created = User.objects.get_or_create(username='unittest_user')
+        filename = 'po/pt_BR.po'
+        po_content = self.unit.browser.get_file_contents(filename)
+        message = "Testing Submission"
+
+        new_content = "#%s\n%s" % ( message, po_content)
+        submitted_file = SimpleUploadedFile(filename, new_content)
+        submitted_file.targetfile = filename
+        file_dict = {'submitted_file':submitted_file}
+
+        # Committing to the default branch
+        self.unit.browser.submit(file_dict, message, user)

File transifex/vcs/tests/test_git.py

 import os
 import unittest
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.core.files.uploadedfile import SimpleUploadedFile
+from vcs.lib.exceptions import *
+from vcs.lib.types.git import REPO_PATH
 from vcs.models import VcsUnit
-from vcs.lib import RepoError
 
-class GitTestCase(unittest.TestCase):
-    """Test Git VCS support.
-    
-    Supplementary tests, in addition to doctests.   
+class GitVCS(unittest.TestCase):
     """
+    Test Git VCS support.
 
-    #TODO: Run the init stuff only when needed.
+    Supplementary tests, in addition to doctests.
+    """
     def setUp(self):
-        self.unit = VcsUnit.objects.create(
-            name="Test-Git",
-            root='%s/test_repo/git' % os.path.split(__file__)[0],
+        self.unittest_scratchdir = os.path.join(settings.SCRATCH_DIR,
+            'unittest/repos/')
+        self.source_root = '%s/test_repo/git' % os.path.split(__file__)[0]
+        self.root = '%sgit' % self.unittest_scratchdir
+        os.system('mkdir -p %s; cd %s; cp -rf %s ./' % (
+            self.unittest_scratchdir, self.unittest_scratchdir, 
+            self.source_root))
+
+        self.unit = VcsUnit.objects.create(name="Test-Git", root=self.root,
             branch='master', type='git')
+        self.unit._init_browser()
+        self.unit.browser.setup_repo()
+
     def tearDown(self):
         self.unit.delete()
         # Until we use a local repo, let's not delete it after the first run:
         # self.unit.browser.teardown_repo()
 
     def test_repo_init(self):
-        """Test correct Git repo initialization."""
-        from os import path
-        from vcs.lib.types.git import REPO_PATH
+        """Test Git repo initialization."""
+        local_unit_path = os.path.join(REPO_PATH, self.unit.name)
+        self.assertTrue(os.path.isdir(local_unit_path))
+
+        self.unit.browser.teardown_repo()
+        self.unit.root = '%s/norepo/' % self.unit.root
         self.unit._init_browser()
-        self.unit.browser.init_repo()
-        local_unit_path = path.join(REPO_PATH, self.unit.name)
-        self.assertTrue(path.isdir(local_unit_path))
+
+        # Trying to setup/checkout a non-existing repository
+        self.assertRaises(SetupRepoError, self.unit.browser.setup_repo)
 
     def test_get_file_contents(self):
-        """Test that Git get_file_contents returns correct file size."""
-        #FIXME: This is not the best way to test something like this!
+        """Test get_file_contents checking for the correct file size."""
+        self.assertEquals(len(self.unit.browser.get_file_contents(
+            'po/test_repo.pot')), 594)
+
+    def test_get_revision(self):
+        """Test get_rev output."""
+        # Get revision for existing file. It must be a tuple.
+        self.assertEquals(type(self.unit.browser.get_rev('po/pt_BR.po')), 
+            tuple)
+
+        # Get revision for non-existing file
+        self.assertRaises(RevisionRepoError, self.unit.browser.get_rev,
+            'po/foo.po')
+
+    def test_update(self):
+        """Test repository update/pull."""
+        # Pulling from an available repository
+        self.assertTrue(self.unit.browser.update)
+
+        os.system('rm -rf %s/.git' % self.root)
+        # Pulling from a non-existing/unavailable repository
+        self.assertRaises(UpdateRepoError, self.unit.browser.update)
+
+    def test_submit(self):
+        """Test submission and pushing."""
+        user, created = User.objects.get_or_create(username='unittest_user')
+        filename = 'po/pt_BR.po'
+        po_content = self.unit.browser.get_file_contents(filename)
+        message = "Testing Submission"
+
+        new_content = "#%s\n%s" % ( message, po_content)
+        submitted_file = SimpleUploadedFile(filename, new_content)
+        submitted_file.targetfile = filename
+        file_dict = {'submitted_file':submitted_file}
+
+        # Committing to the default branch
+        self.unit.browser.submit(file_dict, message, user)
+
+        # Commiting to a 'foo' branch that doesn't exist
+        self.unit.branch = 'foo'
         self.unit._init_browser()
-        self.unit.browser.init_repo()
-        self.assertEquals(len(self.unit.browser.get_file_contents('po/test_repo.pot')),
-                          594)
+        self.assertRaises(CommitRepoError, self.unit.browser.submit, file_dict,
+            message, user)

File transifex/vcs/tests/test_hg.py

 import os
 import unittest
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.core.files.uploadedfile import SimpleUploadedFile
+from vcs.lib.exceptions import *
+from vcs.lib.types.hg import REPO_PATH
 from vcs.models import VcsUnit
 
-class HgTestCase(unittest.TestCase):
-    """Test Hg VCS support.
-    
-    Supplementary tests, in addition to doctests.   
+class HgVCS(unittest.TestCase):
     """
+    Test Hg VCS support.
 
-    #TODO: Run the init stuff only when needed.
+    Supplementary tests, in addition to doctests.
+    """
     def setUp(self):
-        self.root='%s/test_repo/hg' % os.path.split(__file__)[0]
+        self.unittest_scratchdir = os.path.join(settings.SCRATCH_DIR, 
+            'unittest/repos/')
+        self.source_root = '%s/test_repo/hg' % os.path.split(__file__)[0]
+        self.root = '%s/hg' % self.unittest_scratchdir
+        os.system('mkdir -p %s; cd %s; cp -rf %s ./' % (
+            self.unittest_scratchdir, self.unittest_scratchdir, 
+            self.source_root))
         os.system('cd %s; hg init; hg add *; hg commit -m "Init repo"'
                   % self.root)
-        self.unit = VcsUnit.objects.create(
-            name="Test-HG",
-            root=self.root,
-            branch='tip', type='hg')
+
+        self.unit = VcsUnit.objects.create(name="Test-HG", root=self.root,
+            type='hg')
+        self.unit._init_browser()
+        self.unit.browser.setup_repo()
+
     def tearDown(self):
+        self.unit.browser.teardown_repo()
         self.unit.delete()
-        os.system('rm -rf %s/.hg' % self.root)
-        # Until we use a local repo, let's not delete it after the first run:
-        # self.unit.browser.teardown_repo()
+        os.system('rm -rf %s' % self.root)
 
     def test_repo_init(self):
-        """Test correct Hg repo initialization."""
-        from os import path
-        from vcs.lib.types.hg import REPO_PATH
+        """Test Hg repository initialization."""
+        local_unit_path = os.path.join(REPO_PATH, self.unit.name)
+        self.assertTrue(os.path.isdir(local_unit_path))
+
+        self.unit.browser.teardown_repo()
+        self.unit.root = '%s/norepo/' % self.unit.root
         self.unit._init_browser()
-        self.unit.browser.init_repo()
-        local_unit_path = path.join(REPO_PATH, self.unit.name)
-        self.assertTrue(path.isdir(local_unit_path))
+
+        # Trying to setup/checkout a non-existing repository
+        self.assertRaises(SetupRepoError, self.unit.browser.setup_repo)
 
     def test_get_file_contents(self):
-        """Test that Hg get_file_contents returns correct file size."""
-        #FIXME: This is not the best way to test something like this!
+        """Test get_file_contents checking for the correct file size."""
+        self.assertEquals(len(
+            self.unit.browser.get_file_contents('po/test_repo.pot')), 594)
+
+    def test_get_revision(self):
+        """Test get_rev output."""
+        # Get revision for existing file. It must be a tuple.
+        self.assertEquals(type(self.unit.browser.get_rev('po/pt_BR.po')), 
+            tuple)
+
+        # Get revision for non-existing file
+        self.assertRaises(RevisionRepoError, self.unit.browser.get_rev,
+            'po/foo.po')
+
+    def test_update(self):
+        """Test repository update/pull."""
+        # Pulling from an available repository
+        self.assertTrue(self.unit.browser.update)
+
+        os.system('rm -rf %s/.hg' % self.root)
+        # Pulling from a non-existing/unavailable repository
+        self.assertRaises(UpdateRepoError, self.unit.browser.update)
+
+    def test_submit(self):
+        """Test submission and pushing."""
+        user, created = User.objects.get_or_create(username='unittest_user')
+        filename = 'po/pt_BR.po'
+        po_content = self.unit.browser.get_file_contents(filename)
+        message = "Testing Submission"
+
+        new_content = "#%s\n%s" % ( message, po_content)
+        submitted_file = SimpleUploadedFile(filename, new_content)
+        submitted_file.targetfile = filename
+        file_dict = {'submitted_file':submitted_file}
+
+        # Committing to the default branch
+        self.unit.browser.submit(file_dict, message, user)
+
+        # Commiting to a 'foo' branch that doesn't exist
+        self.unit.branch = 'foo'
         self.unit._init_browser()
-        self.unit.browser.init_repo()
-        self.assertEquals(len(self.unit.browser.get_file_contents('po/test_repo.pot')),
-                          594)
+        self.assertRaises(PushRepoError, self.unit.browser.submit, file_dict,
+            message, user)

File transifex/vcs/tests/test_repo/svn/data/trunk/po/pt_BR.po

-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: im-chooser\n"
-"Report-Msgid-Bugs-To: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-"X-Poedit-Language: Portuguese\n"
-"X-Poedit-Country: BRAZIL\n"
-
-msgid "Projects"
-msgstr "Projetos"

File transifex/vcs/tests/test_repo/svn/data/trunk/po/test_repo.pot

-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-10-14 11:50+0900\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-msgid "Projects"
-msgstr ""

File transifex/vcs/tests/test_repo/svn/trunk/po/pt_BR.po

+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: im-chooser\n"
+"Report-Msgid-Bugs-To: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Poedit-Language: Portuguese\n"
+"X-Poedit-Country: BRAZIL\n"
+
+msgid "Projects"
+msgstr "Projetos"

File transifex/vcs/tests/test_repo/svn/trunk/po/test_repo.pot

+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-10-14 11:50+0900\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Projects"
+msgstr ""

File transifex/vcs/tests/test_svn.py

 import os
 import unittest
-from txcommon.commands import (run_command, CommandError)
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.core.files.uploadedfile import SimpleUploadedFile
+from vcs.lib.exceptions import *
 from vcs.lib.types.svn import REPO_PATH 
 from vcs.models import VcsUnit
 
-class SvnTestCase(unittest.TestCase):
+class SvnVCS(unittest.TestCase):
     """Test Subversion VCS support.
 
     Supplementary tests, in addition to doctests.   
     """
     def setUp(self):
-        # Base location of the SVN repo test stuff.
-        base_path = '%s/test_repo/svn' % os.path.split(__file__)[0]
+        # Temp location for the SVN testing repository
+        self.unittest_scratchdir = os.path.join(settings.SCRATCH_DIR,
+            'unittest/repos/svn/')
+
+        # Path to the data that should be used to create the svn repo.
+        self.repo_data = '%s/test_repo/svn' % os.path.split(__file__)[0]
 
         # Base repo path.
-        self.svn_repo = '%s/repo' % base_path
-
-        # Path to the data that should be used to create the svn repo.
-        self.repo_data = '%s/data' % base_path
+        self.svn_repo = '%srepo' % self.unittest_scratchdir
 
         # Root URL of the repo. Used in checkouts for example.
         self.root = 'file://%s/repo_data' % self.svn_repo
 
+        os.system('mkdir -p %s' % self.unittest_scratchdir)
+
         # Creating a SVN repo and importing some data into a project repo.
-        run_command("rm -rf %s" % self.svn_repo )
-        run_command('svnadmin create %s' % self.svn_repo)
-        run_command('svn import %s %s -m "Initial"' % (self.repo_data,
+        os.system("rm -rf %s" % self.svn_repo )
+        os.system('svnadmin create %s' % self.svn_repo)
+        os.system('svn import %s %s -m "Initial"' % (self.repo_data,
             self.root))
 
+        # Enabling pre-revprop-change hook
+        #hook_path = '%s/hooks/pre-revprop-change' % self.svn_repo
+        #os.system('mv %s.tmpl %s' % (hook_path, hook_path))
+        #os.system('chmod +x %s' % hook_path)
+
         self.unit = VcsUnit.objects.create(name="Test-SVN", root=self.root,
             branch='trunk', type='svn')
         self.unit._init_browser()
         self.unit.browser.setup_repo()
-        self.unit.browser.update()
 
     def tearDown(self):
         self.unit.browser.teardown_repo()
         self.unit.delete()
         # Removing temp SVN repo
-        run_command("rm -rf %s" % self.svn_repo )
+        os.system("rm -rf %s" % self.unittest_scratchdir )
 
     def test_repo_init(self):
-        """Test correct SVN repo initialization."""
-        self.assertTrue(os.path.isdir(os.path.join(REPO_PATH, self.unit.name)))
+        """Test Svn repo initialization."""
+        local_unit_path = os.path.join(REPO_PATH, self.unit.name)
+        self.assertTrue(os.path.isdir(local_unit_path))
+
+        self.unit.browser.teardown_repo()
+        self.unit.root = '%s/norepo/' % self.unit.root
+        self.unit._init_browser()
+
+        # Trying to setup/checkout a non-existing repository
+        self.assertRaises(SetupRepoError, self.unit.browser.setup_repo)
 
     def test_get_file_contents(self):
-        """Test that SVN get_file_contents returns correct file size."""
+        """Test get_file_contents checking for the correct file size."""
         self.assertEquals(len(self.unit.browser.get_file_contents(
             'po/test_repo.pot')), 594)
+
+    def test_get_revision(self):
+        """Test get_rev output."""
+        # Get revision for existing file. It must be a tuple.
+        self.assertEquals(type(self.unit.browser.get_rev('po/pt_BR.po')), 
+            tuple)
+
+        # Get revision for non-existing file
+        self.assertRaises(RevisionRepoError, self.unit.browser.get_rev,
+            'po/foo.po')
+
+    def test_update(self):
+        """Test repository update/pull."""
+        # Pulling from an available repository
+        self.assertTrue(self.unit.browser.update)
+
+    def test_submit(self):
+        """Test submission and pushing."""
+        user, created = User.objects.get_or_create(username='unittest_user')
+        filename = 'po/pt_BR.po'
+        po_content = self.unit.browser.get_file_contents(filename)
+        message = "Testing Submission"
+
+        new_content = "#%s\n%s" % ( message, po_content)
+        submitted_file = SimpleUploadedFile(filename, new_content)
+        submitted_file.targetfile = filename
+        file_dict = {'submitted_file':submitted_file}
+
+        # Committing to the default branch
+        self.unit.browser.submit(file_dict, message, user)