Exemplo n.º 1
0
class Result(View):
    '''
    A view for analyzing test case results.
    '''
    NAME = viewName()
    _UI_FILE = "result_view.ui"
    _ICON_FILE = ":/result/icons/mail-mark-task.png"

    _CONFIG_SECTION_MENU = "menu"
    _CONFIG_SECTION_CONSOLE = "console"

    # Menus and Tool bar
    _menuFile = ("actionOpen", ("Recent &Files", "actionClearMenu"), None,
                 "actionClose")
    _menuEdit = ("actionExpand", "actionExpandAll", "actionCollapse",
                 "actionCollapseAll", None)
    _toolBar = ("actionOpen", "actionClose", None, (
        "actionExpand",
        "actionExpandAll",
    ), ("actionCollapse", "actionCollapseAll"))

    def __init__(self, *args):
        View.__init__(self, *args)

        # Recent files menu
        self._recentFiles = LastValues(self.NAME, self._CONFIG_SECTION_MENU,
                                       "recent", 5)
        self._actionClearMenu = self._elements["actionClearMenu"]
        self._menuRecentFiles = self._menuFile[1]
        self._actionClearMenu.triggered.connect(self._recentFiles.clear)
        self._actionClearMenu.triggered.connect(self._updateRecentFiles)

        # Widgets
        self._treeWidget = self._elements["treeWidgetInfo"]
        self._tabWidget = self._elements["tabWidgetResults"]
        self._tabWidget.setTabBar(ClosableTabBar())
        self._widgetConsole = self._elements["widgetConsole"]
        self._buttonShowConsole = self._elements["buttonShowConsole"]
        self._buttonHideConsole = self._elements["buttonHideConsole"]
        self._splitterConsole = self._elements["splitterConsole"]
        self._buttonSaveOutput = self._elements["buttonSaveOutput"]
        self._textEdit = self._elements["textEditConsole"]
        self._tabs = {}
        self._treeWidget.setColumnCount(2)
        self._treeWidget.header().resizeSection(2, 0)
        self._treeWidget.header().setHorizontalScrollMode(
            QtGui.QAbstractItemView.ScrollPerPixel)
        self._treeWidget.header().setResizeMode(
            1, QtGui.QHeaderView.ResizeToContents)
        self._treeWidget.header().setResizeMode(2, QtGui.QHeaderView.Fixed)
        self._treeWidget.header().setStretchLastSection(True)
        self._widgetConsole.setVisible(False)
        self._buttonHideConsole.setVisible(False)
        self._tabWidget.currentChanged[int].connect(self._displaySelected)
        self._tabWidget.tabCloseRequested.connect(self._closeTabOfIndex)
        self._buttonShowConsole.clicked.connect(self._showConsole)
        self._buttonHideConsole.clicked.connect(self._hideConsole)
        self._buttonSaveOutput.clicked.connect(self._saveOutput)

        # Actions
        self._elements["actionOpen"].triggered.connect(self._openDialog)
        self._elements["actionClose"].triggered.connect(self._closeCurrentTab)
        self._actionExpand = self._elements["actionExpand"]
        self._actionExpandAll = self._elements["actionExpandAll"]
        self._actionCollapse = self._elements["actionCollapse"]
        self._actionCollapseAll = self._elements["actionCollapseAll"]

        # Console channel
        self._hideConsole()
        consoleChannelHelper = ConsoleChannelHelper(textEdit=self._textEdit)
        channels.add(ConsoleChannel,
                     "_ui_console",
                     console=consoleChannelHelper)
        self._splitterConsole.handle(1).setEnabled(False)

        # Tab channel
        self._resultChannelHelper = ResultChannelHelper(self)
        channels.add(ResultChannel,
                     "_ui_result",
                     result=self._resultChannelHelper)

# Slots:
#@QtCore.Slot()

    def _openDialog(self):
        '''
        Opens selected files containing test results in tabs.
        '''
        log.debug("Opening result file")
        readableChannels = {}
        for c in [
                c for c in channels.get()
                if isinstance(c, channels.TestResultFileChannel)
        ]:
            desc = "%s (*.%s)" % (c.name, c.fileExt().strip("."))
            readableChannels[desc] = c
        if not readableChannels:
            dialogs.runWarning("There are no readable channels available")
            return
        dialog = QtGui.QFileDialog(self.view)
        dialog.setFileMode(QtGui.QFileDialog.ExistingFiles)
        dialog.setFilter(";;".join(readableChannels))
        if not dialog.exec_():
            log.debug("Opening result file was cancelled")
            return
        channel = readableChannels[dialog.selectedFilter()]
        for path in dialog.selectedFiles():
            try:
                self.addTab(channel.read(path),
                            os.path.split(path)[1],
                            tooltip=path)
                self._updateRecentFiles(path)
            except Exception, ex:
                dialogs.runError("Error occurred while loading result file "
                                 "'%s':\n%s" % (path, ex))
Exemplo n.º 2
0
class ResultTab(QtCore.QObject):
    '''
    Represents tab containing test results.
    '''
    _RESULT_TAB_UI = "result_tab.ui"
    _COLUMN_COUNT = 3

    _ICONS = {
        "directory": QtGui.QIcon(":/result/icons/folder-grey.png"),
        "module": QtGui.QIcon(":/result/icons/text-x-python.png"),
        "suite": QtGui.QIcon(":/result/icons/source_moc.png"),
        "case": QtGui.QIcon(":/result/icons/inode-blockdevice.png"),
        "step": QtGui.QIcon(":/result/icons/application-x-zip.png")
    }

    _expandStatuses = settings.get(viewName(),
                                   "options",
                                   "expand_statuses",
                                   default=",".join(
                                       (STATUS_ERROR, STATUS_FAILED)),
                                   force=True)

    def __init__(self, view, result, select, closable):
        '''
        Initializes tab with test results. The parameters are:
        - view: instance of ResultView
        - result: a test result instance
        - select: a boolean that determines whether tree items should be
            selected on adding
        - closable: a boolean that determines if tab can be closed manually
        '''
        QtCore.QObject.__init__(self)
        self._view = view
        elements = view.loadUi(self._RESULT_TAB_UI)
        self.tab = elements["Tab"]
        self._treeWidget = elements["treeWidget"]
        self._treeWidget.currentItemChanged.connect(self.display)
        self._treeWidget.itemExpanded.connect(self._updateOnExpand)
        self._treeWidget.itemCollapsed.connect(self._updateOnCollapse)
        self._closable = closable

        for res in result:
            log.debug("Adding tree item(s) for result '%s'" % res.id)
            self._addItem(res, None, select)
            for i in xrange(self._COLUMN_COUNT):
                self._treeWidget.resizeColumnToContents(i)

