Ejemplo n.º 1
0
 def get_bug(self, bug, attachments=True, comments=True, history=True):
     """Fetch Bug ``bug``."""
     tmp = {'attachmentdata': attachments, 'comments': comments,
            'history': history}
     params = dict((k, int(v)) for k, v in tmp.items())
     url = urljoin(API_ROOT, 'bug/%s?%s' % (bug, self.qs(**params)))
     return Bug.get(url)
Ejemplo n.º 2
0
    def _attach(self, bug_id, filename, description, is_patch=False,
               reviewer=None, content_type='text/plain'):
        """Create a new attachment."""
        fields = {'data': base64.b64encode(open(filename).read()),
                  'encoding': 'base64',
                  'file_name': filename,
                  'content_type': content_type,
                  'description': description,
                  'is_patch': is_patch,
                  }

        if reviewer is not None:
            fields['flags'] = [Flag(type_id=REVIEW, status='?',
                                    requestee=User(name=reviewer))]

        url = urljoin(API_ROOT, 'bug/%s/attachment?%s' % (bug_id, self.qs()))
        return Attachment(**fields).post_to(url)
Ejemplo n.º 3
0
 def _comment(self, bug_id, comment):
     """Create a new comment."""
     url = urljoin(self.API_ROOT, 'bug/%s/comment?%s' % (bug_id, self.qs()))
     return Comment(text=comment).post_to(url)
Ejemplo n.º 4
0
 def get_bug_list(self, params={}):
     url = url = urljoin(self.API_ROOT, 'bug/?%s' % (self.qs(**params)))
     return BugSearch.get(url, http=self.http).bugs
Ejemplo n.º 5
0
    def get_bug(self, bug, include_fields='_default,token,cc,keywords,whiteboard', exclude_fields=None, params={}):
        params['include_fields'] = include_fields
        params['exclude_fields'] = exclude_fields

        url = urljoin(self.API_ROOT, 'bug/%s?%s' % (bug, self.qs(**params)))
        return Bug.get(url, http=self.http)
