def bisect(self, good, bad, testcondition=None, args_for_condition=[]): #Call hg bisect with initial params, set up building environment (mozconfig) #Support for using dates if self.testBinaries: self.setupTests() badDate = re.search(r'(\d\d\d\d-\d\d-\d\d)', good) goodDate = re.search(r'(\d\d\d\d-\d\d-\d\d)', bad) if badDate != None and goodDate != None: badDate = badDate.group(1) goodDate = goodDate.group(1) good = self.changesetFromDay( goodDate) #Get first changeset from this day bad = self.changesetFromDay( badDate, oldest=False) #Get last changeset from that day if not (bad and good): print "Invalid date range." quit() #Since they entered dates, lets give them info about the changeset range print "Bisecting on changeset range " + str( good)[:12] + " to " + str(bad)[:12] #Get the actual changesets that we will be using good = hgId(good, self.hgPrefix) bad = hgId(bad, self.hgPrefix) if good and bad and self.validate(good, bad): #Valid changesets, so do the bisection subprocess.call(self.hgPrefix + ["bisect", "--reset"]) setUpdate = captureStdout(self.hgPrefix + ["up", bad]) setBad = captureStdout(self.hgPrefix + ["bisect", "--bad"]) setGood = captureStdout(self.hgPrefix + ["bisect", "--good", good]) print str(setUpdate) print str(setBad) print str(setGood) self.check_done(setGood) # Set mozconfig self.mozconfigure() # Call recursive bisection! self.bisectRecurse(testcondition=testcondition, args_for_condition=args_for_condition) else: print "Invalid values. Please check your changeset revision numbers." print "If the problem persists, try running mozcommitbuilder with the -f flag."
def bisect(self,good,bad, testcondition=None, args_for_condition=[]): #Call hg bisect with initial params, set up building environment (mozconfig) #Support for using dates if self.testBinaries: self.setupTests() badDate = re.search(r'(\d\d\d\d-\d\d-\d\d)',good) goodDate = re.search(r'(\d\d\d\d-\d\d-\d\d)',bad) if badDate != None and goodDate != None: badDate = badDate.group(1) goodDate = goodDate.group(1) good = self.changesetFromDay(goodDate) #Get first changeset from this day bad = self.changesetFromDay(badDate, oldest=False) #Get last changeset from that day if not (bad and good): print "Invalid date range." quit() #Since they entered dates, lets give them info about the changeset range print "Bisecting on changeset range " + str(good)[:12] + " to " + str(bad)[:12] #Get the actual changesets that we will be using good = hgId(good, self.hgPrefix) bad = hgId(bad, self.hgPrefix) if good and bad and self.validate(good, bad): #Valid changesets, so do the bisection subprocess.call(self.hgPrefix+["bisect","--reset"]) setUpdate = captureStdout(self.hgPrefix+["up",bad]) setBad = captureStdout(self.hgPrefix+["bisect","--bad"]) setGood = captureStdout(self.hgPrefix+["bisect","--good",good]) print str(setUpdate) print str(setBad) print str(setGood) self.check_done(setGood) # Set mozconfig self.mozconfigure() # Call recursive bisection! self.bisectRecurse(testcondition=testcondition, args_for_condition=args_for_condition) else: print "Invalid values. Please check your changeset revision numbers." print "If the problem persists, try running mozcommitbuilder with the -f flag."
def getBinary(self, revision): #Create binary, put into cache directory. #Returns path of binary for a given changeset self.build(changeset=revision) #Run "make package" print "Making binary..." makeData = captureStdout(["make", "package"], ignoreStderr=True, currWorkingDir=os.path.join( self.repoPath, "obj-ff-dbg")) binary = None renamedBinary = None distFolder = os.path.join(self.repoPath, "obj-ff-dbg", "dist") #return path to package if sys.platform == "darwin": try: binary = glob.glob(distFolder + "/firefox-*.dmg")[0] except: binary = None renamedBinary = str( revision[:8] ) + ".dmg" #Don't want the filename to be too long :) elif sys.platform == "linux2": try: binary = glob.glob(distFolder + "/firefox-*.tar.gz")[0] except: binary = None renamedBinary = str(revision[:8]) + ".tar.gz" elif sys.platform == "win32" or sys.platform == "cygwin": try: binary = glob.glob(distFolder + "/firefox-*.zip")[0] except: binary = None renamedBinary = str(revision[:8]) + ".zip" else: print "ERROR: This platform is unsupported." quit() if binary != None: print "Binary build successful!" print renamedBinary + " is the binary:" #Move the binary into the correct place. try: os.remove(os.path.join(self.shellCacheDir, renamedBinary)) except: pass shutil.move( binary, os.path.join(self.shellCacheDir, "builds", renamedBinary)) #Return binary path return (os.path.join(self.shellCacheDir, "builds", renamedBinary), renamedBinary) print "ERROR: Binary not found." quit()
def getBinary(self, revision): #Create binary, put into cache directory. #Returns path of binary for a given changeset self.build(changeset=revision) #Run "make package" print "Making binary..." makeData = captureStdout(["make","package"], ignoreStderr=True, currWorkingDir=os.path.join(self.repoPath,"obj-ff-dbg")) binary = None renamedBinary = None distFolder = os.path.join(self.repoPath,"obj-ff-dbg","dist") #return path to package if sys.platform == "darwin": try: binary = glob.glob(distFolder+"/firefox-*.dmg")[0] except: binary = None renamedBinary = str(revision[:8]) + ".dmg" #Don't want the filename to be too long :) elif sys.platform == "linux2": try: binary = glob.glob(distFolder+"/firefox-*.tar.gz")[0] except: binary = None renamedBinary = str(revision[:8]) + ".tar.gz" elif sys.platform == "win32" or sys.platform == "cygwin": try: binary = glob.glob(distFolder+"/firefox-*.zip")[0] except: binary = None renamedBinary = str(revision[:8]) + ".zip" else: print "ERROR: This platform is unsupported." quit() if binary != None: print "Binary build successful!" print renamedBinary + " is the binary:" #Move the binary into the correct place. try: os.remove(os.path.join(self.shellCacheDir,renamedBinary)) except: pass shutil.move(binary, os.path.join(self.shellCacheDir,"builds",renamedBinary)) #Return binary path return (os.path.join(self.shellCacheDir,"builds",renamedBinary), renamedBinary) print "ERROR: Binary not found." quit()
def build(self, changeset=0): #Build a binary and return the file path #Binary file named by changeset number if changeset != 0: changeset = str(changeset) print "Switching to revision "+changeset[:8]+"..." subprocess.call(self.hgPrefix+["update",changeset]) #switch to a given directory #Call make on our cached trunk print "Building..." makeData = captureStdout(self.makeCommand, ignoreStderr=True, currWorkingDir=self.repoPath) if showMakeData == 1: print makeData print "Build complete!"
def build(self, changeset=0): #Build a binary and return the file path #Binary file named by changeset number if changeset != 0: changeset = str(changeset) print "Switching to revision " + changeset[:8] + "..." subprocess.call( self.hgPrefix + ["update", changeset]) #switch to a given directory #Call make on our cached trunk print "Building..." makeData = captureStdout(self.makeCommand, ignoreStderr=True, currWorkingDir=self.repoPath) if showMakeData == 1: print makeData print "Build complete!"
def bisectRecurse(self, testcondition=None, args_for_condition=[]): #Recursively build, run, and prompt verdict = "" current_revision = captureStdout(self.hgPrefix + ["id", "-i"]) if self.remote: print "on current revision " + current_revision print "This would ask for a remote changeset, but it's not implemented yet." #TODO: #Remote bisection! #Step 1. Check if revision is in the archive #Step 2. If revision is not in the archive, set remote=False and continue (it will build and bisect that revision) #if not check_archived: # set remote false and continue #else: #Step 3. If the revision is in the archive, download it and its corresponding tests #STEP3 #1. Extract tests into some directory #2. Extract Nightly.app into "tests" #MozInstaller(src=, dest="", dest_app="Nightly.app") #3. run the following: #test_command = ['python', 'mochitest/runtests.py', '--appname=./Nightly.app/Contents/MacOS/firefox-bin', '--utility-path=bin', '--extra-profile-file=bin/plugins', '--certificate-path=certs', '--autorun', '--close-when-done', '--console-level=INFO', '--test-path=test_name'] #output = captureStdout(test_command, ignoreStderr=True) #set verdict based on output #python mochitest/runtests.py --appname=./Nightly.app/Contents/MacOS/firefox-bin --utility-path=bin --extra-profile-file=bin/plugins --certificate-path=certs --autorun --close-when-done --console-level=INFO --test-path=test_name #example test name: Harness_sanity/test_sanityException.html #Step 4. Run and run test to get verdict #Step 5. Set verdict elif self.tryPusher: try: caller = BuildCaller(host=self.tryhost, port=int(self.tryport), data=current_revision) print "Getting revision " + current_revision + "..." except: print "Failed to connect to trypusher. Make sure your settings are correct and that the trypusher server was started." exit() response = caller.getChangeset() print "Waiting on Mozilla Pulse for revision " + response + "..." url = caller.getURLResponse(response) print "the base is " + url_base(url) #Download it here #1. Download from url, extract to same place as tests #2. Run test or start browser. binary_path = os.path.join(self.binaryDir, url_base(url)) downloaded_binary = download_url(url, dest=str(binary_path)) MozInstaller(src=str(binary_path), dest=str(self.testDir), dest_app="Nightly.app") #now nightly is installed in if sys.platform == "darwin": binary_path = os.path.join(self.testDir, "Nightly.app") runner = FirefoxRunner.create( binary=os.path.join(binary_path, "Contents", "MacOS") + "/firefox-bin") elif sys.platform == "linux2": binary_path = os.path.join(self.testDir, "firefox") runner = FirefoxRunner.create(binary=binary_path) elif sys.platform == "win32" or sys.platform == "cygwin": binary_path = os.path.join(self.testDir, "firefox.exe") runner = FirefoxRunner.create(binary=binary_path) else: print "Your platform is not currently supported." quit() dest = runner.start() if not dest: print "Failed to start the downloaded binary" verdict == "skip" runner.wait() if verdict == "skip": pass elif testcondition != None: #Support condition scripts where arg0 is the directory with the binary and tests args_to_pass = [self.testDir] + args_for_condition if hasattr(testcondition, "init"): testcondition.init(args_to_pass) #TODO: refactor to use directories with revision numbers #8.2.11 - revision number can now be found in current_revision variable tmpdir = tempfile.mkdtemp() verdict = testcondition.interesting(args_to_pass, tmpdir) #Allow user to return true/false or bad/good if verdict != "bad" and verdict != "good": verdict = "bad" if verdict else "good" else: try: self.build() except Exception: print "This build failed!" verdict = "skip" if verdict == "skip": pass elif testcondition == None: #Not using a test, interactive bisect begin! self.run() else: #Using Jesse's idea: import any testing script and run it as the truth condition args_to_pass = [self.objdir] + args_for_condition if hasattr(testcondition, "init"): testcondition.init(args_to_pass) #TODO: refactor to use directories with revision numbers #8.2.11 - revision number can now be found in current_revision variable tmpdir = tempfile.mkdtemp() verdict = testcondition.interesting(args_to_pass, tmpdir) #Allow user to return true/false or bad/good if verdict != "bad" and verdict != "good": verdict = "bad" if verdict else "good" while verdict not in ["good", "bad", "skip"]: verdict = raw_input( "Was this commit good or bad? (type 'good', 'bad', or 'skip'): " ) if verdict == 'g': verdict = "good" if verdict == 'b': verdict = "bad" if verdict == 's': verdict = "skip" # do hg bisect --good, --bad, or --skip verdictCommand = self.hgPrefix + ["bisect", "--" + verdict] print " ".join(verdictCommand) retval = captureStdout(verdictCommand) string_to_parse = str(retval) print string_to_parse self.check_done(string_to_parse) if retval.startswith("Testing changeset"): print "\n" self.bisectRecurse(testcondition=testcondition, args_for_condition=args_for_condition)
def bisectRecurse(self, testcondition=None, args_for_condition=[]): #Recursively build, run, and prompt verdict = "" current_revision = captureStdout(self.hgPrefix+["id","-i"]) if self.remote: print "on current revision "+current_revision print "This would ask for a remote changeset, but it's not implemented yet." #TODO: #Remote bisection! #Step 1. Check if revision is in the archive #Step 2. If revision is not in the archive, set remote=False and continue (it will build and bisect that revision) #if not check_archived: # set remote false and continue #else: #Step 3. If the revision is in the archive, download it and its corresponding tests #STEP3 #1. Extract tests into some directory #2. Extract Nightly.app into "tests" #MozInstaller(src=, dest="", dest_app="Nightly.app") #3. run the following: #test_command = ['python', 'mochitest/runtests.py', '--appname=./Nightly.app/Contents/MacOS/firefox-bin', '--utility-path=bin', '--extra-profile-file=bin/plugins', '--certificate-path=certs', '--autorun', '--close-when-done', '--console-level=INFO', '--test-path=test_name'] #output = captureStdout(test_command, ignoreStderr=True) #set verdict based on output #python mochitest/runtests.py --appname=./Nightly.app/Contents/MacOS/firefox-bin --utility-path=bin --extra-profile-file=bin/plugins --certificate-path=certs --autorun --close-when-done --console-level=INFO --test-path=test_name #example test name: Harness_sanity/test_sanityException.html #Step 4. Run and run test to get verdict #Step 5. Set verdict elif self.tryPusher: try: caller = BuildCaller(host=self.tryhost, port=int(self.tryport), data=current_revision) print "Getting revision "+current_revision+"..." except: print "Failed to connect to trypusher. Make sure your settings are correct and that the trypusher server was started." exit() response = caller.getChangeset() print "Waiting on Mozilla Pulse for revision " + response + "..." url = caller.getURLResponse(response) print "the base is " +url_base(url) #Download it here #1. Download from url, extract to same place as tests #2. Run test or start browser. binary_path = os.path.join(self.binaryDir,url_base(url)) downloaded_binary = download_url(url, dest=str(binary_path)) MozInstaller(src=str(binary_path), dest=str(self.testDir), dest_app="Nightly.app") #now nightly is installed in if sys.platform == "darwin": binary_path = os.path.join(self.testDir,"Nightly.app") runner = FirefoxRunner(binary=os.path.join(binary_path,"Contents","MacOS")+"/firefox-bin") elif sys.platform == "linux2": binary_path = os.path.join(self.testDir,"firefox") runner = FirefoxRunner(binary=binary_path) elif sys.platform == "win32" or sys.platform == "cygwin": binary_path = os.path.join(self.testDir,"firefox.exe") runner = FirefoxRunner(binary=binary_path) else: print "Your platform is not currently supported." quit() dest = runner.start() if not dest: print "Failed to start the downloaded binary" verdict == "skip" runner.wait() if verdict == "skip": pass elif testcondition!=None: #Support condition scripts where arg0 is the directory with the binary and tests args_to_pass = [self.testDir] + args_for_condition if hasattr(testcondition, "init"): testcondition.init(args_to_pass) #TODO: refactor to use directories with revision numbers #8.2.11 - revision number can now be found in current_revision variable tmpdir = tempfile.mkdtemp() verdict = testcondition.interesting(args_to_pass,tmpdir) #Allow user to return true/false or bad/good if verdict != "bad" and verdict != "good": verdict = "bad" if verdict else "good" else: try: self.build() except Exception: print "This build failed!" verdict = "skip" if verdict == "skip": pass elif testcondition==None: #Not using a test, interactive bisect begin! self.run() else: #Using Jesse's idea: import any testing script and run it as the truth condition args_to_pass = [self.objdir] + args_for_condition if hasattr(testcondition, "init"): testcondition.init(args_to_pass) #TODO: refactor to use directories with revision numbers #8.2.11 - revision number can now be found in current_revision variable tmpdir = tempfile.mkdtemp() verdict = testcondition.interesting(args_to_pass,tmpdir) #Allow user to return true/false or bad/good if verdict != "bad" and verdict != "good": verdict = "bad" if verdict else "good" while verdict not in ["good", "bad", "skip"]: verdict = raw_input("Was this commit good or bad? (type 'good', 'bad', or 'skip'): ") if verdict == 'g': verdict = "good" if verdict == 'b': verdict = "bad" if verdict == 's': verdict = "skip" # do hg bisect --good, --bad, or --skip verdictCommand = self.hgPrefix+["bisect","--"+verdict] print " ".join(verdictCommand) retval = captureStdout(verdictCommand) string_to_parse = str(retval) print string_to_parse self.check_done(string_to_parse) if retval.startswith("Testing changeset"): print "\n" self.bisectRecurse(testcondition=testcondition, args_for_condition=args_for_condition)
def bisectRecurse(self, testfile=None, testpath=None, testcondition=None, args_for_condition=[]): #Recursively build, run, and prompt verdict = "" try: self.build() except Exception: verdict = "skip" if verdict == "skip": pass elif testfile==None and testpath==None and testcondition==None: #Not using a test, interactive bisect begin! self.run() elif testcondition != None: #Using Jesse's idea: import any testing script and run it as the truth condition conditionscript = ximport.importRelativeOrAbsolute(testcondition) args_to_pass = [self.objdir] + args_for_condition conditionscript.init(args_to_pass) #TODO: refactor to use directories with revision numbers tmpdir = tempfile.mkdtemp() #If conditionscript is true, that means there was a bug. #If conditionscript is false, that means the bug is not present. verdict = conditionscript.interesting(args_to_pass,tmpdir) verdict = "bad" if verdict else "good" else: if testfile == None: #Using External testfile #1. Clear self.mochitest_tmp try: shutil.rmtree(self.mochitest_tmp) except: pass #2. Move file from testpath to self.mochitest_tmp try: os.mkdir(self.mochitest_tmp) dst = os.path.join(self.mochitest_tmp,"test_bug999999.html") subprocess.call(['touch',dst]) print "copy " + str(testpath) + " to " +dst shutil.copy(str(testpath),dst) except: print "Unable to generate test path, quitting. Is your inputted test a valid mochitest?" quit() #3. Make mochitest use the commitbuilder directory. testfile="commitbuilder" #DEBUG #testfile = "content/base/test/test_CrossSiteXHR.html" print "Trying testfile "+str(testfile) #TODO: # 1. Copy relevant test to a directory of my choice #sts = os.system("TEST_PATH="+testpath+" EXTRA_TEST_ARGS='--close-when-done' make -C " +self.objdir+ " mochitest-plain") sts = subprocess.call(['make','-C',self.objdir,'mochitest-plain'],stdout=open('/dev/null','w'),env={'TEST_PATH': testfile, 'EXTRA_TEST_ARGS':"--close-when-done"}) shutil.rmtree(self.mochitest_tmp) #cleanup if sts != 0: verdict = "bad" print "============================" print "Verdict: FAILED test, bad changeset detected!" print "============================" else: verdict = "good" print "============================" print "Verdict: PASSED test, good changeset detected!" print "============================" while verdict not in ["good", "bad", "skip"]: verdict = raw_input("Was this commit good or bad? (type 'good', 'bad', or 'skip'): ") # do hg bisect --good, --bad, or --skip verdictCommand = self.hgPrefix+["bisect","--"+verdict] print " ".join(verdictCommand) retval = captureStdout(verdictCommand) print str(retval) # HACK if retval[1] == 'h': #if retval starts with "the" then we can quit quit() elif retval[1] == 'e': #if retval starts with "testing" then it needs to keep going print "\n" else: print "Something went wrong! :(" quit() self.bisectRecurse(testfile=testfile, testpath=testpath, testcondition=testcondition, args_for_condition=args_for_condition)