# Private methods:

    def _getItem(self, id, force=False):
        '''
        Returns an item related to a test result of given ID. If force is set
        to True, then an item will be created if it does not exist yet.
        '''
        sections = id.split(".")
        matchedItem = None
        parent = self._treeWidget
        for i in xrange(len(sections)):
            matchedItem = None
            if i == 0:
                for j in xrange(parent.topLevelItemCount()):
                    item = parent.topLevelItem(j)
                    if item.testName == sections[0]:
                        matchedItem = item
                        break
            else:
                for j in xrange(parent.childCount()):
                    item = parent.child(j)
                    if item.testName == ".".join(sections[:i + 1]):
                        matchedItem = item
                        break
            if matchedItem is None:
                if not force:
                    return None
                item = ResultItem(parent, testName=".".join(sections[:i + 1]))
                item.setText(0, sections[i])
                item.setIcon(0, self._ICONS["directory"])
                parent = item
            else:
                parent = matchedItem

        return parent

    def _addItem(self, result, parent, select):
        '''
        Adds items to the tree based on given result and its descendants.
        The select boolean determines whether items should be selected.
        '''
        item = None
        if parent is None:
            item = self._getItem(result.id)
            if item is None:
                parent = self._getItem(".".join(result.id.split(".")[:-1]),
                                       force=True)
        if item is None:
            item = ResultItem(parent, testName=result.id, testResult=result)
            if isinstance(result, TestSuiteResult):
                item.setIcon(0, self._ICONS["suite"])
                if item.parent().testResult is None:
                    item.parent().setIcon(0, self._ICONS["module"])
            elif isinstance(result, TestCaseResult):
                item.setIcon(0, self._ICONS["case"])
            elif isinstance(result, TestStepResult):
                item.setIcon(0, self._ICONS["step"])
            item.setText(1, result.id.rsplit(".", 1)[1])
            self._setItemStatus(item)
        if select:
            self._treeWidget.setCurrentItem(item)
        for childResult in result.children:
            self._addItem(childResult, item, select)

    def _setItemStatus(self, item):
        '''
        Sets color of a tree item adequately to status of its result and fills
        the 'Pass rate' column
        '''
        color = STATUS_COLORS[item.testResult.status]
        item.setForeground(1, color)
        item.setForeground(2, color)
        item.setFont(1, STATUS_FONTS[item.testResult.status])
        if isinstance(item.testResult, (TestCaseResult, TestSuiteResult)):
            i = p = 0
            for c in item.testResult.children:
                i += 1
                if c.status == STATUS_PASSED:
                    p += 1
            item.setText(2, "%d of %d" % (p, i))
        item.setTextAlignment(2, QtCore.Qt.AlignHCenter)

# Public methods:

    def isClosable(self):
        '''
        Returns True if tab can be closed or False otherwise.
        '''
        return self._closable

    def setClosable(self, closable):
        '''
        Sets the closable boolean attribute of the tab.
        '''
        self._closable = closable

    def update(self, result):
        '''
        Selects and updates status of a tree item corresponding to given result.
        '''
        log.debug("Selecting tree item for result '%s'" % result.id)
        item = self._getItem(result.id)
        self._setItemStatus(item)
        self._treeWidget.setCurrentItem(item)

    def refresh(self):
        '''
        Refreshes details of a test result that corresponds to the currently
        selected item.
        '''
        log.debug("Refreshing currently selected result item")
        item = self._treeWidget.currentItem()
        self._treeWidget.setCurrentItem(None)
        self._treeWidget.setCurrentItem(item)

    def expandItems(self):
        '''
        Expands tree items with statuses read from the settings option.
        '''
        items = self._treeWidget.findItems(
            "*", QtCore.Qt.MatchRecursive | QtCore.Qt.MatchWildcard)
        for item in items:
            item.setExpanded(False)
        self._treeWidget.setCurrentItem(None)
        QtGui.qApp.processEvents()
        statuses = map(lambda s: s.strip(), self._expandStatuses.getList())
        log.debug("Expanding items of results: %s" % ",".join(statuses))
        for item in items:
            if (item.testResult
                    and not isinstance(item.testResult, TestStepResult)
                    and item.testResult.status in statuses):
                item.setExpanded(True)
                parent = item.parent()
                while parent:
                    parent.setExpanded(True)
                    parent = parent.parent()