Ejemplo n.º 6
0
  def reproduceBug(self, bug, tipBranch=None, tipBranchRev=None):
    bugnum = str(bug.id)

    # Determine comment to look at and revision
    testCommentIdx = 0
    rev = None

    if (tipBranch != None and tipBranchRev != None):
        rev = tipBranchRev

    if (bug.whiteboard != None):
      ret = re.compile('\[jsbugmon:([^\]]+)\]').search(bug.whiteboard)
      if (ret != None and ret.groups > 1):
        wbOpts = ret.group(1).split(",")
        for opt in wbOpts:
          if (opt.find("=") > 0):
            (cmd,param) = opt.split('=')
            if (cmd != None and param != None):
              if (cmd == "origRev" and rev == None):
                rev = param
              elif (cmd == "testComment" and param.isdigit()):
                testCommentIdx = int(param)

    # Look for the first comment
    comment = bug.comments[testCommentIdx] if len(bug.comments) > testCommentIdx else None

    if (comment == None):
      raise BugException("Error: Specified bug does not have any comments")

    text = comment.text

    # Isolate revision to test for
    if (rev == None):
      rev = self.extractRevision(text)
    else:
      # Sanity check of the revision
      rev = self.extractRevision(rev)

    if (rev == None):
      raise BugException("Error: Failed to isolate original revision for test")


    buildFlags = []

    checkFlags = ["--enable-more-deterministic", "--enable-simulator=arm", "--enable-arm-simulator", "--enable-debug", "--disable-debug", "--enable-optimize", "--disable-optimize"]

    for flag in checkFlags:
      if (re.search(flag + "[^-a-zA-Z0-9]", text) != None):
        buildFlags.append(flag)

    viableOptsList = []
    opts = []

    for opt in self.allowedOpts:
      if (text.find(opt) != -1):
        opts.append(opt)

    viableOptsList.append(opts)

    print "Extracted options: %s" % (' '.join(opts))

    # Special hack for flags that changed
    if "--ion-parallel-compile=off" in opts:
      optsCopy = []
      for opt in opts:
        if opt == "--ion-parallel-compile=off":
          optsCopy.append("--ion-offthread-compile=off")
        else:
          optsCopy.append(opt)
      viableOptsList.append(optsCopy)
    
    if (bug.version == "Trunk"):
      reponame = "mozilla-central"
    elif (bug.version == "Other Branch"):
      reponame = "ionmonkey"
    else:
      raise BugException("Error: Unsupported branch \"" + bug.version + "\" required by bug")

    # Default to using the bug.version field as repository specifier
    repoDir = os.path.join(self.repoBase, reponame)

    # If told to use a different tipBranch, use that for tip testing
    if (tipBranch == None):
      tipBranch = reponame

    tipRepoDir = os.path.join(self.repoBase, tipBranch)

    # If we are given a specific revision even for testing, then use
    # the tipBranch for all testing, including initial reproduction
    if (tipBranchRev != None):
      repoDir = tipRepoDir

    print "Using repository at %s with revision %s for initial reproduction" % (repoDir, rev)
    print "Using repository at %s with tip revision for testing" % (tipRepoDir)

    arch = None
    archList = None
    if (bug.platform == "x86_64"):
      arch = "64"
    elif (bug.platform == "x86"):
      arch = "32"
    elif (bug.platform == "All"):
      arch = "64"
      archList = [ "64", "32" ] # TODO: Detect native platform here
      
      # When auto-detecting, avoid using ARM simulator for now
      if "--enable-simulator=arm" in buildFlags:
        buildFlags.remove("--enable-simulator=arm")
    elif (bug.platform == "ARM"):
      arch = "32"
      buildFlags.append("--enable-simulator=arm")
    else:
      raise BugException("Error: Unsupported architecture \"" + bug.platform + "\" required by bug")

    # We need at least some shell to extract the test from the bug, 
    # so we build a debug shell here already
    try:
      (testShell, testRev) = self.getShell("cache/", arch, "dbg", 0, rev, False, repoDir, buildFlags)
    except Exception:
      trace = sys.exc_info()[2]
      raise InternalException("Failed to compile tip shell (toolchain broken?)"), None, trace

    # If the file already exists, then we can reuse it
    if testCommentIdx > 0:
      testFile = "bug" + str(bugnum) + "-" + str(testCommentIdx) + ".js"
    else:
      testFile = "bug" + str(bugnum) + ".js"

    if (os.path.exists(testFile)):
      print "Using existing (cached) testfile " + testFile
    else:

      # We need to detect where our test is.
      blocks = text.split("\n\n")
      found = False
      cnt = 0
      for i,block in enumerate(blocks):
        # Write our test to file
        outFile = open(testFile, "w")
        outFile.write(block)
        outFile.close()
        print "Testing syntax with shell %s" % testShell
        (err, ret) = testBinary(testShell, testFile, [], 0, timeout=30)

        if (err.find("SyntaxError") < 0):
          # We have found the test (or maybe only the start of the test)
          # Try adding more code until we hit an error or are out of
          # blocks.
          oldBlock = block
          curBlock = block
          for j,block in enumerate(blocks):
            if j > i:
              curBlock = curBlock + "\n" + block
              # Write our test to file
              outFile = open(testFile, "w")
              outFile.write(curBlock)
              outFile.close()
              (err, ret) = testBinary(testShell, testFile, [], 0, timeout=30)
              if (err.find("SyntaxError") >= 0):
                # Too much, write oldBlock and break
                outFile = open(testFile, "w")
                outFile.write(oldBlock)
                outFile.close()
                break
              else:
                oldBlock = curBlock

          found = True
          print "Isolated possible testcase starting in textblock " + str(cnt)
          break
        cnt += 1
      if not found:
        # First try to find a suitable attachment
        attachments = [a for a in bug.attachments if not bool(int(a.is_obsolete))]
        for attachment in attachments:
          # Seriously, we don't need anything larger than 512kb here^^
          if (attachment.size <= 512*1024):
            # Refetch attachment with content
            url = urljoin(self.apiroot, 'attachment/%s/?%s&attachmentdata=1' % (attachment.id, self.bz.qs()))
            attachment = attachment.get(url)

            try:
              rawData = base64.b64decode(attachment.data)
              # Write our data to file
              outFile = open(testFile, "w")
              outFile.write(rawData)
              outFile.close()
              (err, ret) = testBinary(testShell, testFile, [], 0, timeout=30)
              if (err.find("SyntaxError") < 0):
                # Found something that looks like JS :)
                found = True
                break
            except TypeError:
              pass

        # If we still haven't found any test, give up here...
        if not found:
          # Ensure we don't cache the wrong test
          os.remove(testFile)
          raise BugException("Error: Failed to isolate test from comment")

    (oouterr, oret) = (None, None)
    (origShell, origRev) = (None, None)

    # If we have an exact architecture, we will only test that
    if (archList == None):
      archList = [ arch ]

    for compileType in ['dbg', 'opt']:
      for archType in archList:
        try:
          (origShell, origRev) = self.getShell("cache/", archType, compileType, 0, rev, False, repoDir, buildFlags)
        except Exception:
          # Unlike compilation failures on tip, we must not ignore compilation failures with the original
          # revision, as it could mean that the bug was filed with a broken revision.
          raise BugException("Error: Failed to compile specified revision %s (maybe try another?)" % rev)

        for opts in viableOptsList:
          (oouterr, oret) = testBinary(origShell, testFile, opts , 0, verbose=self.options.verbose, timeout=30)
          if (oret < 0):
            break

        # If we reproduced with one arch, then we don't need to try the others
        if (oret < 0):
          break

        print ""

      # If we reproduced with dbg, then we don't need to try opt
      if (oret < 0):
        break

    # Check if we reproduced at all (dbg or opt)
    if (oret < 0):
      print ""
      print "Successfully reproduced bug (exit code " + str(oret) + ") on original revision " + rev + ":"
      errl = oouterr.split("\n")
      if len(errl) > 2: errl = errl[-2:]
      for err in errl:
        print err

      # Try running on tip now
      print "Testing bug on tip..."

      # Update to tip and cache result:
      updated = False
      if not self.tipRev.has_key(tipRepoDir):
        # If we don't know the tip revision for this branch, update and get it
        self.tipRev[tipRepoDir] = self.hgUpdate(tipRepoDir)
        updated = True
      
      try:
        (tipShell, tipRev) = self.getShell("cache/", archType, compileType, 0, self.tipRev[tipRepoDir], updated, tipRepoDir, buildFlags)
      except Exception:
        trace = sys.exc_info()[2]
        raise InternalException("Failed to compile tip shell (toolchain broken?)"), None, trace

      tipOpts = None
      for opts in viableOptsList:
        (touterr, tret) = testBinary(tipShell, testFile, opts , 0, verbose=self.options.verbose, timeout=30)
        if (tret < 0):
          tipOpts = opts
          break

      if (tret < 0):
        if (tret == oret):
          if (opts == tipOpts):
            print "Result: Bug still reproduces"
            return BugMonitorResult(reponame, rev, self.tipRev[tipRepoDir], opts, testFile, archType, compileType, buildFlags, BugMonitorResult.statusCodes.REPRODUCED_TIP)
          else:
            print "Result: Bug still reproduces, but with different options: " + " ".join(tipOpts) # TODO need another code here in the future
            return BugMonitorResult(reponame, rev, self.tipRev[tipRepoDir], opts, testFile, archType, compileType, buildFlags, BugMonitorResult.statusCodes.REPRODUCED_TIP)
        else:
          # Unlikely but possible, switched signal
          print "Result: Bug now reproduces with signal " + str(tret) + " (previously " + str(oret) + ")"
          return BugMonitorResult(reponame, rev, self.tipRev[tipRepoDir], opts, testFile, archType, compileType, buildFlags, BugMonitorResult.statusCodes.REPRODUCED_SWITCHED)
      else:
        print "Result: Bug no longer reproduces"
        return BugMonitorResult(reponame, rev, self.tipRev[tipRepoDir], opts, testFile, archType, compileType, buildFlags, BugMonitorResult.statusCodes.REPRODUCED_FIXED)
    else:
      print "Error: Failed to reproduce bug on original revision"
      #return BugMonitorResult(reponame, rev, self.tipRev[tipRepoDir], opts, testFile, archType, compileType, buildFlags, BugMonitorResult.statusCodes.FAILED)
      return BugMonitorResult(reponame, rev, None, opts, testFile, archType, compileType, buildFlags, BugMonitorResult.statusCodes.FAILED)
