PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/system/shared/debugger.py

https://github.com/KDAB/KDAB-Creator
Python | 224 lines | 199 code | 10 blank | 15 comment | 45 complexity | 552758fa9fd4169987e74d29b39e39a9 MD5 | raw file
  1. import re
  2. def handleDebuggerWarnings(config):
  3. if "MSVC" in config:
  4. try:
  5. popup = waitForObject("{text?='<html><head/><body>*' type='QLabel' unnamed='1' visible='1' window=':Symbol Server_Utils::CheckableMessageBox'}", 10000)
  6. symServerNotConfiged = ("<html><head/><body><p>The debugger is not configured to use the public "
  7. "<a href=\"http://support.microsoft.com/kb/311503\">Microsoft Symbol Server</a>. "
  8. "This is recommended for retrieval of the symbols of the operating system libraries.</p>"
  9. "<p><i>Note:</i> A fast internet connection is required for this to work smoothly. "
  10. "Also, a delay might occur when connecting for the first time.</p>"
  11. "<p>Would you like to set it up?</p></br></body></html>")
  12. if popup.text == symServerNotConfiged:
  13. test.log("Creator warned about the debugger not being configured to use the public Microsoft Symbol Server.")
  14. else:
  15. test.warning("Creator showed an unexpected warning: " + str(popup.text))
  16. clickButton(waitForObject("{text='No' type='QPushButton' unnamed='1' visible='1' window=':Symbol Server_Utils::CheckableMessageBox'}", 10000))
  17. except LookupError:
  18. pass # No warning. Fine.
  19. else:
  20. if "Release" in config and platform.system() != "Darwin":
  21. message = waitForObject("{container=':Qt Creator.DebugModeWidget_QSplitter' name='qt_msgbox_label' type='QLabel' visible='1'}", 20000)
  22. test.compare(message.text, "This does not seem to be a \"Debug\" build.\nSetting breakpoints by file name and line number may fail.")
  23. clickButton("{container=':Qt Creator.DebugModeWidget_QSplitter' text='OK' type='QPushButton' unnamed='1' visible='1'}")
  24. def takeDebuggerLog():
  25. invokeMenuItem("Window", "Views", "Debugger Log")
  26. debuggerLogWindow = waitForObject("{container=':DebugModeWidget.Debugger Log_QDockWidget' type='Debugger::Internal::CombinedPane' unnamed='1' visible='1'}", 20000)
  27. debuggerLog = str(debuggerLogWindow.plainText)
  28. mouseClick(debuggerLogWindow, 5, 5, 0, Qt.LeftButton)
  29. activateItem(waitForObjectItem(openContextMenuOnTextCursorPosition(debuggerLogWindow),
  30. "Clear Contents"))
  31. waitFor("str(debuggerLogWindow.plainText)==''", 5000)
  32. invokeMenuItem("Window", "Views", "Debugger Log")
  33. return debuggerLog
  34. # function to set breakpoints for the current project
  35. # on the given file,line pairs inside the given dict
  36. # the lines are treated as regular expression
  37. def setBreakpointsForCurrentProject(filesAndLines):
  38. # internal helper for setBreakpointsForCurrentProject
  39. # double clicks the treeElement inside the given navTree
  40. # TODO: merge with doubleClickFile() from tst_qml_editor & move to utils(?)
  41. def __doubleClickFile__(navTree, treeElement):
  42. waitForObjectItem(navTree, treeElement)
  43. fileNamePattern = re.compile(".*\.(?P<file>(.*\\\..*)?)$")
  44. fileName = fileNamePattern.search(treeElement).group("file").replace("\\.", ".")
  45. doubleClickItem(navTree, treeElement, 5, 5, 0, Qt.LeftButton)
  46. mainWindow = waitForObject(":Qt Creator_Core::Internal::MainWindow")
  47. waitFor('fileName in str(mainWindow.windowTitle)', 5000)
  48. return fileName
  49. switchViewTo(ViewConstants.DEBUG)
  50. removeOldBreakpoints()
  51. if not filesAndLines or not isinstance(filesAndLines, dict):
  52. test.fatal("This function only takes a non-empty dict.")
  53. return False
  54. navTree = waitForObject("{type='Utils::NavigationTreeView' unnamed='1' visible='1' "
  55. "window=':Qt Creator_Core::Internal::MainWindow'}", 20000)
  56. for curFile,curLine in filesAndLines.iteritems():
  57. fName = __doubleClickFile__(navTree, curFile)
  58. editor = getEditorForFileSuffix(curFile)
  59. if not placeCursorToLine(editor, curLine, True):
  60. return False
  61. invokeMenuItem("Debug", "Toggle Breakpoint")
  62. test.log('Set breakpoint in %s' % fName, curLine)
  63. try:
  64. breakPointTreeView = waitForObject("{type='Debugger::Internal::BreakWindow' visible='1' "
  65. "windowTitle='Breakpoints' name='Debugger.Docks.Break'}")
  66. waitFor("breakPointTreeView.model().rowCount() == len(filesAndLines)", 2000)
  67. except:
  68. test.fatal("UI seems to have changed - check manually and fix this script.")
  69. return False
  70. test.compare(breakPointTreeView.model().rowCount(), len(filesAndLines),
  71. 'Expected %d set break points, found %d listed' %
  72. (len(filesAndLines), breakPointTreeView.model().rowCount()))
  73. return True
  74. # helper that removes all breakpoints - assumes that it's getting called
  75. # being already on Debug view and Breakpoints widget is not disabled
  76. def removeOldBreakpoints():
  77. test.log("Removing old breakpoints if there are any")
  78. try:
  79. breakPointTreeView = waitForObject("{type='Debugger::Internal::BreakWindow' visible='1' "
  80. "windowTitle='Breakpoints' name='Debugger.Docks.Break'}")
  81. model = breakPointTreeView.model()
  82. if model.rowCount()==0:
  83. test.log("No breakpoints found...")
  84. else:
  85. test.log("Found %d breakpoints - removing them" % model.rowCount())
  86. for row in range(model.rowCount()):
  87. currentIndex = model.index(row,0)
  88. rect = breakPointTreeView.visualRect(currentIndex)
  89. mouseClick(breakPointTreeView, rect.x+5, rect.y+5, 0, Qt.LeftButton)
  90. type(breakPointTreeView, "<Delete>")
  91. except:
  92. test.fatal("UI seems to have changed - check manually and fix this script.")
  93. return False
  94. return test.compare(model.rowCount(), 0, "Check if all breakpoints have been removed.")
  95. # function to do simple debugging of the current (configured) project
  96. # param pressContinueCount defines how often it is expected to press
  97. # the 'Continue' button while debugging
  98. # param expectedBPOrder holds a list of dicts where the dicts contain always
  99. # only 1 key:value pair - the key is the name of the file, the value is
  100. # line number where the debugger should stop
  101. def doSimpleDebugging(currentConfigName, pressContinueCount=1, expectedBPOrder=[]):
  102. expectedLabelTexts = ['Stopped\.', 'Stopped at breakpoint \d+ \(\d+\) in thread \d+\.']
  103. if len(expectedBPOrder) == 0:
  104. expectedLabelTexts.append("Running\.")
  105. if not __startDebugger__(currentConfigName):
  106. return False
  107. statusLabel = findObject(":Debugger Toolbar.StatusText_Utils::StatusLabel")
  108. test.log("Continuing debugging %d times..." % pressContinueCount)
  109. for i in range(pressContinueCount):
  110. if waitFor("regexVerify(str(statusLabel.text), expectedLabelTexts)", 20000):
  111. verifyBreakPoint(expectedBPOrder[i])
  112. else:
  113. test.fail('%s' % str(statusLabel.text))
  114. contDbg = waitForObject(":*Qt Creator.Continue_Core::Internal::FancyToolButton", 3000)
  115. test.log("Continuing...")
  116. clickButton(contDbg)
  117. waitFor("str(statusLabel.text) == 'Running.'", 5000)
  118. timedOut = not waitFor("str(statusLabel.text) in ['Running.', 'Debugger finished.']", 30000)
  119. if timedOut:
  120. test.log("Waiting for 'Running.' / 'Debugger finished.' timed out.",
  121. "Debugger is in state: '%s'..." % statusLabel.text)
  122. if str(statusLabel.text) == 'Running.':
  123. test.log("Debugger is still running... Will be stopped.")
  124. return __stopDebugger__()
  125. elif str(statusLabel.text) == 'Debugger finished.':
  126. test.log("Debugger has finished.")
  127. return __logDebugResult__()
  128. else:
  129. test.log("Trying to stop debugger...")
  130. try:
  131. return __stopDebugger__()
  132. except:
  133. # if stopping failed - debugger had already stopped
  134. return True
  135. def __startDebugger__(config):
  136. clickButton(waitForObject(":*Qt Creator.Start Debugging_Core::Internal::FancyToolButton"))
  137. handleDebuggerWarnings(config)
  138. hasNotTimedOut = waitFor("object.exists(':Debugger Toolbar.Continue_QToolButton')", 60000)
  139. try:
  140. mBox = findObject(":Failed to start application_QMessageBox")
  141. mBoxText = mBox.text
  142. mBoxIText = mBox.informativeText
  143. clickButton(":DebugModeWidget.OK_QPushButton")
  144. test.fail("Debugger hasn't started... QMessageBox appeared!")
  145. test.log("QMessageBox content: '%s'" % mBoxText,
  146. "'%s'" % mBoxIText)
  147. return False
  148. except:
  149. pass
  150. if hasNotTimedOut:
  151. test.passes("Debugger started...")
  152. else:
  153. test.fail("Debugger seems to have not started...")
  154. if "MSVC" in config:
  155. debuggerLog = takeDebuggerLog()
  156. if "lib\qtcreatorcdbext64\qtcreatorcdbext.dll cannot be found." in debuggerLog:
  157. test.fatal("qtcreatorcdbext.dll is missing in lib\qtcreatorcdbext64")
  158. else:
  159. test.fatal("Debugger log did not behave as expected. Please check manually.")
  160. logApplicationOutput()
  161. return False
  162. try:
  163. waitForObject(":*Qt Creator.Interrupt_Core::Internal::FancyToolButton", 3000)
  164. test.passes("'Interrupt' (debugger) button visible.")
  165. except:
  166. try:
  167. waitForObject(":*Qt Creator.Continue_Core::Internal::FancyToolButton", 3000)
  168. test.passes("'Continue' (debugger) button visible.")
  169. except:
  170. test.fatal("Neither 'Interrupt' nor 'Continue' button visible (Debugger).")
  171. return True
  172. def __stopDebugger__():
  173. clickButton(waitForObject(":Debugger Toolbar.Exit Debugger_QToolButton"))
  174. ensureChecked("{type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' "
  175. "window=':Qt Creator_Core::Internal::MainWindow' occurrence='3'}")
  176. output = waitForObject("{type='Core::OutputWindow' visible='1' windowTitle='Application Output Window'}", 20000)
  177. waitFor("'Debugging has finished' in str(output.plainText)", 20000)
  178. return __logDebugResult__()
  179. def __logDebugResult__():
  180. try:
  181. result = waitForObject(":*Qt Creator.Start Debugging_Core::Internal::FancyToolButton")
  182. test.passes("'Start Debugging' button visible.")
  183. except:
  184. test.fail("'Start Debugging' button is not visible.")
  185. result = None
  186. if result:
  187. test.passes("Debugger stopped.. Qt Creator is back at normal state.")
  188. else:
  189. test.fail("Debugger seems to have not stopped...")
  190. logApplicationOutput()
  191. return result
  192. def verifyBreakPoint(bpToVerify):
  193. if isinstance(bpToVerify, dict):
  194. fileName = bpToVerify.keys()[0]
  195. editor = getEditorForFileSuffix(fileName)
  196. if editor == None:
  197. return False
  198. textPos = editor.textCursor().position()
  199. line = str(editor.plainText)[:textPos].count("\n") + 1
  200. windowTitle = str(waitForObject(":Qt Creator_Core::Internal::MainWindow").windowTitle)
  201. if fileName in windowTitle:
  202. test.passes("Creator's window title changed according to current file")
  203. else:
  204. test.fail("Creator's window title did not change according to current file")
  205. if line == bpToVerify.values()[0]:
  206. test.passes("Breakpoint at expected line (%d) inside expected file (%s)"
  207. % (line, fileName))
  208. return True
  209. else:
  210. test.fail("Breakpoint did not match expected line/file",
  211. "Found: %d in %s" % (line, fileName))
  212. else:
  213. test.fatal("Expected a dict for bpToVerify - got '%s'" % className(bpToVerify))
  214. return False