# Slots:
#@QtCore.Slot(QtGui.QTreeWidgetItem)

    def display(self, item):
        '''
        Displays details of given tree item.
        '''
        if self._view.isTabCurrent(self):
            if item is None or item.testResult is None:
                self._view.clearDetails()
            else:
                self._view.showDetails(item.testResult)

    #@QtCore.Slot()
    def expand(self):
        '''
        Expands the currently selected tree item.
        '''
        def expandRecursively(item):
            self._treeWidget.expandItem(item)
            for index in xrange(item.childCount()):
                expandRecursively(item.child(index))

        if self._view.isTabCurrent(self):
            log.debug("Expanding selected tree item")
            item = self._treeWidget.currentItem()
            if item:
                expandRecursively(item)

    #@QtCore.Slot()
    def expandAll(self):
        '''
        Expands all items in the tree.
        '''
        if self._view.isTabCurrent(self):
            log.debug("Expanding all tree items")
            self._treeWidget.expandAll()
            for i in xrange(self._COLUMN_COUNT):
                self._treeWidget.resizeColumnToContents(i)

    #@QtCore.Slot()
    def collapse(self):
        '''
        Collapses currently selected tree item.
        '''
        if self._view.isTabCurrent(self):
            log.debug("Collapsing selected tree item")
            self._treeWidget.collapseItem(self._treeWidget.currentItem())

    #@QtCore.Slot()
    def collapseAll(self):
        '''
        Collapses all items of tree.
        '''
        if self._view.isTabCurrent(self):
            log.debug("Collapsing all tree items")
            self._treeWidget.collapseAll()
            for i in xrange(self._COLUMN_COUNT):
                self._treeWidget.resizeColumnToContents(i)

    #@QtCore.Slot(QtGui.QTreeWidgetItem)
    def _updateOnExpand(self, item):
        '''
        Updates widths of columns in the tree to fit given tree item.
        '''
        if self._view.isTabCurrent(self):
            for i in xrange(self._COLUMN_COUNT):
                self._treeWidget.resizeColumnToContents(i)

    #@QtCore.Slot(QtGui.QTreeWidgetItem)
    def _updateOnCollapse(self, item):
        '''
        Clears result details in the Result view, sets the given item as
        current and updates widths of columns to fit it.
        '''
        if self._view.isTabCurrent(self):
            items = self._treeWidget.selectedItems()
            if items and items[0] is not item and items[0].parent():
                parent = items[0].parent()
                while True:
                    if parent is item:
                        self._view.clearDetails()
                        self._treeWidget.setCurrentItem(None)
                        break
                    if parent is None:
                        break
                    parent = parent.parent()
            for i in xrange(self._COLUMN_COUNT):
                self._treeWidget.resizeColumnToContents(i)
Exemplo n.º 3
0
class KeyboardDialog(QtCore.QObject):
    '''
    A dialog class for executing keyboard event.
    '''
    _DIALOG_UI = "kbevent_dialog.ui"
    _UNICODE_PREFIX = 0x01000000
    _CONFIG_NAME = viewName()
    _CONFIG_SECTION = "modifier_keys"
    _MODIFIERS = {
        'Alt': constants.KEY_CODES['LEFT_ALT'],
        'Control': constants.KEY_CODES['LEFT_CONTROL'],
        'Shift': constants.KEY_CODES['LEFT_SHIFT'],
    }

    def __init__(self, view):
        QtCore.QObject.__init__(self, view)
        self._elements = view.loadUi(self._DIALOG_UI)
        self.dialog = self._elements['dialog']
        self._character = self._elements['comboBoxChar']
        self._keycode = self._elements['lineEditKeycode']
        self._addMod = self._elements['buttonAdd']
        self._removeMod = self._elements['buttonRemove']
        self._ok = self._elements['buttonBox'].button(
            QtGui.QDialogButtonBox.Ok)
        self._modifiers = self._elements['listWidget']
        self._addEditDialog = AddEditDialog(view)

        self._character.editTextChanged.connect(self._update)
        self._keycode.textEdited.connect(self._clearAndDisable)
        self._ok.clicked.connect(self._execute)
        self._addEditDialog.modifierAccepted.connect(self._addModifier)
        self._modifiers.setSortingEnabled(True)
        self._modifiers.itemActivated.connect(self._deselectItem)
        self._modifiers.itemSelectionChanged.connect(self._switchButtons)
        self._addMod.clicked.connect(self._showAddEditDialog)
        self._removeMod.clicked.connect(self._removeModifier)

        for name, code in constants.KEY_SYMS.iteritems():
            prettyName = ' '.join([s.capitalize() for s in name.split('_')])
            self._character.addItem(prettyName, userData=str(code))
        self._character.setCompleter(None)
        self._keycode.setValidator(QtGui.QIntValidator(self.dialog))
        for name in sorted(self._MODIFIERS):
            self._createItem(name, self._MODIFIERS[name])
        self._userModifiers = []
        for name in config.get(self._CONFIG_NAME, self._CONFIG_SECTION):
            code = config.getInt(self._CONFIG_NAME, self._CONFIG_SECTION, name)
            if code is not None and code not in self._userModifiers:
                self._createItem(name, code)
                self._userModifiers.append(name)
        self._deviceTab = None

# Slots:
#@QtCore.Slot(unicode)

    def _update(self, string):
        '''
        Fills an entry field containing key code.
        '''
        if len(string) == 0:
            return

        self._ok.setEnabled(True)
        data = self._character.itemData(self._character.findText(string))
        if data is None:
            code = ord(string[-1])
            if code > 127:  # not ASCII code
                code += self._UNICODE_PREFIX
            self._character.setEditText(string[-1])
            self._keycode.setText(str(code))
        else:
            self._keycode.setText(data)

    #@QtCore.Slot(unicode)
    def _clearAndDisable(self, string):
        '''
        Clears an entry filed with character/button name and disables
        OK button if there is no key code.
        '''
        self._ok.setEnabled(len(string) > 0)
        self._character.clearEditText()

    #@QtCore.Slot()
    def _execute(self):
        '''
        Executes keyboard event based on key code and modifiers.
        '''
        log.info('Executing keyboard event')
        path = self._deviceTab.selectedItemPath()
        if path is None:
            dialogs.runWarning("No accessible item is selected. ",
                               "Keyboard events unavailable")
        else:
            keycode = int(self._keycode.text())
            items = self._modifiers.findItems('', QtCore.Qt.MatchContains)
            mods = [
                self._itemData(item)[1] for item in items
                if item.checkState() == QtCore.Qt.Checked
            ]
            self._deviceTab.device.requestDevice("requestKeyboardEvent", path,
                                                 keycode, mods)

    #@QtCore.Slot(unicode, int)
    def _addModifier(self, name, code):
        '''
        Adds modifier to the list.
        '''
        selected = self._modifiers.selectedItems()
        if name in self._MODIFIERS:
            dialogs.runWarning("Default modifier '%s' cannot be redefined" %
                               name)
            return
        if selected:
            self._setItemData(selected[0], name, code)
        elif name in self._userModifiers:
            if dialogs.runQuestion("Modifier '%s' already exists. Do you "
                                   "want to update it?" % name):
                item = self._modifiers.findItems(name,
                                                 QtCore.Qt.MatchExactly)[0]
                self._setItemData(item, name, code)
            else:
                return
        else:
            self._userModifiers.append(name)
            self._modifiers.scrollToItem(self._createItem(name, code))
        config.set(self._CONFIG_NAME, self._CONFIG_SECTION, name, code)

    #@QtCore.Slot()
    def _removeModifier(self):
        '''
        Removes modifier from the list.
        '''
        selected = self._modifiers.selectedItems()
        for item in selected:
            removed = self._modifiers.takeItem(self._modifiers.row(item))
            if removed:
                self._userModifiers.remove(removed.text())
                config.remove(self._CONFIG_NAME, self._CONFIG_SECTION,
                              removed.text())

    #@QtCore.Slot()
    def _showAddEditDialog(self):
        '''
        Shows dialog for adding/editing modifiers.
        '''
        selected = self._modifiers.selectedItems()
        name = code = None
        if selected:
            if self._itemData(selected[0])[0] in self._MODIFIERS:
                self._modifiers.clearSelection()
            else:
                name, code = self._itemData(selected[0])
        self._addEditDialog.run(name, code, self._addMod.text() == "Add")

    #@QtCore.Slot()
    def _deselectItem(self, item):
        '''
        Deselects item if selected.
        '''
        if item.isSelected():
            item.setSelected(False)

    #@QtCore.Slot()
    def _switchButtons(self):
        '''
        Switches button Add (modifier) to Edit (modifier) and vice versa
        depending on selected modifiers. Also disables/enables Add and Remove
        buttons.
        '''
        selected = self._modifiers.selectedItems()
        self._addMod.setDisabled(True)
        self._removeMod.setDisabled(True)
        if len(selected) > 0:
            self._addMod.setDisabled(False)
            if selected[0].text() in self._userModifiers:
                self._addMod.setText("Edit")
                self._removeMod.setDisabled(False)
            else:
                self._addMod.setText("Add")
        else:
            self._addMod.setText("Add")
            self._removeMod.setDisabled(False)

