Runs self.find_pulls() *and* processes the pull requests unit tests,
        status updates and wiki page creations.

        :arg expected: for unit testing the output results that would be returned
          from running the tests in real time.

def process_pulls(self, testpulls=None, testarchive=None, expected=None):
        """Runs self.find_pulls() *and* processes the pull requests unit tests,
        status updates and wiki page creations.

        :arg expected: for unit testing the output results that would be returned
          from running the tests in real time.
        """
        from datetime import datetime
        pulls = self.find_pulls(None if testpulls is None else testpulls.values())
        for reponame in pulls:
            for pull in pulls[reponame]:
                try:
                    archive = self.archive[pull.repokey]
                    if pull.snumber in archive:
                        #We pass the archive in so that an existing staging directory (if
                        #different from the configured one) can be cleaned up if the previous
                        #attempt failed and left the file system dirty.
                        pull.init(archive[pull.snumber])
                    else:
                        pull.init({})
                        
                    if self.testmode and testarchive is not None:
                        #Hard-coded start times so that the model output is reproducible
                        if pull.number in testarchive[pull.repokey]:
                            start = testarchive[pull.repokey][pull.number]["start"]
                        else:
                            start = datetime(2015, 4, 23, 13, 8)
                    else:
                        start = datetime.now()
                    archive[pull.snumber] = {"success": False, "start": start,
                                             "number": pull.number, "stage": pull.repodir,
                                             "completed": False, "finished": None}
                    #Once a local staging directory has been initialized, we add the sha
                    #signature of the pull request to our archive so we can track the rest
                    #of the testing process. If it fails when trying to merge the head of
                    #the pull request, the exception block should catch it and email the
                    #owner of the repo.
                    #We need to save the state of the archive now in case the testing causes
                    #an unhandled exception.
                    self._save_archive()

                    pull.begin()
                    self.cron.email(pull.repo.name, "start", self._get_fields("start", pull), self.testmode)
                    pull.test(expected[pull.number])
                    pull.finalize()

                    #Update the status of this pull request on the archive, save the archive
                    #file in case the next pull request throws an unhandled exception.
                    archive[pull.snumber]["completed"] = True
                    archive[pull.snumber]["success"] = abs(pull.percent - 1) < 1e-12

                    #This if block looks like a mess; it is necessary so that we can easily
                    #unit test this processing code by passing in the model outputs etc. that should
                    #have been returned from running live.
                    if (self.testmode and testarchive is not None and
                        pull.number in testarchive[pull.repokey] and
                        testarchive[pull.repokey][pull.number]["finished"] is not None):
                        archive[pull.snumber]["finished"] = testarchive[pull.repokey][pull.number]["finished"]
                    elif self.testmode:
                        archive[pull.snumber]["finished"] = datetime(2015, 4, 23, 13, 9)
                    else:
                        #This single line could replace the whole if block if we didn't have
                        #unit tests integrated with the main code.
                        archive[pull.snumber]["finished"] = datetime.now()
                    self._save_archive()

                    #We email after saving the archive in case the email server causes exceptions.
                    if archive[pull.snumber]["success"]:
                        key = "success"
                    else:
                        key = "failure"
                    self.cron.email(pull.repo.name, key, self._get_fields(key, pull), self.testmode)
                except:
                    import sys, traceback
                    e = sys.exc_info()
                    errmsg = '\n'.join(traceback.format_exception(e[0], e[1], e[2]))
                    err(errmsg)
                    self.cron.email(pull.repo.name, "error", self._get_fields("error", pull, errmsg),
                                    self.testmode)