Ejemplo n.º 7
0
 def postComment(self, bugnum, comment):
   url = urljoin(self.apiroot, 'bug/%s/comment?%s' % (bugnum, self.bz.qs()))
   return Comment(text=comment).post_to(url)
Ejemplo n.º 8
0
 def get_bugs(self, **kwargs):
     url = urljoin(self.API_ROOT, 'bug/?%s' % (self.qs(**kwargs)))
     return DashBugSearch.get(url, http=self.http).bugs
Ejemplo n.º 9
0
  def reproduceBug(self, bug, tipBranch=None):
    # Fetch the bug
    #bug = self.fetchBug(bugnum)
    bugnum = str(bug.id)

    # Determine comment to look at and revision
    testCommentIdx = 0
    rev = None

    if (bug.whiteboard != None):
      ret = re.compile('\[jsbugmon:([^\]]+)\]').search(bug.whiteboard)
      if (ret != None and ret.groups > 1):
        wbOpts = ret.group(1).split(",")
        for opt in wbOpts:
          if (opt.find("=") > 0):
            (cmd,param) = opt.split('=')
            if (cmd != None and param != None):
              if (cmd == "origRev"):
                rev = param
              elif (cmd == "testComment" and param.isdigit()):
                testCommentIdx = int(param)

    # Look for the first comment
    comment = bug.comments[testCommentIdx] if len(bug.comments) > testCommentIdx else None

    if (comment == None):
      raise BugException("Error: Specified bug does not have any comments")

    text = comment.text

    # Isolate revision to test for
    if (rev == None):
      rev = self.extractRevision(text)
    else:
      # Sanity check of the revision
      rev = self.extractRevision(rev)

    if (rev == None):
      raise BugException("Error: Failed to isolate original revision for test")

    opts = None
    tipOpts = None

    # Isolate options for testing, not explicitly instructed to guess
    if not self.options.guessopts:
      opts = self.extractOptions(text)
      if (opts == None):
        print "Warning: No options found, will try to guess"

    arch = None
    archList = None
    if (bug.platform == "x86_64"):
      arch = "64"
    elif (bug.platform == "x86"):
      arch = "32"
    elif (bug.platform == "All"):
      arch = "64"
      archList = [ "64", "32" ] # TODO: Detect native platform here
    else:
      raise BugException("Error: Unsupported architecture \"" + bug.platform + "\" required by bug")

    if (bug.version == "Trunk"):
      reponame = "mozilla-central"
    elif (bug.version == "Other Branch"):
      reponame = "ionmonkey"
    else:
      raise BugException("Error: Unsupported branch \"" + bug.version + "\" required by bug")

    if (tipBranch == None):
      tipBranch = reponame

    print "Repobase: " + self.repoBase
    print "Reponame: " + reponame
    repoDir = os.path.join(self.repoBase, reponame)
    tipRepoDir = os.path.join(self.repoBase, tipBranch)

    # We need at least some shell to extract the test from the bug, 
    # so we build a debug tip shell here already
    updated = False
    if not self.tipRev.has_key(repoDir):
      # If we don't know the tip revision for this branch, update and get it
      self.tipRev[repoDir] = self.hgUpdate(repoDir)
      updated = True
    (tipShell, tipRev) = self.getShell("cache/", arch, "dbg", 0, self.tipRev[repoDir], updated, repoDir)

    # If the file already exists, then we can reuse it
    if testCommentIdx > 0:
      testFile = "bug" + str(bugnum) + "-" + str(testCommentIdx) + ".js"
    else:
      testFile = "bug" + str(bugnum) + ".js"

    if (os.path.exists(testFile)):
      print "Using existing (cached) testfile " + testFile
    else:

      # We need to detect where our test is.
      blocks = text.split("\n\n")
      found = False
      cnt = 0
      for i,block in enumerate(blocks):
        # Write our test to file
        outFile = open(testFile, "w")
        outFile.write(block)
        outFile.close()
        (err, ret) = testBinary(tipShell, testFile, [], 0, timeout=30)

        if (err.find("SyntaxError") < 0):
          # We have found the test (or maybe only the start of the test)
          # Try adding more code until we hit an error or are out of
          # blocks.
          oldBlock = block
          curBlock = block
          for j,block in enumerate(blocks):
            if j > i:
              curBlock = curBlock + "\n" + block
              # Write our test to file
              outFile = open(testFile, "w")
              outFile.write(curBlock)
              outFile.close()
              (err, ret) = testBinary(tipShell, testFile, [], 0, timeout=30)
              if (err.find("SyntaxError") >= 0):
                # Too much, write oldBlock and break
                outFile = open(testFile, "w")
                outFile.write(oldBlock)
                outFile.close()
                break
              else:
                oldBlock = curBlock

          found = True
          print "Isolated possible testcase starting in textblock " + str(cnt)
          break
        cnt += 1
      if not found:
        # First try to find a suitable attachment
        attachments = [a for a in bug.attachments if not bool(int(a.is_obsolete))]
        for attachment in attachments:
          # Seriously, we don't need anything larger than 512kb here^^
          if (attachment.size <= 512*1024):
            # Refetch attachment with content
            url = urljoin(self.apiroot, 'attachment/%s/?%s&attachmentdata=1' % (attachment.id, self.bz.qs()))
            attachment = attachment.get(url)

            try:
              rawData = base64.b64decode(attachment.data)
              # Write our data to file
              outFile = open(testFile, "w")
              outFile.write(rawData)
              outFile.close()
              (err, ret) = testBinary(tipShell, testFile, [], 0, timeout=30)
              if (err.find("SyntaxError") < 0):
                # Found something that looks like JS :)
                found = True
                break
            except TypeError:
              pass

        # If we still haven't found any test, give up here...
        if not found:
          # Ensure we don't cache the wrong test
          os.remove(testFile)
          raise BugException("Error: Failed to isolate test from comment")

    (oouterr, oret) = (None, None)
    (origShell, origRev) = (None, None)

    # If we have an exact architecture, we will only test that
    if (archList == None):
      archList = [ arch ]

    for compileType in ['dbg', 'opt']:
      for archType in archList:
        # Update to tip and cache result:
        updated = False
        if not self.tipRev.has_key(tipRepoDir):
          # If we don't know the tip revision for this branch, update and get it
          self.tipRev[tipRepoDir] = self.hgUpdate(tipRepoDir)
          updated = True
      
        (tipShell, tipRev) = self.getShell("cache/", archType, compileType, 0, self.tipRev[tipRepoDir], updated, tipRepoDir)
        (origShell, origRev) = self.getShell("cache/", archType, compileType, 0, rev, False, repoDir)


        if (opts != None):
          (oouterr, oret) = testBinary(origShell, testFile, opts , 0, verbose=self.options.verbose, timeout=30)
        else:
          print "Guessing options...",
          guessopts = self.guessopts[reponame]
          for opt in guessopts:
            topts = []
            if opt == None:
              print " no options",
            else:
              print " " + opt,
              topts = opt.split(' ')
            (oouterr, oret) = testBinary(origShell, testFile, topts , 0, verbose=self.options.verbose, timeout=30)
            if (oret < 0):
              opts = topts
              break;

        # If we reproduced with one arch, then we don't need to try the others
        if (oret < 0):
          break;

        print ""

      # If we reproduced with dbg, then we don't need to try opt
      if (oret < 0):
        break;

    # Check if we reproduced at all (dbg or opt)
    if (oret < 0):
      print ""
      print "Successfully reproduced bug (exit code " + str(oret) + ") on original revision " + rev + ":"
      errl = oouterr.split("\n")
      if len(errl) > 2: errl = errl[-2:]
      for err in errl:
        print err

      if (opts != None):
        # Try running on tip now
        print "Testing bug on tip..."
        if self.options.guessopts:
          guessopts = self.guessopts[reponame]
          for opt in guessopts:
            tipOpts = []
            if opt == None:
              print " no options",
            else:
              print " " + opt,
              tipOpts = opt.split(' ')
            (touterr, tret) = testBinary(tipShell, testFile, tipOpts , 0, verbose=self.options.verbose, timeout=30)
            if (tret < 0):
              break;
        else:
          tipOpts = opts
          (touterr, tret) = testBinary(tipShell, testFile, tipOpts , 0, verbose=self.options.verbose, timeout=30)
      else:
        print ""

      if (tret < 0):
        if (tret == oret):
          if (opts == tipOpts):
            print "Result: Bug still reproduces"
            return BugMonitorResult(reponame, rev, self.tipRev[tipRepoDir], opts, testFile, archType, compileType, BugMonitorResult.statusCodes.REPRODUCED_TIP)
          else:
            print "Result: Bug still reproduces, but with different options: " + " ".join(tipOpts) # TODO need another code here in the future
            return BugMonitorResult(reponame, rev, self.tipRev[tipRepoDir], opts, testFile, archType, compileType, BugMonitorResult.statusCodes.REPRODUCED_TIP)
        else:
          # Unlikely but possible, switched signal
          print "Result: Bug now reproduces with signal " + str(tret) + " (previously " + str(oret) + ")"
          return BugMonitorResult(reponame, rev, self.tipRev[tipRepoDir], opts, testFile, archType, compileType, BugMonitorResult.statusCodes.REPRODUCED_SWITCHED)
      else:
        print "Result: Bug no longer reproduces"
        return BugMonitorResult(reponame, rev, self.tipRev[tipRepoDir], opts, testFile, archType, compileType, BugMonitorResult.statusCodes.REPRODUCED_FIXED)
    else:
      print "Error: Failed to reproduce bug on original revision"
      return BugMonitorResult(reponame, rev, self.tipRev[tipRepoDir], opts, testFile, archType, compileType, BugMonitorResult.statusCodes.FAILED)
Ejemplo n.º 10
0
 def postComment(self, bugnum, comment):
   url = urljoin(self.apiroot, 'bug/%s/comment?%s' % (bugnum, self.bz.qs()))
   return Comment(text=comment).post_to(url)