# Private methods:

    def _createItem(self, name, code):
        '''
        Creates and returns a list item.
        '''
        item = QtGui.QListWidgetItem()
        item.setFlags(QtCore.Qt.ItemIsSelectable
                      | QtCore.Qt.ItemIsUserCheckable
                      | QtCore.Qt.ItemIsEnabled)
        item.setCheckState(QtCore.Qt.Unchecked)
        self._setItemData(item, name, code)
        self._modifiers.addItem(item)
        return item

    def _setItemData(self, item, name, code):
        '''
        Updates modifier name and code associated with an item.
        '''
        item.setText(name)
        item.setData(QtCore.Qt.UserRole, code)
        item.setToolTip("Code: %d" % code)

    def _itemData(self, item):
        '''
        Returns modifier name and code associated with an item.
        '''
        return item.text(), item.data(QtCore.Qt.UserRole)


# Public methods:

    def run(self, deviceTab):
        '''
        Runs the keyboard event dialog.
        '''
        log.debug("Running keyboard event dialog")
        self._deviceTab = deviceTab
        self.dialog.show()

    def hide(self):
        '''
        Hides the keyboard event dialog.
        '''
        log.debug("Hiding keyboard event dialog")
        self.dialog.done(1)
Exemplo n.º 4
0
class Test(View):
    '''
    A view for running test cases.
    '''
    NAME = viewName()
    _UI_FILE = "test_view.ui"
    _ICON_FILE = ":/test/icons/system-run.png"

    _checkOnConnect = settings.get(NAME,
                                   "options",
                                   "check_on_connect",
                                   default="Yes",
                                   force=True)

    # Menus and Tool bar
    _menuFile = ("actionAdd", "actionRemove", None)
    _menuEdit = ("actionExpand", "actionExpandAll", "actionCollapse",
                 "actionCollapseAll", None)
    _menuView = ("actionRefresh", None)
    _toolBar = ("actionAdd", "actionRemove", None, ("actionExpand",
                                                    "actionExpandAll"),
                ("actionCollapse", "actionCollapseAll"), "actionRefresh", None,
                "actionStart", "actionPause", "actionResume", "actionStop")

    def __init__(self, parent):
        View.__init__(self, parent)
        self._devices = TestDeviceList(self._elements["treeWidgetDevices"])

        self._actionStart = self._elements["actionStart"]
        self._actionStop = self._elements["actionStop"]
        self._actionPause = self._elements["actionPause"]
        self._actionResume = self._elements["actionResume"]

        self._actionStart.triggered.connect(self._startTests)
        self._actionStop.triggered.connect(self._stopTests)
        self._actionPause.triggered.connect(self._pauseTests)
        self._actionResume.triggered.connect(self._resumeTests)

        self._actionStart.setVisible(True)
        self._actionStop.setVisible(False)
        self._actionPause.setVisible(False)
        self._actionResume.setVisible(False)

        # Summary channel
        channels.add("SummaryChannel", "_ui_summary")

        # Progress channel
        pBar = QtGui.QProgressBar()
        pBar.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        font = pBar.font()
        font.setBold(True)
        pBar.setFont(font)
        self._parent.getStatusBar().addPermanentWidget(pBar, 1)
        self._progress = ProgressChannelHelper(pBar)
        channels.add(ProgressChannel, "_ui_progress", progress=self._progress)
        self._progress.testStarted.connect(self._onTestStarted)
        self._progress.testStopped.connect(self._onTestStopped)
        self._progress.stopped.connect(self._onStopped)

        self._tests = Tests(self._elements["treeWidgetLocations"],
                            self._elements["treeWidgetTests"],
                            self._elements["treeWidgetModels"])

        self._elements["actionAdd"].triggered.connect(self._tests.addLocation)
        self._elements["actionRemove"].triggered.connect(
            self._tests.removeLocation)
        self._elements["actionExpand"].triggered.connect(
            self._tests.expandSelected)
        self._elements["actionExpandAll"].triggered.connect(
            self._tests.expandAll)
        self._elements["actionCollapse"].triggered.connect(
            self._tests.collapseSelected)
        self._elements["actionCollapseAll"].triggered.connect(
            self._tests.collapseAll)
        self._elements["actionRefresh"].triggered.connect(self._tests.refresh)

        # Initialize private test variables
        self._suiteRuns = 0
        self._todoSuites = 0
        self._testResult = None
        self._testRunner = None

