import re def handleDebuggerWarnings(config): if "MSVC" in config: try: popup = waitForObject("{text?='*' type='QLabel' unnamed='1' visible='1' window=':Symbol Server_Utils::CheckableMessageBox'}", 10000) symServerNotConfiged = ("

The debugger is not configured to use the public " "Microsoft Symbol Server. " "This is recommended for retrieval of the symbols of the operating system libraries.

" "

Note: A fast internet connection is required for this to work smoothly. " "Also, a delay might occur when connecting for the first time.

" "

Would you like to set it up?


") if popup.text == symServerNotConfiged: test.log("Creator warned about the debugger not being configured to use the public Microsoft Symbol Server.") else: test.warning("Creator showed an unexpected warning: " + str(popup.text)) clickButton(waitForObject("{text='No' type='QPushButton' unnamed='1' visible='1' window=':Symbol Server_Utils::CheckableMessageBox'}", 10000)) except LookupError: pass # No warning. Fine. else: if "Release" in config and platform.system() != "Darwin": message = waitForObject("{container=':Qt Creator.DebugModeWidget_QSplitter' name='qt_msgbox_label' type='QLabel' visible='1'}", 20000) messageText = str(message.text) test.verify(messageText.startswith('This does not seem to be a "Debug" build.\nSetting breakpoints by file name and line number may fail.'), "Got warning: %s" % messageText) clickButton("{container=':Qt Creator.DebugModeWidget_QSplitter' text='OK' type='QPushButton' unnamed='1' visible='1'}") def takeDebuggerLog(): invokeMenuItem("Window", "Views", "Debugger Log") debuggerLogWindow = waitForObject("{container=':DebugModeWidget.Debugger Log_QDockWidget' type='Debugger::Internal::CombinedPane' unnamed='1' visible='1'}", 20000) debuggerLog = str(debuggerLogWindow.plainText) mouseClick(debuggerLogWindow, 5, 5, 0, Qt.LeftButton) activateItem(waitForObjectItem(openContextMenuOnTextCursorPosition(debuggerLogWindow), "Clear Contents")) waitFor("str(debuggerLogWindow.plainText)==''", 5000) invokeMenuItem("Window", "Views", "Debugger Log") return debuggerLog # function to set breakpoints for the current project # on the given file,line pairs inside the given list of dicts # the lines are treated as regular expression def setBreakpointsForCurrentProject(filesAndLines): # internal helper for setBreakpointsForCurrentProject # double clicks the treeElement inside the given navTree # TODO: merge with doubleClickFile() from tst_qml_editor & move to utils(?) def __doubleClickFile__(navTree, treeElement): waitForObjectItem(navTree, treeElement) fileNamePattern = re.compile(".*\.(?P(.*\\\..*)?)$") fileName = fileNamePattern.search(treeElement).group("file").replace("\\.", ".") doubleClickItem(navTree, treeElement, 5, 5, 0, Qt.LeftButton) mainWindow = waitForObject(":Qt Creator_Core::Internal::MainWindow") waitFor('fileName in str(mainWindow.windowTitle)', 5000) return fileName switchViewTo(ViewConstants.DEBUG) removeOldBreakpoints() if not filesAndLines or not isinstance(filesAndLines, (list,tuple)): test.fatal("This function only takes a non-empty list/tuple holding dicts.") return False navTree = waitForObject("{type='Utils::NavigationTreeView' unnamed='1' visible='1' " "window=':Qt Creator_Core::Internal::MainWindow'}", 20000) for current in filesAndLines: for curFile,curLine in current.iteritems(): fName = __doubleClickFile__(navTree, curFile) editor = getEditorForFileSuffix(curFile) if not placeCursorToLine(editor, curLine, True): return False invokeMenuItem("Debug", "Toggle Breakpoint") test.log('Set breakpoint in %s' % fName, curLine) try: breakPointTreeView = waitForObject(":Breakpoints_Debugger::Internal::BreakTreeView") waitFor("breakPointTreeView.model().rowCount() == len(filesAndLines)", 2000) except: test.fatal("UI seems to have changed - check manually and fix this script.") return False test.compare(breakPointTreeView.model().rowCount(), len(filesAndLines), 'Expected %d set break points, found %d listed' % (len(filesAndLines), breakPointTreeView.model().rowCount())) return True # helper that removes all breakpoints - assumes that it's getting called # being already on Debug view and Breakpoints widget is not disabled def removeOldBreakpoints(): test.log("Removing old breakpoints if there are any") try: breakPointTreeView = waitForObject(":Breakpoints_Debugger::Internal::BreakTreeView") model = breakPointTreeView.model() if model.rowCount()==0: test.log("No breakpoints found...") else: test.log("Found %d breakpoints - removing them" % model.rowCount()) for row in range(model.rowCount()): currentIndex = model.index(row,0) rect = breakPointTreeView.visualRect(currentIndex) mouseClick(breakPointTreeView, rect.x+5, rect.y+5, 0, Qt.LeftButton) type(breakPointTreeView, "") except: test.fatal("UI seems to have changed - check manually and fix this script.") return False return test.compare(model.rowCount(), 0, "Check if all breakpoints have been removed.") # function to do simple debugging of the current (configured) project # param pressContinueCount defines how often it is expected to press # the 'Continue' button while debugging # param expectedBPOrder holds a list of dicts where the dicts contain always # only 1 key:value pair - the key is the name of the file, the value is # line number where the debugger should stop def doSimpleDebugging(currentConfigName, pressContinueCount=1, expectedBPOrder=[]): expectedLabelTexts = ['Stopped\.', 'Stopped at breakpoint \d+ \(\d+\) in thread \d+\.'] if len(expectedBPOrder) == 0: expectedLabelTexts.append("Running\.") if not __startDebugger__(currentConfigName): return False statusLabel = findObject(":Debugger Toolbar.StatusText_Utils::StatusLabel") test.log("Continuing debugging %d times..." % pressContinueCount) for i in range(pressContinueCount): if waitFor("regexVerify(str(statusLabel.text), expectedLabelTexts)", 20000): verifyBreakPoint(expectedBPOrder[i]) else: test.fail('%s' % str(statusLabel.text)) contDbg = waitForObject(":*Qt Creator.Continue_Core::Internal::FancyToolButton", 3000) test.log("Continuing...") clickButton(contDbg) waitFor("str(statusLabel.text) == 'Running.'", 5000) timedOut = not waitFor("str(statusLabel.text) in ['Running.', 'Debugger finished.']", 30000) if timedOut: test.log("Waiting for 'Running.' / 'Debugger finished.' timed out.", "Debugger is in state: '%s'..." % statusLabel.text) if str(statusLabel.text) == 'Running.': test.log("Debugger is still running... Will be stopped.") return __stopDebugger__() elif str(statusLabel.text) == 'Debugger finished.': test.log("Debugger has finished.") return __logDebugResult__() else: test.log("Trying to stop debugger...") try: return __stopDebugger__() except: # if stopping failed - debugger had already stopped return True def __startDebugger__(config): clickButton(waitForObject(":*Qt Creator.Start Debugging_Core::Internal::FancyToolButton")) handleDebuggerWarnings(config) hasNotTimedOut = waitFor("object.exists(':Debugger Toolbar.Continue_QToolButton')", 60000) try: mBox = findObject(":Failed to start application_QMessageBox") mBoxText = mBox.text mBoxIText = mBox.informativeText clickButton(":DebugModeWidget.OK_QPushButton") test.fail("Debugger hasn't started... QMessageBox appeared!") test.log("QMessageBox content: '%s'" % mBoxText, "'%s'" % mBoxIText) return False except: pass if hasNotTimedOut: test.passes("Debugger started...") else: test.fail("Debugger seems to have not started...") if "MSVC" in config: debuggerLog = takeDebuggerLog() if "lib\qtcreatorcdbext64\qtcreatorcdbext.dll cannot be found." in debuggerLog: test.fatal("qtcreatorcdbext.dll is missing in lib\qtcreatorcdbext64") else: test.fatal("Debugger log did not behave as expected. Please check manually.") logApplicationOutput() return False try: waitForObject(":*Qt Creator.Interrupt_Core::Internal::FancyToolButton", 3000) test.passes("'Interrupt' (debugger) button visible.") except: try: waitForObject(":*Qt Creator.Continue_Core::Internal::FancyToolButton", 3000) test.passes("'Continue' (debugger) button visible.") except: test.fatal("Neither 'Interrupt' nor 'Continue' button visible (Debugger).") return True def __stopDebugger__(): clickButton(waitForObject(":Debugger Toolbar.Exit Debugger_QToolButton")) ensureChecked("{type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' " "window=':Qt Creator_Core::Internal::MainWindow' occurrence='3'}") output = waitForObject("{type='Core::OutputWindow' visible='1' windowTitle='Application Output Window'}", 20000) waitFor("'Debugging has finished' in str(output.plainText)", 20000) return __logDebugResult__() def __logDebugResult__(): try: result = waitForObject(":*Qt Creator.Start Debugging_Core::Internal::FancyToolButton") test.passes("'Start Debugging' button visible.") except: test.fail("'Start Debugging' button is not visible.") result = None if result: test.passes("Debugger stopped.. Qt Creator is back at normal state.") else: test.fail("Debugger seems to have not stopped...") logApplicationOutput() return result def verifyBreakPoint(bpToVerify): if isinstance(bpToVerify, dict): fileName = bpToVerify.keys()[0] editor = getEditorForFileSuffix(fileName) if editor == None: return False textPos = editor.textCursor().position() line = str(editor.plainText)[:textPos].count("\n") + 1 windowTitle = str(waitForObject(":Qt Creator_Core::Internal::MainWindow").windowTitle) if fileName in windowTitle: test.passes("Creator's window title changed according to current file") else: test.fail("Creator's window title did not change according to current file") if line == bpToVerify.values()[0]: test.passes("Breakpoint at expected line (%d) inside expected file (%s)" % (line, fileName)) return True else: test.fail("Breakpoint did not match expected line/file", "Found: %d in %s" % (line, fileName)) else: test.fatal("Expected a dict for bpToVerify - got '%s'" % className(bpToVerify)) return False