# Public methods:

    def saveState(self):
        '''
        Saves the view's state to configuration.
        '''
        View.saveState(self)
        self._tests.saveState()

    def loadState(self):
        '''
        Loads the view's state from configuration.
        '''
        View.loadState(self)
        self._tests.loadState()

# Slots:
#@QtCore.Slot(Device)

    def _deviceConnected(self, device):
        '''
        Adds a device to list.
        '''
        self._devices.add(device, check=self._checkOnConnect.getBool())

    #@QtCore.Slot(Device)
    def _deviceDisconnected(self, device, error):
        '''
        Removes given device from list. The error parameter can be set to True
        to indicate that the device was disconnected due to an error.
        '''
        self._devices.remove(device)

    #@QtCore.Slot()
    def _startTests(self):
        '''
        Starts execution of tests.
        '''
        log.debug("Starting tests")
        self._actionStart.setVisible(False)
        devices = self._devices.getChecked()
        if not devices:
            runWarning("Select some devices first")
            self._actionStart.setVisible(True)
            return
        tests = self._tests.getCheckedTests()
        if not tests:
            self._actionStart.setVisible(True)
            return
        if sum([test.count() for test in tests]) == 0:
            runWarning("Selected test suites do not contain any test cases")
            self._actionStart.setVisible(True)
            return

        self._suiteRuns = 0
        self._todoSuites = len(tests)
        self._testResult = testresult.TestResult()
        self._testRunner = TestRunner(devices, tests, self._testResult)
        self._devices.deviceChecked.connect(self._testRunner.addDevice)
        self._devices.deviceUnchecked.connect(self._testRunner.removeDevice)
        self._devices.setWarning(True)

        self._testRunner.start()

        self._actionStop.setVisible(True)
        self._actionPause.setVisible(True)

    #@QtCore.Slot()
    def _stopTests(self):
        '''
        Stops execution of tests.
        '''
        log.debug("Stopping tests")
        self._actionStart.setVisible(True)
        self._actionStop.setVisible(False)
        self._actionPause.setVisible(False)
        self._actionResume.setVisible(False)
        self._testRunner.stop()

    #@QtCore.Slot()
    def _pauseTests(self):
        '''
        Pauses execution of tests.
        '''
        log.debug("Pausing tests")
        self._actionStart.setVisible(False)
        self._actionStop.setVisible(True)
        self._actionPause.setVisible(False)
        self._actionResume.setVisible(True)
        self._testRunner.pause()

    #@QtCore.Slot()
    def _resumeTests(self):
        '''
        Resumes execution of tests.
        '''
        log.debug("Resuming tests")
        self._actionStart.setVisible(False)
        self._actionStop.setVisible(True)
        self._actionPause.setVisible(True)
        self._actionResume.setVisible(False)
        self._testRunner.resume()

    #@QtCore.Slot(testresult.TestResultBase, testresult.DeviceExecResult)
    def _onTestStarted(self, result, device):
        '''
        Handles a start test execution of a test represented by the given
        result.
        '''
        if isinstance(result, testresult.TestCaseResult):
            log.debug("Began execution of test case: %s" % result.id)
        # If it is a top-level test suite result then increase the counter of
        # running top-level test suites
        if result.parent is None:
            self._suiteRuns += 1

    #@QtCore.Slot(testresult.TestResultBase, testresult.DeviceExecResult)
    def _onTestStopped(self, result, device):
        '''
        Handles a stop test execution of a test represented by the given
        result.
        '''
        if isinstance(result, testresult.TestCaseResult):
            log.debug("Finished execution of test case: %s" % result.id)
        # If it is a top-level test suite result then decrease the counters of
        # running top-level test suites and to do test suites.
        if result.parent is None:
            self._suiteRuns -= 1
            self._todoSuites -= 1
            # If all top-level test suites are done then join() the test runner
            if self._suiteRuns == 0 and self._todoSuites <= 0:
                self._testRunner.join()

    #@QtCore.Slot()
    def _onStopped(self):
        '''
        Shows summary dialog after finishing test executions.
        '''
        log.debug("All tests finished")

        self._actionStart.setVisible(True)
        self._actionStop.setVisible(False)
        self._actionPause.setVisible(False)
        self._actionResume.setVisible(False)

        self._devices.deviceChecked.disconnect(self._testRunner.addDevice)
        self._devices.deviceUnchecked.disconnect(self._testRunner.removeDevice)
        self._devices.setWarning(False)

        files = []
        for c in self._testRunner.result.get():
            if isinstance(c, channels.TestResultFileChannel) and c.isActive():
                files.append((c.name, c.filePath()))
        dialog = ReportDialog(
            self._testResult.get(name='_ui_summary')[0].getSummary(), files,
            len(self._devices.getChecked()) > 0)
        dialog.closed.connect(self._progress.reset,
                              type=QtCore.Qt.DirectConnection)
        dialog.runAgainClicked.connect(self._startTests,
                                       type=QtCore.Qt.QueuedConnection)
        dialog.showDetailsClicked.connect(self._showDetails)
        dialog.run()

    #@QtCore.Slot()
    def _showDetails(self):
        '''
        Shows execution result in Result view.
        '''
        resultView = self._parent.getView("result")
        if resultView is not None:
            log.debug("Showing details in Result view")
            resultView.activate()
            resultView.showLastResult()
Exemplo n.º 5
0
class Tests(QtCore.QObject):
    '''
    Class for management of locations list, tests tree and models tree.
    '''
    _TESTCASES_DIR = "testcases"

    _CONFIG_NAME = viewName()
    _CONFIG_SECTION_LOCATIONS = "locations"
    _expandOnRefresh = settings.get(_CONFIG_NAME,
                                    "options",
                                    "expand_on_refresh",
                                    default="No",
                                    force=True)

    _ICONS = {
        "directory": QtGui.QIcon(":/test/icons/folder-grey.png"),
        "module": QtGui.QIcon(":/test/icons/text-x-python.png"),
        "suite": QtGui.QIcon(":/test/icons/source_moc.png"),
        "case": QtGui.QIcon(":/test/icons/inode-blockdevice.png")
    }

    def __init__(self, locsTree, testsTree, modelsTree):
        '''
        Takes trees for locations, tests and models, initializes a test
        loader instance and loads default paths from configuration.
        '''
        QtCore.QObject.__init__(self)
        self._locsTree = locsTree
        self._testsTree = testsTree
        self._modelsTree = modelsTree
        self._locsTree.itemChanged.connect(self._updateLocations)
        self._testsTree.itemChanged.connect(self._updateCheckboxes)
        self._loader = TestLoader()
        self._loaded = False
        self._locItems = {}
        self.loadState()

# Private methods:

    def _addLocation(self, path):
        '''
        Adds a location and a corresponding item to the locations tree.
        Returns None on success or an error message on failure.
        '''
        if path in self._locItems:
            return
        location.add(path, enabled=False)
        item = QtGui.QTreeWidgetItem()
        item.setText(0, path)
        item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable
                      | QtCore.Qt.ItemIsSelectable)
        item.setCheckState(0, QtCore.Qt.Unchecked)
        self._locsTree.addTopLevelItem(item)
        self._locsTree.resizeColumnToContents(0)
        self._locItems[path] = item

    def _changeExpansionState(self, state, selected=True):
        '''
        Manipulates expansion states of items in the tests tree. If selected is
        True (default), then only selected items are affected.
        '''
        def expandWithChildren(item, state):
            item.setExpanded(state)
            for i in xrange(item.childCount()):
                expandWithChildren(item.child(i), state)

        if selected:
            for item in self._testsTree.selectedItems():
                expandWithChildren(item, state)
        else:
            for i in xrange(self._testsTree.topLevelItemCount()):
                expandWithChildren(self._testsTree.topLevelItem(i), state)
        self._testsTree.resizeColumnToContents(0)

    def _updateModels(self):
        '''
        Refreshes the models tree.
        '''
        self._modelsTree.clear()
        for name, path in location.getModels().iteritems():
            QtGui.QTreeWidgetItem(self._modelsTree, [name, path])
        self._modelsTree.resizeColumnToContents(0)
        self._modelsTree.resizeColumnToContents(1)

# Slots:
#@QtCore.Slot()

    def addLocation(self):
        '''
        Adds an item to locations tree and enables it. A warning is displayed
        if path is invalid.
        '''
        path = QtGui.QFileDialog.getExistingDirectory(window())
        if not path:
            return
        log.debug("Adding test path '%s'" % path)
        message = self._addLocation(path)
        if message is not None:
            runWarning(message)
            return
        self._locItems[path].setCheckState(0, QtCore.Qt.Checked)

    #@QtCore.Slot()
    def removeLocation(self):
        '''
        Removes selected items from locations tree and refreshes
        the tests tree.
        '''
        items = self._locsTree.selectedItems()
        if not (items and runQuestion(
                "Are you sure you want to remove %s?\n\n %s" %
            ("these locations" if len(items) > 1 else "this location",
             "\n".join(item.text(0) for item in items)))):
            return
        for item in items:
            path = item.text(0)
            log.debug("Removing test path '%s'" % path)
            location.remove(path)
            self._locsTree.takeTopLevelItem(
                self._locsTree.indexOfTopLevelItem(item))
            self._locItems.pop(path)
        self.refresh()

    #@QtCore.Slot()
    def getCheckedTests(self):
        '''
        Loads checked tests cases and returns them in a list on success
        or None on failure.
        '''
        def findCheckedNames(tests, item):
            if item.checkState(0) == QtCore.Qt.Checked:
                tests.append(item.testName)
                return
            elif item.checkState(0) == QtCore.Qt.PartiallyChecked:
                for i in xrange(item.childCount()):
                    findCheckedNames(tests, item.child(i))

        names = []
        for i in xrange(self._testsTree.topLevelItemCount()):
            findCheckedNames(names, self._testsTree.topLevelItem(i))
        if not names:
            runWarning("Select some test cases first")
            return names
        tests, errors = self._loader.loadFromNames(*names)
        if errors:
            if self._loaded:
                dialog = LoadingErrorDialog(
                    "Errors occurred while loading "
                    "test cases", errors)
                dialog.run()
                return []

        return tests

    #@QtCore.Slot()
    def refresh(self):
        '''
        Clears and fills the tests tree and the models tree with contents from
        currently enabled locations.
        '''
        def makeItem(parent):
            item = TestItem(parent)
            item.setFlags(QtCore.Qt.ItemIsEnabled
                          | QtCore.Qt.ItemIsUserCheckable
                          | QtCore.Qt.ItemIsSelectable)
            item.setCheckState(0, QtCore.Qt.Unchecked)
            return item

        def buildSuite(name, suite, parent):
            item = makeItem(parent)
            item.setText(
                0, name if name is not None else suite.__class__.__name__)
            item.testName = ".".join((item.parent().testName, item.text(0)))
            if isinstance(suite, TestSuite):
                item.setIcon(0, self._ICONS["suite"])
                for name, childSuite in suite:
                    buildSuite(name, childSuite, item)
            elif isinstance(suite, TestCase):
                item.setIcon(0, self._ICONS["case"])
            else:
                raise TypeError()

        def buildTree(tree, parent):
            for key in sorted(tree):
                value = tree[key]
                if key is None and isinstance(value, list):
                    if not value and len(tree) == 1:
                        if parent.parent() is not None:
                            parent.parent().removeChild(parent)
                        else:
                            self._testsTree.takeTopLevelItem(
                                self._testsTree.indexOfTopLevelItem(parent))
                        continue
                    suite = None
                    for suite in value:
                        buildSuite(None, suite, parent)
                    if value:
                        r = suite.result()
                        if os.path.split(r.path)[1] != r.id.split(".")[-2]:
                            parent.setIcon(0, self._ICONS["module"])
                elif isinstance(value, dict):
                    item = makeItem(parent)
                    item.setText(0, key)
                    item.setIcon(0, self._ICONS["directory"])
                    item.testName = (".".join(
                        (item.parent().testName,
                         key)) if item.parent() is not None else key)
                    buildTree(value, item)

        self._testsTree.clear()
        tree, errors = self._loader.loadTree()
        if errors:
            if self._loaded:
                dialog = LoadingErrorDialog(
                    "Errors occurred while loading "
                    "test cases", errors)
                dialog.run()
        buildTree(tree, self._testsTree)
        self._testsTree.resizeColumnToContents(0)
        if self._expandOnRefresh.getBool():
            self.expandAll()
        self._updateModels()

    #@QtCore.Slot()
    def expandSelected(self):
        '''
        Expands selected test items including all descendants.
        '''
        self._changeExpansionState(state=True, selected=True)

    #@QtCore.Slot()
    def expandAll(self):
        '''
        Expands all test items including all descendants.
        '''
        self._changeExpansionState(state=True, selected=False)

    #@QtCore.Slot()
    def collapseSelected(self):
        '''
        Collapses selected test items including all descendants.
        '''
        self._changeExpansionState(state=False, selected=True)

    #@QtCore.Slot()
    def collapseAll(self):
        '''
        Collapses all test items including all descendants.
        '''
        self._changeExpansionState(state=False, selected=False)

    #@QtCore.Slot(QtGui.QTreeWidgetItem, int)
    def _updateLocations(self, item, column):
        '''
        Enables or disables a location and refreshes the tests tree.
        '''
        if item.checkState(0) == QtCore.Qt.Checked:
            errors = location.enable(item.text(0))
            if errors:
                if self._loaded:
                    dialog = LoadingErrorDialog(
                        "Errors occurred while enabling"
                        " a location", errors)
                    dialog.run()
                item.setCheckState(0, QtCore.Qt.Unchecked)
                return
        else:
            location.disable(item.text(0))
        self.refresh()

    #@QtCore.Slot(TestItem, int)
    def _updateCheckboxes(self, item, column):
        '''
        Updates states of check boxes in the tests tree in three-state manner. 
        '''
        def updateChildren(item, state):
            item.setCheckState(0, state)
            for i in xrange(item.childCount()):
                updateChildren(item.child(i), state)

        if item is not self._testsTree.currentItem():
            return
        updateChildren(item, item.checkState(0))
        parent = item.parent()
        while parent is not None:
            states = [
                parent.child(i).checkState(0)
                for i in xrange(parent.childCount())
            ]
            if (len(filter(lambda s: s == QtCore.Qt.Checked,
                           states)) == len(states)):
                state = QtCore.Qt.Checked
            elif (len(filter(lambda s: s == QtCore.Qt.Unchecked,
                             states)) == len(states)):
                state = QtCore.Qt.Unchecked
            else:
                state = QtCore.Qt.PartiallyChecked
            parent.setCheckState(0, state)
            parent = parent.parent()

# Public methods:

    def saveState(self):
        '''
        Saves paths to configuration.
        '''
        config.set(self._CONFIG_NAME, self._CONFIG_SECTION_LOCATIONS,
                   "enabled", location.get(enabled=True))
        config.set(self._CONFIG_NAME, self._CONFIG_SECTION_LOCATIONS,
                   "disabled", location.get(enabled=False))

    def loadState(self):
        '''
        Restores paths from configuration.
        '''
        enabled = config.getList(self._CONFIG_NAME,
                                 self._CONFIG_SECTION_LOCATIONS, "enabled", [])
        disabled = config.getList(self._CONFIG_NAME,
                                  self._CONFIG_SECTION_LOCATIONS, "disabled",
                                  [])
        for path in disabled:
            message = self._addLocation(path)
            if message:
                log.warning(message)
        for path in enabled:
            message = self._addLocation(path)
            if message:
                log.warning(message)
            else:
                self._locItems[path].setCheckState(0, QtCore.Qt.Checked)
        self._loaded = True
Exemplo n.º 6
0
class HighlightableItem(QtGui.QTreeWidgetItem):
    '''
    Class of items that can be highlighted.
    '''

    section = settings.get(viewName(), "highlight", force=True)
    _enabled = section.get("enabled", default="Yes")
    _mode = section.get("mode", default="selection")
    _red = section.get("red", default=0)
    _green = section.get("green", default=255)
    _blue = section.get("blue", default=255)
    _shading = section.get("shading", default=5)
    del section

    @classmethod
    def isHightlightEnabled(cls):
        '''
        Returns True if the highlight feature is enabled in settings. 
        '''
        return cls._enabled.getBool()

    @classmethod
    def hightlightMode(cls):
        '''
        Returns the highlight mode.
        '''
        return cls._mode.get().lower()

    def __init__(self, *args, **kwargs):
        QtGui.QTreeWidgetItem.__init__(self, *args, **kwargs)
        self._colorized = False
        mode = self.hightlightMode()

        if self.isHightlightEnabled() and mode in ("all", "selection"):
            if not self.parent() and mode == "all":
                self.setColor()
            else:
                self.updateColor()

    def resetColor(self):
        '''
        Changes background color of an item to transparent.
        '''
        self._colorized = False
        for i in range(self.treeWidget().columnCount()):
            self.setBackground(i, QtCore.Qt.transparent)

    def setColor(self):
        '''
        Highlights an item.
        '''
        self._colorized = True
        color = QtGui.QColor()
        color.setRed(self._red.getInt())
        color.setGreen(self._green.getInt())
        color.setBlue(self._blue.getInt())
        for i in range(self.treeWidget().columnCount()):
            self.setBackground(i, color)

    def updateColor(self):
        '''
        Makes an item highlighted using a slightly darker color than
        its parent is.
        '''
        step = self._shading.getInt()
        if self.parent() and self.parent().isColorized():
            self._colorized = True
            c = self.parent().background(0).color().darker(100 + step)
            for i in range(self.treeWidget().columnCount()):
                self.setBackground(i, c)

    def isColorized(self):
        '''
        Returns True if item setColor() was called or False otherwise.
        '''
        return self._colorized
Exemplo n.º 7
0
class Explore(View):
    '''
    A view for exploring accessible widgets on multiple devices.
    '''
    NAME = viewName()
    _UI_FILE = "explore_view.ui"
    _ICON_FILE = ":/explore/icons/system-search.png"

    _CONFIG_SECTION_MENU = "menu"

    # Menus and Tool bar
    _menuFile = ("actionOpen", ("Recent &Files", "actionClearMenu"), None,
                 "actionSave", "actionSaveAll", None, "actionClose")
    _menuEdit = ("actionSearch", None, "actionExpand", "actionExpandAll",
                 "actionCollapse", "actionCollapseAll", None)
    _menuView = ("actionRefresh", "actionRefreshAll", None)
    _toolBar = ("actionOpen", ("actionSave", "actionSaveAll"), "actionClose",
                None, ("actionExpand", "actionExpandAll"),
                ("actionCollapse", "actionCollapseAll"),
                ("actionRefresh", "actionRefreshAll"), None, "actionSearch")

    def __init__(self, parent):
        View.__init__(self, parent)
        self._tabWidget = self._elements["tabWidget"]
        self._tabWidget.currentChanged[int].connect(self._updateView)
        self._tabWidget.tabCloseRequested.connect(self._close)
        self._tabWidget.setTabBar(ClosableTabBar())
        self._actionRefresh = self._elements["actionRefresh"]
        self._actionRefreshAll = self._elements["actionRefreshAll"]
        self._actionExpand = self._elements["actionExpand"]
        self._actionExpandAll = self._elements["actionExpandAll"]
        self._actionCollapse = self._elements["actionCollapse"]
        self._actionCollapseAll = self._elements["actionCollapseAll"]
        self._actionSearch = self._elements["actionSearch"]
        self._actionSave = self._elements["actionSave"]
        self._actionSaveAll = self._elements["actionSaveAll"]
        self._actionOpen = self._elements["actionOpen"]
        self._actionClose = self._elements["actionClose"]
        self._actionOpen.triggered.connect(self._openDialog)
        self._actionClose.triggered.connect(self._close)

        # Recent files menu
        self._recentFiles = LastValues(self.NAME, self._CONFIG_SECTION_MENU,
                                       "recent", 5)
        self._actionClearMenu = self._elements["actionClearMenu"]
        self._menuRecentFiles = self._menuFile[1]
        self._actionClearMenu.triggered.connect(self._recentFiles.clear)
        self._actionClearMenu.triggered.connect(self.updateRecentFiles)

        self._tabs = {}
        self._offlineDevs = {}
        self._readOnly = False

        self.search = SearchDialog(self)
        self._actionSearch.triggered.connect(self.search.run)

        self._dialogs = {
            'keyboard': KeyboardDialog(self),
            'mouse': MouseDialog(self)
        }

        # widgets
        self._states = self._elements["listWidgetStates"]
        self._relations = self._elements["listWidgetRelations"]
        self._attributes = self._elements["treeWidgetAttributes"]
        self._text = self._elements["textEditText"]
        self._changeText = self._elements["buttonChangeText"]
        self._actions = self._elements["groupBoxActions"]
        self._actionButtons = QtGui.QButtonGroup(self)
        self._value = self._elements["spinBoxValue"]
        self._changeValue = self._elements["buttonChangeValue"]
        self._mouse = self._elements["buttonMouse"]
        self._keyboard = self._elements["buttonKeyboard"]
        self.clear()

# Private methods:

    def _addDeviceTab(self, device):
        '''
        Adds the device tab for given device.
        '''
        log.debug("Adding device tab: %s" % device)
        tab = DeviceTab(device, self)
        self._tabs[device] = tab
        index = self._tabWidget.addTab(tab.tab, device.name)
        address, port = device.address
        if port:
            tooltip = "%s:%d" % (address, port)
        else:
            tooltip = address
        self._tabWidget.setTabToolTip(index, tooltip)
        self._tabWidget.setCurrentIndex(index)
        self._actionRefresh.triggered.connect(tab.refresh)
        self._actionRefreshAll.triggered.connect(tab.refreshAll)
        self._actionExpand.triggered.connect(tab.expand)
        self._actionExpandAll.triggered.connect(tab.expandAll)
        self._actionCollapse.triggered.connect(tab.collapse)
        self._actionCollapseAll.triggered.connect(tab.collapseAll)
        if not tab.isOffline():
            self._actionSave.triggered.connect(tab.save)
            self._actionSaveAll.triggered.connect(tab.saveAll)
            self._actionButtons.buttonClicked.connect(tab.doAction)
            self._mouse.clicked.connect(self._callMouseDialog)
            self._keyboard.clicked.connect(self._callKeyboardDialog)

    def _removeDeviceTab(self, device):
        '''
        Removes the device tab associated with given device.
        '''
        tab = self._tabs.pop(device, None)
        if tab is None:
            return
        log.debug("Removing device tab: %s" % device)
        self._tabWidget.removeTab(self._tabWidget.indexOf(tab.tab))
        if tab.isOffline():
            self._offlineDevs.pop(device.address[0])

    def _setInfoActive(self, devTab):
        '''
        Enables or disables interactions with right panel based on the state
        of given device tab.
        '''
        if devTab.isOffline() or not devTab.isActive():
            log.debug("Disabling interactions with right panel")
            self._mouse.setEnabled(False)
            self._keyboard.setEnabled(False)
            self._readOnly = True
        else:
            log.debug("Enabling interactions with right panel")
            self._mouse.setEnabled(True)
            self._keyboard.setEnabled(True)
            self._readOnly = False

    def _open(self, path):
        '''
        Opens a dump in a new tab and returns True on success or False
        on failure.
        '''
        if path in self._offlineDevs:
            self._offlineDevs[path].disconnectDevice()
        try:
            dev = OfflineDevice(os.path.split(path)[1], file=path)
            dev.responseReceived.connect(
                lambda id: self._deviceResponseReceived(dev, id))
            dev.connected.connect(lambda: self._deviceConnected(dev))
            dev.disconnected.connect(
                lambda: self._deviceDisconnected(dev, False))
            log.debug("Connecting off-line device: %s" % dev.name)
            dev.connectDevice()
            self._offlineDevs[path] = dev
            return True
        except Exception, ex:
            dialogs.runError("Error occurred while loading dump:\n%s" %
                             str(ex))
            return False