diff options
author | Yoann Lopes <yoann.lopes@nokia.com> | 2010-10-27 11:02:23 +0200 |
---|---|---|
committer | Yoann Lopes <yoann.lopes@nokia.com> | 2010-10-27 11:02:23 +0200 |
commit | 57f428ed3327dc5c9ad10a46daa6c597b6f73729 (patch) | |
tree | 5eb8908398a999e216fdff77ba72764ce824ec43 |
Initial commit.
-rw-r--r-- | clearablelineedit.cpp | 72 | ||||
-rw-r--r-- | clearablelineedit.h | 66 | ||||
-rw-r--r-- | images.qrc | 21 | ||||
-rw-r--r-- | images/autotester.icns | bin | 0 -> 39258 bytes | |||
-rw-r--r-- | images/autotester.png | bin | 0 -> 6531 bytes | |||
-rw-r--r-- | images/build.png | bin | 0 -> 2163 bytes | |||
-rw-r--r-- | images/check.png | bin | 0 -> 3346 bytes | |||
-rw-r--r-- | images/delete.png | bin | 0 -> 1405 bytes | |||
-rw-r--r-- | images/dir.png | bin | 0 -> 862 bytes | |||
-rw-r--r-- | images/edit-clear.png | bin | 0 -> 1682 bytes | |||
-rw-r--r-- | images/error.png | bin | 0 -> 498 bytes | |||
-rw-r--r-- | images/filesave.png | bin | 0 -> 1133 bytes | |||
-rw-r--r-- | images/gear.png | bin | 0 -> 2154 bytes | |||
-rw-r--r-- | images/minus.png | bin | 0 -> 258 bytes | |||
-rw-r--r-- | images/next.png | bin | 0 -> 494 bytes | |||
-rw-r--r-- | images/plus.png | bin | 0 -> 541 bytes | |||
-rw-r--r-- | images/reload.png | bin | 0 -> 1414 bytes | |||
-rw-r--r-- | images/start.png | bin | 0 -> 739 bytes | |||
-rw-r--r-- | images/stop.png | bin | 0 -> 314 bytes | |||
-rw-r--r-- | images/test.png | bin | 0 -> 367 bytes | |||
-rw-r--r-- | images/warn.png | bin | 0 -> 679 bytes | |||
-rw-r--r-- | logger.cpp | 72 | ||||
-rw-r--r-- | logger.h | 67 | ||||
-rw-r--r-- | main.cpp | 62 | ||||
-rw-r--r-- | mainwindow.cpp | 1161 | ||||
-rw-r--r-- | mainwindow.h | 163 | ||||
-rw-r--r-- | mainwindow.ui | 787 | ||||
-rw-r--r-- | qt-autotester.pro | 46 | ||||
-rw-r--r-- | qtdirectory.cpp | 275 | ||||
-rw-r--r-- | qtdirectory.h | 120 | ||||
-rw-r--r-- | qtdirectorydialog.cpp | 325 | ||||
-rw-r--r-- | qtdirectorydialog.h | 97 | ||||
-rw-r--r-- | qtdirectorydialog.ui | 129 | ||||
-rw-r--r-- | test.cpp | 532 | ||||
-rw-r--r-- | test.h | 155 | ||||
-rw-r--r-- | testgraphicsview.cpp | 241 | ||||
-rw-r--r-- | testgraphicsview.h | 103 | ||||
-rw-r--r-- | testprofile.cpp | 271 | ||||
-rw-r--r-- | testprofile.h | 141 | ||||
-rw-r--r-- | testresultwidget.cpp | 163 | ||||
-rw-r--r-- | testresultwidget.h | 88 | ||||
-rw-r--r-- | testresultwidget.ui | 166 | ||||
-rw-r--r-- | testview.cpp | 341 | ||||
-rw-r--r-- | testview.h | 101 | ||||
-rw-r--r-- | testviewlayout.cpp | 190 | ||||
-rw-r--r-- | testviewlayout.h | 99 |
46 files changed, 6054 insertions, 0 deletions
diff --git a/clearablelineedit.cpp b/clearablelineedit.cpp new file mode 100644 index 0000000..6f5fdee --- /dev/null +++ b/clearablelineedit.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "clearablelineedit.h" + +#include <QPushButton> +#include <QHBoxLayout> + +ClearableLineEdit::ClearableLineEdit(QWidget *parent) : + QLineEdit(parent) +{ + clearButton = new QPushButton(this); + clearButton->setCursor(Qt::ArrowCursor); + clearButton->setIcon(QIcon::fromTheme("edit-clear", QIcon(":/images/edit-clear.png"))); + clearButton->setStyleSheet("background-color: transparent; border: 0"); + QHBoxLayout *clearLayout = new QHBoxLayout(this); + clearLayout->setAlignment(Qt::AlignRight); + setLayout(clearLayout); + clearLayout->addWidget(clearButton); + clearButton->setVisible(false); + + connect(this, SIGNAL(textChanged(QString)), this, SLOT(on_textChanged(QString))); + connect(clearButton, SIGNAL(clicked()), this, SLOT(on_clearClicked())); +} + +void ClearableLineEdit::on_textChanged(const QString &text) +{ + clearButton->setVisible(!text.isEmpty()); +} + +void ClearableLineEdit::on_clearClicked() +{ + clear(); +} diff --git a/clearablelineedit.h b/clearablelineedit.h new file mode 100644 index 0000000..672b919 --- /dev/null +++ b/clearablelineedit.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CLEARABLELINEEDIT_H +#define CLEARABLELINEEDIT_H + +#include <QLineEdit> + +class QPushButton; + +class ClearableLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit ClearableLineEdit(QWidget *parent = 0); + +signals: + +protected Q_SLOTS: + void on_textChanged(const QString &text); + void on_clearClicked(); + +private: + QPushButton *clearButton; + +}; + +#endif // CLEARABLELINEEDIT_H diff --git a/images.qrc b/images.qrc new file mode 100644 index 0000000..77ebb47 --- /dev/null +++ b/images.qrc @@ -0,0 +1,21 @@ +<RCC> + <qresource prefix="/"> + <file>images/stop.png</file> + <file>images/start.png</file> + <file>images/plus.png</file> + <file>images/minus.png</file> + <file>images/reload.png</file> + <file>images/filesave.png</file> + <file>images/delete.png</file> + <file>images/test.png</file> + <file>images/dir.png</file> + <file>images/build.png</file> + <file>images/check.png</file> + <file>images/gear.png</file> + <file>images/warn.png</file> + <file>images/next.png</file> + <file>images/error.png</file> + <file>images/autotester.png</file> + <file>images/edit-clear.png</file> + </qresource> +</RCC> diff --git a/images/autotester.icns b/images/autotester.icns Binary files differnew file mode 100644 index 0000000..a5defd0 --- /dev/null +++ b/images/autotester.icns diff --git a/images/autotester.png b/images/autotester.png Binary files differnew file mode 100644 index 0000000..88bbb1f --- /dev/null +++ b/images/autotester.png diff --git a/images/build.png b/images/build.png Binary files differnew file mode 100644 index 0000000..30a2c62 --- /dev/null +++ b/images/build.png diff --git a/images/check.png b/images/check.png Binary files differnew file mode 100644 index 0000000..1cced5b --- /dev/null +++ b/images/check.png diff --git a/images/delete.png b/images/delete.png Binary files differnew file mode 100644 index 0000000..e42dc2c --- /dev/null +++ b/images/delete.png diff --git a/images/dir.png b/images/dir.png Binary files differnew file mode 100644 index 0000000..57cec6b --- /dev/null +++ b/images/dir.png diff --git a/images/edit-clear.png b/images/edit-clear.png Binary files differnew file mode 100644 index 0000000..5542948 --- /dev/null +++ b/images/edit-clear.png diff --git a/images/error.png b/images/error.png Binary files differnew file mode 100644 index 0000000..bb40db9 --- /dev/null +++ b/images/error.png diff --git a/images/filesave.png b/images/filesave.png Binary files differnew file mode 100644 index 0000000..8f8d38c --- /dev/null +++ b/images/filesave.png diff --git a/images/gear.png b/images/gear.png Binary files differnew file mode 100644 index 0000000..de10854 --- /dev/null +++ b/images/gear.png diff --git a/images/minus.png b/images/minus.png Binary files differnew file mode 100644 index 0000000..4466844 --- /dev/null +++ b/images/minus.png diff --git a/images/next.png b/images/next.png Binary files differnew file mode 100644 index 0000000..45b21fb --- /dev/null +++ b/images/next.png diff --git a/images/plus.png b/images/plus.png Binary files differnew file mode 100644 index 0000000..be8c961 --- /dev/null +++ b/images/plus.png diff --git a/images/reload.png b/images/reload.png Binary files differnew file mode 100644 index 0000000..9c336d9 --- /dev/null +++ b/images/reload.png diff --git a/images/start.png b/images/start.png Binary files differnew file mode 100644 index 0000000..4a3788c --- /dev/null +++ b/images/start.png diff --git a/images/stop.png b/images/stop.png Binary files differnew file mode 100644 index 0000000..1063d08 --- /dev/null +++ b/images/stop.png diff --git a/images/test.png b/images/test.png Binary files differnew file mode 100644 index 0000000..61ad743 --- /dev/null +++ b/images/test.png diff --git a/images/warn.png b/images/warn.png Binary files differnew file mode 100644 index 0000000..645cc5f --- /dev/null +++ b/images/warn.png diff --git a/logger.cpp b/logger.cpp new file mode 100644 index 0000000..862e27e --- /dev/null +++ b/logger.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "logger.h" + +Logger *Logger::instance = 0; + +Logger *Logger::getIt() +{ + if (!instance) + instance = new Logger; + return instance; +} + +void Logger::log(const QString& msg) +{ + if (msg.isEmpty()) + return; + + m_log.append(msg); + emit newLog(msg); +} + +void Logger::clear() +{ + m_log.clear(); + emit logCleared(); +} + +QString Logger::allLogs() const +{ + return m_log; +} + diff --git a/logger.h b/logger.h new file mode 100644 index 0000000..fb13508 --- /dev/null +++ b/logger.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LOGGER_H +#define LOGGER_H + +#include "QObject" + +class Logger : public QObject +{ + Q_OBJECT +public: + static Logger *getIt(); + void log(const QString& msg); + void clear(); + QString allLogs() const; + +Q_SIGNALS: + void newLog(const QString &log); + void logCleared(); + +private: + static Logger *instance; + QString m_log; + + Logger() : QObject() {} +}; + +#endif // LOGGER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..738d168 --- /dev/null +++ b/main.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/QApplication> +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication::setOrganizationName("Nokia"); + QApplication::setOrganizationDomain("qt.nokia.com"); + QApplication::setApplicationName("QtAutotester"); + QApplication::setGraphicsSystem("raster"); + + qRegisterMetaType<Test::TestStatus>(); + + QApplication a(argc, argv); + MainWindow w; +#if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5) + w.showMaximized(); +#else + w.show(); +#endif + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..f977859 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,1161 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "testprofile.h" +#include "test.h" +#include "qtdirectory.h" +#include "qtdirectorydialog.h" +#include "testview.h" +#include "testviewlayout.h" +#include "testgraphicsview.h" +#include "clearablelineedit.h" +#include "logger.h" + +#include <QStandardItemModel> +#include <QStandardItem> +#include <QDir> +#include <QFileInfo> +#include <QMessageBox> +#include <QLineEdit> +#include <QSettings> +#include <QGraphicsGridLayout> +#include <QDebug> +#include <QFileDialog> +#include <QTextStream> +#include <QMetaEnum> +#include <QApplication> +#include <QScrollBar> + +QtDirectory *MainWindow::currentRepository = 0; +TestProfile *MainWindow::currentProfile = 0; +TestGraphicsView *MainWindow::view = 0; +QMap<QString, TestView*> MainWindow::testviews; +QStandardItemModel *MainWindow::currentAutoTestModel = 0; + +/************************************************************************************************** + **************************************************************************************************/ +static void clearItemSelection(QStandardItem *item) +{ + item->setData(Qt::Unchecked, Qt::CheckStateRole); + for (int i = 0 ; i < item->rowCount() ; i++) { + clearItemSelection(item->child(i)); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +static void checkDirectorySelection(QStandardItem *item) +{ + bool allChildrenChecked = true; + for (int i = 0 ; i < item->rowCount() ; i++) { + if (item->child(i)->data(Qt::UserRole + 1).toBool()) + checkDirectorySelection(item->child(i)); + if (item->child(i)->data(Qt::CheckStateRole) != Qt::Checked) { + allChildrenChecked = false; + } + } + + if (allChildrenChecked) + item->setData(Qt::Checked, Qt::CheckStateRole); +} + +/************************************************************************************************** + **************************************************************************************************/ +static QStandardItem * findChildItemRecursive(QStandardItem *item, const QString &text) +{ + if (QString::compare(item->text(), text) == 0) + return item; + + for (int i = 0 ; i < item->rowCount() ; i++) { + QStandardItem *found = findChildItemRecursive(item->child(i), text); + if (found) + return found; + } + + return 0; +} + +/************************************************************************************************** + **************************************************************************************************/ +bool CustomProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + QModelIndex index = sourceModel()->index(source_row, 0, source_parent); + return filterAcceptsIndex(index, source_parent); +} + +/************************************************************************************************** + **************************************************************************************************/ +bool CustomProxyModel::filterAcceptsIndex(QModelIndex index, + QModelIndex parent, + bool skipParentCheck, + bool skipChildrenCheck) const +{ + bool parentIsRoot = sourceModel()->data(parent).toString().startsWith("All Tests"); + QModelIndex grandparent = sourceModel()->parent(parent); + + if (sourceModel()->data(index).toString().startsWith("All Tests")) + return true; + + if (sourceModel()->data(index).toString().contains(filterRegExp())) + return true; + + if (!skipChildrenCheck && sourceModel()->hasChildren(index)) { + int count = sourceModel()->rowCount(index); + for (int i = 0; i < count; i++) { + QModelIndex childIndex = sourceModel()->index(i, 0, index); + if (filterAcceptsIndex(childIndex, index, true)) + return true; + } + } + + if (!skipParentCheck && !parentIsRoot && filterAcceptsIndex(parent, grandparent, false, true)) + return true; + + return false; +} + +/************************************************************************************************** + **************************************************************************************************/ +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow), + dialog(new QtDirectoryDialog), + inSetProfile(false), + alreadyRestored(false), + elapsedTimerID(0) +{ + ui->setupUi(this); + setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); + + QWidget* titleWidget = new QWidget(this); + ui->dockWidget->setTitleBarWidget(titleWidget); + + delete ui->filter; + ui->filter = new ClearableLineEdit(ui->dockWidgetContents); + ui->verticalLayout->insertWidget(1, ui->filter); + connect(ui->filter, SIGNAL(textChanged(QString)), this, SLOT(on_filter_textChanged(QString))); + ui->filter->setEnabled(false); +#if QT_VERSION >= 0x040700 + ui->filter->setPlaceholderText("Filter"); +#endif + + ui->addVersion->setIcon(QIcon::fromTheme("list-add", QIcon(":/images/plus.png"))); + ui->remVersion->setIcon(QIcon::fromTheme("list-remove", QIcon(":/images/minus.png"))); + ui->reloadProfile->setIcon(QIcon::fromTheme("view-refresh", QIcon(":/images/reload.png"))); + ui->saveProfile->setIcon(QIcon::fromTheme("document-save", QIcon(":/images/filesave.png"))); + ui->remProfile->setIcon(QIcon::fromTheme("edit-delete", QIcon(":/images/delete.png"))); + ui->runTests->setIcon(QIcon::fromTheme("media-playback-start", QIcon(":/images/start.png"))); + ui->runTestsNoQMake->setIcon(QIcon::fromTheme("media-playback-start", QIcon(":/images/start.png"))); + ui->stopTests->setIcon(QIcon::fromTheme("media-playback-stop", QIcon(":/images/stop.png"))); + ui->skipCurrent->setIcon(QIcon::fromTheme("go-next", QIcon(":/images/next.png"))); + + ui->actionSave_Results->setIcon(QIcon::fromTheme("document-save")); + ui->actionQuit->setIcon(QIcon::fromTheme("application-exit")); + ui->actionRun_Tests->setIcon(QIcon::fromTheme("media-playback-start")); + ui->actionStop_Tests->setIcon(QIcon::fromTheme("media-playback-stop")); + ui->actionSkip_Current_Test->setIcon(QIcon::fromTheme("go-next")); + ui->actionDefault_Zoom->setIcon(QIcon::fromTheme("zoom-original")); + ui->actionZoom_to_Fit->setIcon(QIcon::fromTheme("zoom-fit-best")); + ui->actionZoom_In->setIcon(QIcon::fromTheme("zoom-in")); + ui->actionZoom_Out->setIcon(QIcon::fromTheme("zoom-out")); + ui->actionShow_Explorer->setIcon(QIcon::fromTheme("system-file-manager")); + ui->actionShow_Run_Panel->setIcon(QIcon::fromTheme("media-playback-start")); + ui->actionShow_Output->setIcon(QIcon::fromTheme("utilities-terminal")); + ui->actionSave_Results->setShortcut(QKeySequence::Save); + ui->actionQuit->setShortcut(QKeySequence::Quit); + ui->actionZoom_In->setShortcut(QKeySequence::ZoomIn); + ui->actionZoom_Out->setShortcut(QKeySequence::ZoomOut); + + connect(Logger::getIt(), SIGNAL(newLog(QString)), this, SLOT(onNewLogReceived(QString))); + + view = new TestGraphicsView(ui->viewContainer); + view->setObjectName(QString::fromUtf8("graphicsView")); + view->setFrameShape(QFrame::NoFrame); + ui->viewContainerLayout->addWidget(view); + onZoomLevelChanged(1.0); + connect(view, SIGNAL(zoomLevelChanged(qreal)), this, SLOT(onZoomLevelChanged(qreal))); + + connect(dialog, SIGNAL(accepted()), this, SLOT(onDialogAccepted())); + timerID = startTimer(0); + restoreWindowSettings(); +} + +/************************************************************************************************** + **************************************************************************************************/ +MainWindow::~MainWindow() +{ + delete ui; + delete dialog; + delete currentAutoTestModel; + foreach (QtDirectory *repo, repositories.values()) + delete repo; + foreach (TestProfile *profile, profiles.values()) + delete profile; +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::addProfile(TestProfile *profile) +{ + if (!profile) + return; + + if (profiles.contains(profile->name())) + return; + + profiles.insert(profile->name(), profile); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::removeProfile(const QString &name) +{ + if (!profiles.contains(name)) + return; + + delete profiles[name]; + profiles.remove(name); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::addTest(const QString &name, const QString &path, const QString &proFile) +{ + if (!currentProfile) + return; + + if (currentProfile->testNames().contains(name)) + return; + + Test *test = new Test(name, path, proFile); + currentProfile->addTest(test); + + TestView *testview = new TestView(test); + testviews.insert(name, testview); + view->itemsLayout()->addItem(testview); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::removeTest(const QString &name) +{ + if (!currentProfile || !currentProfile->testNames().contains(name)) + return; + + view->itemsLayout()->removeTestView(testviews.value(name)); + delete testviews[name]; + testviews.remove(name); + currentProfile->removeTest(name); + + QStandardItem *rootItem = currentAutoTestModel->invisibleRootItem()->child(0); + QStandardItem *item = findChildItemRecursive(rootItem, name); + if (item->data(Qt::CheckStateRole) == Qt::Checked) + item->setData(Qt::Unchecked, Qt::CheckStateRole); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::setCurrentQtRepo(const QString &repoName) +{ + if (!repositories.contains(repoName)) + return; + + currentRepository = repositories.value(repoName); + + if (currentAutoTestModel) { + delete currentAutoTestModel; + delete proxyModel; + currentAutoTestModel = 0; + proxyModel = 0; + } + + ui->profiles->clear(); + + ui->qtvariants->clear(); + ui->qtvariants->addItems(currentRepository->variants()); + + QFileInfo f(currentRepository->testSrcDir()); + if (!f.exists()) { + ui->qtversions->setStyleSheet("QComboBox { color:red; }"); + ui->profiles->setEnabled(false); + // clear TestViews + foreach (TestView *tview, testviews.values()) { + view->itemsLayout()->removeTestView(tview); + delete tview; + } + testviews.clear(); + currentProfile = 0; + return; + } else { + ui->qtversions->setStyleSheet(""); + ui->profiles->setEnabled(true); + } + + currentAutoTestModel = currentRepository->createModelFromPro(); + proxyModel = new CustomProxyModel; + proxyModel->setSourceModel(currentAutoTestModel); + proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + ui->treeView->setModel(proxyModel); + QStandardItem *rootItem = currentAutoTestModel->invisibleRootItem()->child(0, 0); + ui->treeView->setExpanded(proxyModel->mapFromSource(currentAutoTestModel->indexFromItem(rootItem)), true); + connect(currentAutoTestModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(onModelItemChanged(QStandardItem*))); + ui->filter->setText(""); + + ui->remVersion->setEnabled(true); + + if (currentProfile) + setCurrentProfile(currentProfile->name()); + else + setCurrentProfile(""); + + QString var = currentProfile->variant(); + if(var.isEmpty()) + currentProfile->setVariant(ui->qtvariants->currentText()); + else { + int idx = ui->qtvariants->findText(var); + if(idx >= 0) + ui->qtvariants->setCurrentIndex(idx); + else { + ui->qtvariants->addItem(var); + ui->qtvariants->setCurrentIndex(ui->qtvariants->count() -1); + } + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::setCurrentProfile(const QString &profileName) +{ + if (!currentRepository || !currentAutoTestModel) + return; + + TestProfile *profile = profiles.value(profileName); + if (!profile && !profileName.isEmpty()) + return; + + inSetProfile = true; + + // clear TestViews + foreach (TestView *tview, testviews.values()) { + view->itemsLayout()->removeTestView(tview); + delete tview; + } + testviews.clear(); + + if (currentProfile && QString::compare(currentProfile->name(), QString("")) == 0) { + ui->profiles->setItemText(0, "<New Profile>"); + delete currentProfile; + currentProfile = 0; + } + + if (profileName.isEmpty()) { + // temporary profile + currentProfile = new TestProfile; + } else { + currentProfile = profile; + } + + QStandardItem *rootItem = currentAutoTestModel->invisibleRootItem()->child(0); + + // clear old selection + clearItemSelection(rootItem); + + // Set new selection according to profile + QStringList testNames = currentProfile->testNames(); + foreach (const QString &testName, testNames) { + QStandardItem *item = findChildItemRecursive(rootItem, testName); + Test *test = currentProfile->test(testName); + if (item) { + test->setAvailable(true); + item->setData(Qt::Checked, Qt::CheckStateRole); + TestView *testview = new TestView(test); + testviews.insert(testName, testview); + view->itemsLayout()->addItem(testview); + } else { + test->setAvailable(false); + } + } + checkDirectorySelection(rootItem); + + connect(currentProfile, SIGNAL(stateChanged(TestProfile::ProfileState)), this, SLOT(onProfileStateChanged(TestProfile::ProfileState))); + connect(currentProfile, SIGNAL(runningStateChanged()), this, SLOT(onProfileRunningStateChanged())); + + inSetProfile = false; +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_filter_textChanged(const QString &text) +{ + if (!proxyModel) + return; + proxyModel->setFilterRegExp(text); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::onModelItemChanged(QStandardItem *item) +{ + if (!currentProfile) + return; + + if (!inSetProfile) { + if (item->data(Qt::UserRole + 1).toBool()) { + // The item is a directory + if (QString::compare(item->text(), QLatin1String("All Tests")) == 0 + && item->data(Qt::CheckStateRole) == Qt::Unchecked + && proxyModel->filterRegExp().isEmpty()) { + foreach (const QString &test, currentProfile->testNames()) { + delete testviews[test]; + currentProfile->removeTest(test); + } + testviews.clear(); + view->itemsLayout()->clearItems(); + } + for (int i = 0 ; i < item->rowCount() ; i++) { + if (proxyModel->filterAcceptsIndex(item->child(i)->index(), item->index(), true)) + item->child(i)->setData(item->data(Qt::CheckStateRole), Qt::CheckStateRole); + } + } else { + // The item is an auto-test + if (item->data(Qt::CheckStateRole) == Qt::Checked) { + addTest(item->text(), item->data(Qt::UserRole).toString(), item->data(Qt::UserRole + 2).toString()); + } else { + removeTest(item->text()); + } + } + } + + ui->runTests->setEnabled(currentProfile->testCount() > 0); + ui->actionRun_Tests->setEnabled(currentProfile->testCount() > 0); + ui->arguments->setReadOnly(currentProfile->testCount() == 0); + ui->runTestsNoQMake->setEnabled(currentProfile->testCount() > 0); + ui->actionRun_Tests_Skip_QMake->setEnabled(currentProfile->testCount() > 0); + ui->actionSave_Results->setEnabled(currentProfile->testCount() > 0); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_addVersion_clicked() +{ + dialog->promptDialog(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_remVersion_clicked() +{ + if (!currentRepository) + return; + + QMessageBox::StandardButton result = QMessageBox::question(this, + "Delete Qt Version", QString("Delete Qt version <i>%1</i>?").arg(currentRepository->name()), + QMessageBox::Yes | QMessageBox::No); + if (result == QMessageBox::Yes) { + QtDirectory *oldDir = currentRepository; + currentRepository = 0; + ui->qtversions->removeItem(ui->qtversions->currentIndex()); + ui->qtversions->setStyleSheet(""); + repositories.remove(oldDir->name()); + delete oldDir; + + if (ui->qtversions->currentIndex() == -1) { + delete currentAutoTestModel; + delete proxyModel; + currentAutoTestModel = 0; + proxyModel = 0; + ui->remVersion->setEnabled(false); + ui->profiles->clear(); + } + + saveRepoSettings(); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::onDialogAccepted() +{ + QString name = dialog->name(); + QString location = dialog->location(); + + if (repositories.contains(name)) + return; + +#ifdef Q_WS_WIN + QtDirectory *dir = new QtDirectory(name, location, dialog->makeLocation()); +#else + QtDirectory *dir = new QtDirectory(name, location); +#endif + repositories.insert(name, dir); + ui->qtversions->addItem(name); + ui->qtversions->setCurrentIndex(ui->qtversions->findText(name)); + + if (ui->profiles->count() == 0) { + ui->profiles->addItem("<New Profile>", ""); + foreach (const TestProfile *profile, profiles.values()) { + ui->profiles->addItem(profile->name(), profile->name()); + } + ui->profiles->setCurrentIndex(profiles.count() > 0 ? 1 : 0); + } + + saveRepoSettings(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_qtversions_currentIndexChanged(const QString &text) +{ + setCurrentQtRepo(text); + + int qtRepoIndex = ui->qtversions->currentIndex(); + ui->profiles->setEnabled(qtRepoIndex != -1); + ui->label_2->setEnabled(qtRepoIndex != -1); + ui->filter->setEnabled(qtRepoIndex != -1); + ui->qtversions->setEnabled(qtRepoIndex != -1); + ui->qtvariants->setEnabled(qtRepoIndex != -1); +} + +void MainWindow::on_qtvariants_currentIndexChanged(const QString &text) +{ + if (currentProfile) + currentProfile->setVariant(text); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_profiles_currentIndexChanged(int index) +{ + if (index == -1) { + ui->reloadProfile->setEnabled(false); + ui->saveProfile->setEnabled(false); + ui->remProfile->setEnabled(false); + ui->runTests->setEnabled(false); + ui->runTestsNoQMake->setEnabled(false); + ui->arguments->setReadOnly(true); + ui->actionRun_Tests->setEnabled(false); + ui->actionRun_Tests_Skip_QMake->setEnabled(false); + ui->actionSave_Results->setEnabled(false); + // clear TestViews + foreach (TestView *tview, testviews.values()) { + view->itemsLayout()->removeTestView(tview); + delete tview; + } + testviews.clear(); + if (currentProfile && QString::compare(currentProfile->name(), QString("")) == 0) { + delete currentProfile; + } + currentProfile = 0; + return; + } + + setCurrentProfile(ui->profiles->itemData(index).toString()); + + if (currentProfile && currentProfile->testCount() < 1) { + ui->runTests->setEnabled(false); + ui->runTestsNoQMake->setEnabled(false); + ui->arguments->setReadOnly(true); + ui->actionRun_Tests->setEnabled(false); + ui->actionRun_Tests_Skip_QMake->setEnabled(false); + ui->actionSave_Results->setEnabled(false); + } + + if (ui->profiles->itemData(index).toString().isEmpty()) { + ui->remProfile->setEnabled(false); + ui->reloadProfile->setEnabled(false); + ui->saveProfile->setEnabled(false); + } else { + ui->remProfile->setEnabled(true); + if (currentProfile->state() == TestProfile::Saved) { + ui->reloadProfile->setEnabled(false); + ui->saveProfile->setEnabled(false); + } else { + ui->reloadProfile->setEnabled(true); + ui->saveProfile->setEnabled(true); + } + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::onProfileStateChanged(TestProfile::ProfileState newState) +{ + QString currentText = ui->profiles->currentText(); + + if (newState == TestProfile::Unsaved) { + if (!currentText.endsWith(QLatin1Char('*'))) + currentText += QLatin1Char('*'); + ui->reloadProfile->setEnabled(true); + ui->saveProfile->setEnabled(true); + } else { + if (currentText.endsWith(QLatin1Char('*'))) + currentText.truncate(currentText.length() - 1); + ui->reloadProfile->setEnabled(false); + ui->saveProfile->setEnabled(false); + } + ui->profiles->setItemText(ui->profiles->currentIndex(), currentText); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::onProfileRunningStateChanged() +{ + if (!currentProfile) + return; + + if (currentProfile->runningState() == TestProfile::Running) { + ui->elapsedTime->setText("00:00:00"); + if (!elapsedTimerID) + elapsedTimerID = startTimer(100); + elapsedTimer.start(); + + ui->runTests->setEnabled(false); + ui->runTestsNoQMake->setEnabled(false); + ui->arguments->setReadOnly(true); + ui->actionRun_Tests->setEnabled(false); + ui->actionRun_Tests_Skip_QMake->setEnabled(false); + ui->actionSave_Results->setEnabled(false); + ui->stopTests->setEnabled(true); + ui->skipCurrent->setEnabled(true); + ui->actionStop_Tests->setEnabled(true); + ui->actionSkip_Current_Test->setEnabled(true); + ui->dockWidget->setEnabled(false); + } else { + if (elapsedTimerID) { + killTimer(elapsedTimerID); + elapsedTimerID = 0; + } + ui->runTests->setEnabled(true); + ui->runTestsNoQMake->setEnabled(true); + ui->arguments->setReadOnly(false); + ui->actionRun_Tests->setEnabled(true); + ui->actionRun_Tests_Skip_QMake->setEnabled(true); + ui->actionSave_Results->setEnabled(true); + ui->stopTests->setEnabled(false); + ui->skipCurrent->setEnabled(false); + ui->actionStop_Tests->setEnabled(false); + ui->actionSkip_Current_Test->setEnabled(false); + ui->dockWidget->setEnabled(true); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_reloadProfile_clicked() +{ + if (!currentProfile) + return; + + if (currentProfile->state() == TestProfile::Unsaved) { + QMessageBox::StandardButton result = QMessageBox::question(this, + "Reload Profile", "The changes made to this profile will be lost.\nDo you want to continue?", + QMessageBox::Yes | QMessageBox::No); + if (result == QMessageBox::Yes) { + currentProfile->restore(); + setCurrentProfile(currentProfile->name()); + saveProfileSettings(); + } + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_remProfile_clicked() +{ + if (!currentProfile) + return; + + if (currentProfile->name().isEmpty()) + return; + + QMessageBox::StandardButton result = QMessageBox::question(this, + "Delete Profile", QString("Delete profile <i>%1</i>?").arg(currentProfile->name()), + QMessageBox::Yes | QMessageBox::No); + if (result == QMessageBox::Yes) { + TestProfile *profile = currentProfile; + ui->profiles->removeItem(ui->profiles->currentIndex()); + removeProfile(profile->name()); + saveProfileSettings(); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_saveProfile_clicked() +{ + if (!currentRepository) + return; + + if (currentProfile->name().isEmpty()) { + QMessageBox prompt; + prompt.setText("Enter profile name."); + prompt.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel); + QLineEdit le(&prompt); + le.resize(190, le.height()); + prompt.layout()->setSpacing(25); + le.setFocus(); + le.move(prompt.sizeHint().width() / 2 - le.width() / 2, prompt.sizeHint().height() / 2 - le.sizeHint().height() / 2 - 10); + int result = prompt.exec(); + + if (result == QMessageBox::Save) { + if (le.text().isEmpty()) { + QMessageBox::warning(this, "Save Profile", "Please enter a profile name."); + return; + } + + if (profiles.contains(le.text())) { + QMessageBox::warning(this, "Save Profile", QString("A profile named <i>%1</i> already exists.").arg(le.text())); + return; + } + + currentProfile->setName(le.text()); + currentProfile->save(); + addProfile(currentProfile); + ui->profiles->addItem(le.text(), le.text()); + ui->profiles->setCurrentIndex(ui->profiles->count() - 1); + } + } else { + currentProfile->save(); + } + + saveProfileSettings(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::saveRepoSettings() +{ + QSettings settings; + settings.remove("qtversions"); + settings.beginWriteArray("qtversions"); + for (int i = 0 ; i < ui->qtversions->count(); i++) { + QString repo = ui->qtversions->itemText(i); + settings.setArrayIndex(i); + settings.setValue("name", repositories.value(repo)->name()); + settings.setValue("location", repositories.value(repo)->qmake()); +#ifdef Q_WS_WIN + settings.setValue("make", repositories.value(repo)->make()); +#endif + } + settings.endArray(); + + saveProfileSettings(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::saveProfileSettings() +{ + QSettings settings; + settings.remove("profiles"); + settings.beginWriteArray("profiles"); + int i = 0; + foreach (const TestProfile *profile, profiles.values()) { + settings.setArrayIndex(i); + settings.setValue("name", profile->name()); + settings.beginWriteArray("tests"); + int j = 0; + foreach (const Test *test, profile->savedTests()) { + settings.setArrayIndex(j); + settings.setValue("name", test->name()); + settings.setValue("path", test->path()); + settings.setValue("pro", test->proFile()); + j++; + } + settings.endArray(); + i++; + } + settings.endArray(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::saveWindowSettings() +{ + QSettings settings; + settings.setValue("size", size()); + settings.setValue("maximized", isMaximized()); + settings.setValue("pos", pos()); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::restoreSettings() +{ + QSettings settings; + QStringList repos; + int size = settings.beginReadArray("qtversions"); + for (int i = 0; i < size; ++i) { + settings.setArrayIndex(i); + QString name = settings.value("name").toString(); + QString location = settings.value("location").toString(); +#ifdef Q_WS_WIN + QString make = settings.value("make").toString(); + QtDirectory *dir = new QtDirectory(name, location, make); +#else + QtDirectory *dir = new QtDirectory(name, location); +#endif + repositories.insert(name, dir); + repos.append(name); + } + settings.endArray(); + + size = settings.beginReadArray("profiles"); + for (int i = 0; i < size; ++i) { + settings.setArrayIndex(i); + QString name = settings.value("name").toString(); + TestProfile *profile = new TestProfile(name); + addProfile(profile); + int nbTests = settings.beginReadArray("tests"); + for (int j = 0; j < nbTests; j++) { + settings.setArrayIndex(j); + QString testname = settings.value("name").toString(); + QString path = settings.value("path").toString(); + QString pro = settings.value("pro").toString(); + Test *test = new Test(testname, path, pro); + profile->addTestToList(test); + } + settings.endArray(); + } + settings.endArray(); + + int i = 0; + foreach (const QString &repo, repos) { + ui->qtversions->addItem(repo); + QFileInfo f(repositories.value(repo)->testSrcDir()); + if (!f.exists()) + ui->qtversions->setItemData(i, QBrush(Qt::red), Qt::ForegroundRole); + i++; + } + + if (!repos.isEmpty()) { + ui->profiles->addItem("<New Profile>", ""); + foreach (const TestProfile *profile, profiles.values()) { + ui->profiles->addItem(profile->name(), profile->name()); + } + ui->profiles->setCurrentIndex(profiles.count() > 0 ? 1 : 0); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::restoreWindowSettings() +{ + QSettings settings; + QSize size = settings.value("size", QSize(880, 600)).toSize(); + bool maximized = settings.value("maximized", false).toBool(); + QPoint pos = settings.value("pos").toPoint(); + + resize(size); + if (!pos.isNull()) + move(pos); + if (maximized) + showMaximized(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_runTests_clicked() +{ + if (!currentProfile) + return; + + Test::setRunQMake(true); + currentProfile->run(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_runTestsNoQMake_clicked() +{ + if (!currentProfile) + return; + + Test::setRunQMake(false); + currentProfile->run(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_stopTests_clicked() +{ + if (!currentProfile) + return; + + if (currentProfile->runningState() == TestProfile::NotRunning) + return; + + currentProfile->cancelRun(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_skipCurrent_clicked() +{ + if (!currentProfile) + return; + + if (currentProfile->runningState() == TestProfile::NotRunning) + return; + + currentProfile->cancelRun(true); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::closeEvent(QCloseEvent *e) +{ + if (currentProfile && currentProfile->runningState() == TestProfile::Running) + currentProfile->cancelRun(); + + bool hasUnsavedProfile = false; + foreach (const TestProfile *profile, profiles.values()) { + if (profile->state() == TestProfile::Unsaved) { + hasUnsavedProfile = true; + break; + } + } + + if (hasUnsavedProfile) { + QMessageBox::StandardButton result = QMessageBox::question(this, + "Save Profiles", + QString("Some profiles are not saved. Do you want to save these profiles before exiting?"), + QMessageBox::SaveAll | QMessageBox::Discard | QMessageBox::Cancel); + if (result == QMessageBox::Cancel) { + e->ignore(); + return; + } else { + if (result == QMessageBox::SaveAll) { + foreach (TestProfile *profile, profiles.values()) { + if (profile->state() == TestProfile::Unsaved) { + profile->save(); + } + } + } + e->accept(); + } + } + + saveWindowSettings(); + saveRepoSettings(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::timerEvent(QTimerEvent *e) +{ + Q_UNUSED(e); + + if (!view->isVisible()) + return; + + if (e->timerId() == timerID) { + if (!alreadyRestored) { + alreadyRestored = true; + killTimer(timerID); + restoreSettings(); + } + } else if (e->timerId() == elapsedTimerID) { + int elapsed = elapsedTimer.elapsed(); + elapsed /= 1000; + + int hours = elapsed / 3600; + elapsed -= hours * 3600; + + int minutes = elapsed / 60; + elapsed -= minutes * 60; + + int seconds = elapsed; + + QString time = QString("%1:%2:%3").arg(hours, 2, 10, QLatin1Char('0')) + .arg(minutes, 2, 10, QLatin1Char('0')) + .arg(seconds, 2, 10, QLatin1Char('0')); + + ui->elapsedTime->setText(time); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionDefault_Zoom_triggered() +{ + view->resetZoom(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionZoom_to_Fit_triggered() +{ + view->zoomFit(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionZoom_In_triggered() +{ + view->zoomIn(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionZoom_Out_triggered() +{ + view->zoomOut(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::onZoomLevelChanged(qreal zoomLevel) +{ + ui->actionDefault_Zoom->setEnabled(zoomLevel > 1.001 || zoomLevel < 0.999); + ui->actionZoom_In->setEnabled(zoomLevel * ZOOM_IN_FACTOR < ZOOM_MAX); + ui->actionZoom_Out->setEnabled(zoomLevel * ZOOM_OUT_FACTOR > ZOOM_MIN); +} + + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionSave_Results_triggered() +{ + const QString resultsFilePath = QFileDialog::getSaveFileName(this, "Choose file to save test results to"); + if (resultsFilePath.isEmpty()) + return; + + const QMetaEnum statusEnum = Test::staticMetaObject.enumerator(Test::staticMetaObject.indexOfEnumerator("TestStatus")); + + QFile resultsFile(resultsFilePath); + resultsFile.open(QIODevice::WriteOnly); + QTextStream resultsStream(&resultsFile); + + QStringList testNames = currentProfile->testNames(); + testNames.sort(); + foreach (const QString &testName, testNames) { + Test *test = currentProfile->test(testName); + resultsStream << test->proFile() << ", " << statusEnum.valueToKey(test->status()) << "\n"; + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionQuit_triggered() +{ + QCoreApplication::quit(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionRun_Tests_triggered() +{ + on_runTests_clicked(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionRun_Tests_Skip_QMake_triggered() +{ + on_runTestsNoQMake_clicked(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionStop_Tests_triggered() +{ + on_stopTests_clicked(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionSkip_Current_Test_triggered() +{ + on_skipCurrent_clicked(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionAbout_Autotester_triggered() +{ + QString message = + "<p><big><b>Qt AutoTester</b></big></p>" + "<p>Based on Qt %1</p>" + "<p> </p>" + "<p>Copyright 2010 Nokia Corporation. All rights reserved.</p>" + "<p>The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY" + " OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.</p>"; + QMessageBox::about(this, "About Qt AutoTester", message.arg(qVersion())); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_arguments_textChanged(const QString &text) +{ + Test::setGlobalArguments(text); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionShow_Explorer_triggered() +{ + ui->dockWidget->setVisible(!ui->dockWidget->isVisible()); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionShow_Run_Panel_triggered() +{ + ui->frame->setVisible(!ui->frame->isVisible()); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::on_actionShow_Output_triggered() +{ + ui->outputDock->setVisible(!ui->outputDock->isVisible()); +} + +/************************************************************************************************** + **************************************************************************************************/ +void MainWindow::onNewLogReceived(const QString &log) +{ + ui->output->append(log.trimmed()); + ui->output->verticalScrollBar()->setValue(ui->output->verticalScrollBar()->maximum()); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..eb0a366 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> +#include <QHash> +#include <QSortFilterProxyModel> +#include <QMap> +#include <QTime> +#include "testprofile.h" + +class QStandardItemModel; +class QStandardItem; +class QtDirectory; +class QtDirectoryDialog; +class TestView; +class TestGraphicsView; + +namespace Ui { + class MainWindow; +} + +class CustomProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + CustomProxyModel() : QSortFilterProxyModel() + {} + ~CustomProxyModel() + {} + bool filterAcceptsIndex(const QModelIndex index, + const QModelIndex parent, + bool skipParentCheck = false, + bool skipChildrenCheck = false) const; + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; +}; + +class MainWindow : public QMainWindow { + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + + static QtDirectory *getCurrentQtVersion() + { return currentRepository; } + static TestProfile *getCurrentProfile() + { return currentProfile; } + static void removeTest(const QString &name); + +protected Q_SLOTS: + void onModelItemChanged(QStandardItem *item); + void on_addVersion_clicked(); + void on_remVersion_clicked(); + void onDialogAccepted(); + void on_qtversions_currentIndexChanged(const QString &text); + void on_qtvariants_currentIndexChanged(const QString &text); + void on_profiles_currentIndexChanged(int index); + void onProfileStateChanged(TestProfile::ProfileState newState); + void onProfileRunningStateChanged(); + void on_reloadProfile_clicked(); + void on_remProfile_clicked(); + void on_saveProfile_clicked(); + void on_runTests_clicked(); + void on_runTestsNoQMake_clicked(); + void on_stopTests_clicked(); + void on_skipCurrent_clicked(); + void on_filter_textChanged(const QString & text); + void onZoomLevelChanged(qreal zoomLevel); + void on_actionSave_Results_triggered(); + void on_actionQuit_triggered(); + void on_actionZoom_to_Fit_triggered(); + void on_actionDefault_Zoom_triggered(); + void on_actionZoom_In_triggered(); + void on_actionZoom_Out_triggered(); + void on_actionRun_Tests_triggered(); + void on_actionRun_Tests_Skip_QMake_triggered(); + void on_actionStop_Tests_triggered(); + void on_actionSkip_Current_Test_triggered(); + void on_actionAbout_Autotester_triggered(); + void on_arguments_textChanged(const QString &text); + void on_actionShow_Explorer_triggered(); + void on_actionShow_Run_Panel_triggered(); + void on_actionShow_Output_triggered(); + void onNewLogReceived(const QString &log); + +protected: + void closeEvent(QCloseEvent *); + void timerEvent(QTimerEvent *); + +private: + void setCurrentQtRepo(const QString &repoName); + void setCurrentProfile(const QString &profileName); + void saveRepoSettings(); + void saveProfileSettings(); + void saveWindowSettings(); + void restoreSettings(); + void restoreWindowSettings(); + void addTest(const QString &name, const QString &path, const QString &proFile); + void addProfile(TestProfile *profile); + void removeProfile(const QString &name); + + Ui::MainWindow *ui; + static TestGraphicsView *view; + QtDirectoryDialog *dialog; + static QMap<QString, TestView*> testviews; + + QHash<QString, QtDirectory*> repositories; + QHash<QString, TestProfile*> profiles; + static QStandardItemModel *currentAutoTestModel; + CustomProxyModel *proxyModel; + static QtDirectory *currentRepository; + static TestProfile *currentProfile; + bool inSetProfile; + bool alreadyRestored; + int timerID; + + int elapsedTimerID; + QTime elapsedTimer; +}; + +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..2cfb997 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,787 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>986</width> + <height>712</height> + </rect> + </property> + <property name="windowTitle"> + <string>Qt AutoTester</string> + </property> + <property name="windowIcon"> + <iconset resource="images.qrc"> + <normaloff>:/images/autotester.png</normaloff>:/images/autotester.png</iconset> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QFrame" name="frame"> + <property name="styleSheet"> + <string notr="true">.QFrame { +background-color: qlineargradient(spread:pad, x1:0.5, y1:0, x2:0.5, y2:1, stop:0 rgba(0, 0, 0, 180), stop:1 rgba(255, 255, 255, 0)); +}</string> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QWidget" name="widget_3" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QToolButton" name="runTests"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Run Tests</string> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/start.png</normaloff>:/images/start.png</iconset> + </property> + <property name="iconSize"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + <property name="autoRaise"> + <bool>false</bool> + </property> + <property name="arrowType"> + <enum>Qt::NoArrow</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="runTestsNoQMake"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Run Tests (Skip QMake)</string> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/start.png</normaloff>:/images/start.png</iconset> + </property> + <property name="iconSize"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + <property name="autoRaise"> + <bool>false</bool> + </property> + <property name="arrowType"> + <enum>Qt::NoArrow</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="stopTests"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Stop Tests</string> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/stop.png</normaloff>:/images/stop.png</iconset> + </property> + <property name="iconSize"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + <property name="autoRaise"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="skipCurrent"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Skip Current Test</string> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/next.png</normaloff>:/images/next.png</iconset> + </property> + <property name="iconSize"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>372</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="elapsedTime"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>00:00:00</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="widget_4" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Auto-tests arguments:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="arguments"> + <property name="maximumSize"> + <size> + <width>230</width> + <height>16777215</height> + </size> + </property> + <property name="toolTip"> + <string>Arguments to pass to auto-tests</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QWidget" name="viewContainer" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>500</width> + <height>300</height> + </size> + </property> + <layout class="QVBoxLayout" name="viewContainerLayout"> + <property name="spacing"> + <number>0</number> + </property> + <property name="margin"> + <number>0</number> + </property> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QDockWidget" name="outputDock"> + <property name="features"> + <set>QDockWidget::NoDockWidgetFeatures</set> + </property> + <property name="allowedAreas"> + <set>Qt::BottomDockWidgetArea</set> + </property> + <property name="windowTitle"> + <string>Output</string> + </property> + <attribute name="dockWidgetArea"> + <number>8</number> + </attribute> + <widget class="QWidget" name="dockWidgetContents_2"> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QTextEdit" name="output"> + <property name="font"> + <font> + <family>FreeMono</family> + <pointsize>9</pointsize> + </font> + </property> + <property name="undoRedoEnabled"> + <bool>false</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <widget class="QDockWidget" name="dockWidget"> + <property name="styleSheet"> + <string notr="true"/> + </property> + <property name="features"> + <set>QDockWidget::NoDockWidgetFeatures</set> + </property> + <property name="allowedAreas"> + <set>Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea</set> + </property> + <property name="windowTitle"> + <string/> + </property> + <attribute name="dockWidgetArea"> + <number>1</number> + </attribute> + <widget class="QWidget" name="dockWidgetContents"> + <property name="styleSheet"> + <string notr="true"/> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QWidget" name="widget" native="true"> + <layout class="QGridLayout" name="gridLayout"> + <property name="horizontalSpacing"> + <number>-1</number> + </property> + <property name="verticalSpacing"> + <number>2</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item row="1" column="0"> + <widget class="QComboBox" name="qtversions"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="minimumSize"> + <size> + <width>130</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item row="1" column="1"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="addVersion"> + <property name="minimumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/plus.png</normaloff>:/images/plus.png</iconset> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QPushButton" name="remVersion"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="minimumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/minus.png</normaloff>:/images/minus.png</iconset> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Qt Version:</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Variant:</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QComboBox" name="qtvariants"> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLineEdit" name="filter"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="frame"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QTreeView" name="treeView"> + <property name="styleSheet"> + <string notr="true"/> + </property> + </widget> + </item> + <item> + <widget class="QWidget" name="widget_2" native="true"> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="horizontalSpacing"> + <number>-1</number> + </property> + <property name="verticalSpacing"> + <number>2</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item row="1" column="0"> + <widget class="QComboBox" name="profiles"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="minimumSize"> + <size> + <width>130</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_2"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Profile:</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="reloadProfile"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="minimumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="toolTip"> + <string>Reload Profile</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/reload.png</normaloff>:/images/reload.png</iconset> + </property> + </widget> + </item> + <item row="1" column="3"> + <widget class="QPushButton" name="saveProfile"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="minimumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="toolTip"> + <string>Save Profile</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/filesave.png</normaloff>:/images/filesave.png</iconset> + </property> + </widget> + </item> + <item row="1" column="4"> + <widget class="QPushButton" name="remProfile"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="minimumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>25</width> + <height>25</height> + </size> + </property> + <property name="toolTip"> + <string>Delete Profile</string> + </property> + <property name="text"> + <string/> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/delete.png</normaloff>:/images/delete.png</iconset> + </property> + </widget> + </item> + <item row="1" column="1"> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </widget> + <widget class="QMenuBar" name="menuBar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>986</width> + <height>21</height> + </rect> + </property> + <widget class="QMenu" name="menuTests"> + <property name="title"> + <string>&Tests</string> + </property> + <addaction name="actionRun_Tests"/> + <addaction name="actionRun_Tests_Skip_QMake"/> + <addaction name="actionStop_Tests"/> + <addaction name="separator"/> + <addaction name="actionSkip_Current_Test"/> + </widget> + <widget class="QMenu" name="menuFile"> + <property name="title"> + <string>&File</string> + </property> + <addaction name="actionSave_Results"/> + <addaction name="separator"/> + <addaction name="actionQuit"/> + </widget> + <widget class="QMenu" name="menuView"> + <property name="title"> + <string>&View</string> + </property> + <addaction name="actionShow_Explorer"/> + <addaction name="actionShow_Run_Panel"/> + <addaction name="actionShow_Output"/> + <addaction name="separator"/> + <addaction name="actionDefault_Zoom"/> + <addaction name="actionZoom_to_Fit"/> + <addaction name="actionZoom_In"/> + <addaction name="actionZoom_Out"/> + </widget> + <widget class="QMenu" name="menuHelp"> + <property name="title"> + <string>&Help</string> + </property> + <addaction name="actionAbout_Autotester"/> + </widget> + <addaction name="menuFile"/> + <addaction name="menuTests"/> + <addaction name="menuView"/> + <addaction name="menuHelp"/> + </widget> + <action name="actionSave_Results"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Save Results...</string> + </property> + </action> + <action name="actionQuit"> + <property name="text"> + <string>&Quit</string> + </property> + </action> + <action name="actionDefault_Zoom"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Default Zoom</string> + </property> + </action> + <action name="actionZoom_to_Fit"> + <property name="text"> + <string>Zoom to &Fit</string> + </property> + </action> + <action name="actionZoom_In"> + <property name="text"> + <string>Zoom &In</string> + </property> + </action> + <action name="actionZoom_Out"> + <property name="text"> + <string>Zoom &Out</string> + </property> + <property name="toolTip"> + <string>Zoom Out</string> + </property> + </action> + <action name="actionRun_Tests"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/start.png</normaloff>:/images/start.png</iconset> + </property> + <property name="text"> + <string>&Run Tests</string> + </property> + <property name="shortcut"> + <string>F5</string> + </property> + </action> + <action name="actionRun_Tests_Skip_QMake"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Run Test (Skip QMake)</string> + </property> + <property name="shortcut"> + <string>F6</string> + </property> + </action> + <action name="actionStop_Tests"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/stop.png</normaloff>:/images/stop.png</iconset> + </property> + <property name="text"> + <string>&Stop Tests</string> + </property> + <property name="shortcut"> + <string>Shift+F5</string> + </property> + </action> + <action name="actionSkip_Current_Test"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/next.png</normaloff>:/images/next.png</iconset> + </property> + <property name="text"> + <string>S&kip Current Test</string> + </property> + <property name="shortcut"> + <string>F7</string> + </property> + </action> + <action name="actionAbout_Autotester"> + <property name="icon"> + <iconset resource="images.qrc"> + <normaloff>:/images/autotester.png</normaloff>:/images/autotester.png</iconset> + </property> + <property name="text"> + <string>&About Qt AutoTester...</string> + </property> + <property name="toolTip"> + <string>About Qt AutoTester</string> + </property> + </action> + <action name="actionShow_Explorer"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="text"> + <string>Show Explorer</string> + </property> + <property name="shortcut"> + <string>F1</string> + </property> + </action> + <action name="actionShow_Run_Panel"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="text"> + <string>Show Run Panel</string> + </property> + <property name="shortcut"> + <string>F2</string> + </property> + </action> + <action name="actionShow_Output"> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="text"> + <string>Show Output</string> + </property> + <property name="shortcut"> + <string>F3</string> + </property> + </action> + </widget> + <resources> + <include location="images.qrc"/> + </resources> + <connections/> +</ui> diff --git a/qt-autotester.pro b/qt-autotester.pro new file mode 100644 index 0000000..2d71efc --- /dev/null +++ b/qt-autotester.pro @@ -0,0 +1,46 @@ +QT += core gui xml + +TARGET = autotester +TEMPLATE = app + +SOURCES += main.cpp \ + mainwindow.cpp \ + test.cpp \ + testprofile.cpp \ + qtdirectory.cpp \ + qtdirectorydialog.cpp \ + testview.cpp \ + testviewlayout.cpp \ + testgraphicsview.cpp \ + testresultwidget.cpp \ + clearablelineedit.cpp \ + logger.cpp + +HEADERS += \ + mainwindow.h \ + test.h \ + testprofile.h \ + qtdirectory.h \ + qtdirectorydialog.h \ + testview.h \ + testviewlayout.h \ + testgraphicsview.h \ + testresultwidget.h \ + clearablelineedit.h \ + logger.h + +FORMS += \ + mainwindow.ui \ + qtdirectorydialog.ui \ + testresultwidget.ui + +RESOURCES += \ + images.qrc + +MOC_DIR = ./.moc +OBJECTS_DIR = ./.obj +UI_DIR = ./.ui + +macx { + ICON = images/autotester.icns +} diff --git a/qtdirectory.cpp b/qtdirectory.cpp new file mode 100644 index 0000000..6e8ef16 --- /dev/null +++ b/qtdirectory.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtdirectory.h" + +#include <QProcess> +#include <QFile> +#include <QDir> +#include <QStandardItemModel> +#include <QDebug> + +/************************************************************************************************** + **************************************************************************************************/ +QtDirectory::QtDirectory(const QString &name, const QString &location +#ifdef Q_WS_WIN + , const QString &make +#endif + ) + : QObject(), + repoName(name), + repoQmake(location) +{ + repoBuildDir = location; + QString toRemove = "/bin/qmake"; +#ifdef Q_WS_WIN + toRemove += QString(".exe"); +#endif + repoBuildDir.replace(toRemove, QString("")); + repoTestBuildDir = repoBuildDir + QString("/tests/auto"); + repoBinDir = repoBuildDir + QString("/bin"); + + bool symbianBuildSystem = false; + repoVariants.clear(); + + QFile cache(repoBuildDir + "/.qmake.cache"); + if (cache.open(QIODevice::ReadOnly | QIODevice::Text)) { + while (!cache.atEnd()) { + QString line = cache.readLine(); + if (line.contains("QT_SOURCE_TREE")) { + line.replace("\\", "/"); + repoSrcDir = line; + repoSrcDir.remove(0, repoSrcDir.indexOf("=") + 1); + repoSrcDir = repoSrcDir.trimmed(); + repoSrcDir.replace("$$quote(", ""); + repoSrcDir.replace(")", ""); + repoTestSrcDir = repoSrcDir + QString("/tests/auto"); + break; + } + else if (line.startsWith("QMAKESPEC")) { + if (line.contains("symbian")) { + repoRemoteOS = "symbian"; + } + else if (line.contains("wince")) { + repoRemoteOS = "wince"; + } + if (line.contains("symbian-abld") || line.contains("symbian-sbsv2")) { + symbianBuildSystem = true; + repoVariants.clear(); + repoVariants << "release-armv6"; + repoVariants << "release-armv5"; + repoVariants << "release-gcce"; + repoVariants << "release-winscw"; + repoVariants << "debug-armv6"; + repoVariants << "debug-armv5"; + repoVariants << "debug-gcce"; + repoVariants << "debug-winscw"; + } + } + else if (line.startsWith("CONFIG")) { + if (!symbianBuildSystem) { + if(line.contains("release")) + repoVariants << "release"; + if(line.contains("debug")) + repoVariants << "debug"; + } + repoCrossCompiled = line.contains("cross_compile"); + } + } + } + + QStringList arguments; + arguments << "--version"; + QProcess process; + process.start(location, arguments); + if (!process.waitForStarted(2000)) { + return; + } + process.waitForFinished(1000); + QString read = process.readAll(); + repoLibDir = read.split(" in ").at(1).trimmed(); + +#ifdef Q_WS_WIN + repoMakeLocation = make; + repoMakeDir = make; + repoMakeDir.truncate(repoMakeDir.lastIndexOf("/")); +#endif +} + +/************************************************************************************************** + **************************************************************************************************/ +QtDirectory::~QtDirectory() +{ +} + +/************************************************************************************************** + **************************************************************************************************/ +QStandardItemModel *QtDirectory::createModelFromPro() const +{ + QStandardItemModel *model = new QStandardItemModel; + model->setObjectName(repoBuildDir); + model->setHorizontalHeaderItem(0, new QStandardItem("Auto-tests")); + + QStandardItem *invisibleItem = model->invisibleRootItem(); + QStandardItem *rootItem = new QStandardItem("All Tests"); + rootItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + rootItem->setData(Qt::Unchecked, Qt::CheckStateRole); + rootItem->setIcon(QIcon(":/images/dir.png")); + rootItem->setData(true, Qt::UserRole + 1); + invisibleItem->appendRow(rootItem); + createChildrenItemsFromPro(".", "auto.pro", rootItem); + + return model; +} + +/************************************************************************************************** + **************************************************************************************************/ +void QtDirectory::createChildrenItemsFromPro(const QString &dir, const QString &pro, QStandardItem *parent) const +{ + if (dir.isEmpty() || pro.isEmpty() || !parent) + return; + + QFile f(repoTestSrcDir + "/" + dir + "/" + pro); + if (!f.exists()) + return; + + QStandardItem *item; + if (pro == "auto.pro") { + item = parent; + } else { + QString name = pro; + name.replace(".pro", ""); + item = new QStandardItem(name); + item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + item->setData(Qt::Unchecked, Qt::CheckStateRole); + item->setData("/" + dir, Qt::UserRole); + QString proFilePath = QFileInfo(f).absoluteFilePath(); + proFilePath.replace(repoTestSrcDir, ""); + item->setData(proFilePath, Qt::UserRole + 2); + parent->appendRow(item); + } + + QStringList subdirs = getSubdirsFromPro(f); + QString tstName = pro; + tstName.replace(".pro", ".cpp"); + QFileInfo srcFile(repoTestSrcDir + "/" + dir + "/tst_" + tstName); + QFileInfo srcFile2(repoTestSrcDir + "/" + dir + "/test/tst_" + tstName); + if (subdirs.isEmpty() || srcFile.exists() || srcFile2.exists()) { + // it is an autotest + item->setIcon(QIcon(":/images/test.png")); + item->setData(false, Qt::UserRole + 1); + + if (srcFile2.exists()) + item->setData("/" + dir + "/test", Qt::UserRole); + + } else { + // it is a directory + item->setIcon(QIcon::fromTheme("folder", QIcon(":/images/dir.png"))); + item->setData(true, Qt::UserRole + 1); + foreach (const QString &subdir, subdirs) + if (subdir.endsWith(".pro")) + createChildrenItemsFromPro(dir, subdir, item); + else + createChildrenItemsFromPro(dir + "/" + subdir, subdir + ".pro", item); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +QStringList QtDirectory::getSubdirsFromPro(QFile &pro) const +{ + QStringList list; + + if (pro.open(QIODevice::ReadOnly | QIODevice::Text)) { + bool inSubdirs = false; + while (!pro.atEnd()) { + QString line = pro.readLine(); + line = line.trimmed(); + if (!inSubdirs) { + int comPos; + if ((comPos = line.indexOf("#")) != -1) + line.truncate(comPos); + if (line.contains("SUBDIRS +=") || line.contains("Q3SUBDIRS +=") + || line.contains("SUBDIRS =") || line.contains("Q3SUBDIRS =") + || line.contains("SUBDIRS+=") || line.contains("Q3SUBDIRS+=") + || line.contains("SUBDIRS=") || line.contains("Q3SUBDIRS=")) { + inSubdirs = true; + line.remove(0, line.indexOf("=") + 1); + if (!line.contains("\\")) + inSubdirs = false; + line.replace("\\", ""); + line = line.trimmed(); + QStringList sublist = line.split(" ", QString::SkipEmptyParts); + for (int i = 0; i < sublist.count(); i++) { + if (sublist.at(i).startsWith("$$")) { + sublist.removeAt(i); + i--; + } + } + list.append(sublist); + } + } else { + if (line.isEmpty()) { + inSubdirs = false; + } else { + if (!line.contains("\\")) + inSubdirs = false; + line.replace("\\", ""); + int comPos; + if ((comPos = line.indexOf("#")) != -1) + line.truncate(comPos); + QStringList sublist = line.split(" ", QString::SkipEmptyParts); + for (int i = 0; i < sublist.count(); i++) { + if (sublist.at(i).startsWith("$$")) { + sublist.removeAt(i); + i--; + } + } + list.append(sublist); + } + } + } + } + + list.removeDuplicates(); + return list; +} + diff --git a/qtdirectory.h b/qtdirectory.h new file mode 100644 index 0000000..5102a4a --- /dev/null +++ b/qtdirectory.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTREPOSITORY_H +#define TESTREPOSITORY_H + +#include <QtCore/QObject> +#include <QtCore/QHash> +#include <QtCore/QStringList> + +class QStandardItemModel; +class QStandardItem; +class QFile; + +class QtDirectory : public QObject +{ + Q_OBJECT + +public: + QtDirectory(const QString &name, const QString& location +#ifdef Q_WS_WIN + , const QString &make +#endif + ); + ~QtDirectory(); + + inline QString name() const + { return repoName; } + inline QString buildDir() const + { return repoBuildDir; } + inline QString srcDir() const + { return repoSrcDir; } + inline QString libDir() const + { return repoLibDir; } + inline QString binDir() const + { return repoBinDir; } + inline QString testBuildDir() const + { return repoTestBuildDir; } + inline QString testSrcDir() const + { return repoTestSrcDir; } + inline QString qmake() const + { return repoQmake; } +#ifdef Q_WS_WIN + inline QString make() const + { return repoMakeLocation; } + inline QString makeDir() const + { return repoMakeDir; } +#endif + inline QStringList variants() const + { return repoVariants; } + + QStandardItemModel *createModelFromPro() const; + + inline QString remoteOS() const + { return repoRemoteOS; } + inline bool isCrossCompiled() const + { return repoCrossCompiled; } + +private: + QString repoName; + QString repoQmake; + QString repoBuildDir; + QString repoSrcDir; + QString repoLibDir; + QString repoBinDir; + QString repoTestBuildDir; + QString repoTestSrcDir; + bool repoCrossCompiled; + QString repoRemoteOS; + +#ifdef Q_WS_WIN + QString repoMakeLocation; + QString repoMakeDir; +#endif + + QStringList repoVariants; + + void createChildrenItemsFromPro(const QString &dir, const QString &pro, QStandardItem *parent) const; + QStringList getSubdirsFromPro(QFile &pro) const; +}; + +#endif // TESTREPOSITORY_H diff --git a/qtdirectorydialog.cpp b/qtdirectorydialog.cpp new file mode 100644 index 0000000..92a9a62 --- /dev/null +++ b/qtdirectorydialog.cpp @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtdirectorydialog.h" +#include "ui_qtdirectorydialog.h" + +#include <QFileDialog> +#include <QProcess> +#include <QDebug> + +/************************************************************************************************** + **************************************************************************************************/ +QtDirectoryDialog::QtDirectoryDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::QtDirectoryDialog), + qmakeOk(false) +{ + ui->setupUi(this); + +#ifdef Q_WS_WIN + uimakelabel = new QLabel; + uimakelabel->setObjectName(QString::fromUtf8("uimakelabel")); + uimake = new QLineEdit; + uimake->setObjectName(QString::fromUtf8("uimake")); + uibrowsemake = new QPushButton("Browse..."); + uibrowsemake->setObjectName(QString::fromUtf8("uibrowsemake")); + + connect(uimake, SIGNAL(textChanged(QString)), this, SLOT(onuimake_textChanged(QString))); + connect(uibrowsemake, SIGNAL(clicked()), this, SLOT(onuibrowsemake_clicked())); +#endif +} + +/************************************************************************************************** + **************************************************************************************************/ +QtDirectoryDialog::~QtDirectoryDialog() +{ + delete ui; +#ifdef Q_WS_WIN + delete uimakelabel; + delete uimake; + delete uibrowsemake; +#endif +} + +/************************************************************************************************** + **************************************************************************************************/ +QString QtDirectoryDialog::name() const +{ + return ui->name->text(); +} + +/************************************************************************************************** + **************************************************************************************************/ +QString QtDirectoryDialog::location() const +{ + return ui->qmake->text(); +} + +/************************************************************************************************** + **************************************************************************************************/ +#ifdef Q_WS_WIN +QString QtDirectoryDialog::makeLocation() const +{ + return uimake->text(); +} +#endif + +/************************************************************************************************** + **************************************************************************************************/ +void QtDirectoryDialog::promptDialog() +{ + ui->name->setText(QString()); + ui->qmake->setText(QString()); + ui->message->setText(QString()); + ui->name->setFocus(); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + + show(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void QtDirectoryDialog::on_browse_clicked() +{ + QString qmakeLoc = QFileDialog::getOpenFileName(this, "Select qmake location"); + if (!qmakeLoc.isEmpty()) + ui->qmake->setText(qmakeLoc); +} + +/************************************************************************************************** + **************************************************************************************************/ +#ifdef Q_WS_WIN +void QtDirectoryDialog::onuibrowsemake_clicked() +{ + QString makeLoc = QFileDialog::getOpenFileName(this, "Select " + makename + " location"); + if (!makeLoc.isEmpty()) + uimake->setText(makeLoc); +} +#endif + +/************************************************************************************************** + **************************************************************************************************/ +void QtDirectoryDialog::on_name_textChanged(const QString &text) +{ + Q_UNUSED(text) + checkFields(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void QtDirectoryDialog::on_qmake_textChanged(const QString &text) +{ + Q_UNUSED(text) + checkFields(); +} + +/************************************************************************************************** + **************************************************************************************************/ +#ifdef Q_WS_WIN +void QtDirectoryDialog::onuimake_textChanged(const QString &text) +{ + Q_UNUSED(text) + checkFields(); +} +#endif + +/************************************************************************************************** + **************************************************************************************************/ +void QtDirectoryDialog::checkFields() +{ + QString name = ui->name->text(); + QString qmake = ui->qmake->text(); + + ui->message->setStyleSheet("color:red"); + if (name.isEmpty()) { + ui->message->setText("Please enter a Qt version name."); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + return; + } + + if (qmake.isEmpty()) { + ui->message->setText("Please select a qmake location"); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + qmakeOk = false; +#ifdef Q_WS_WIN + updateMakeFields(); +#endif + return; + } + + QFileInfo file(qmake); + if (!file.exists() || !file.fileName().startsWith("qmake", Qt::CaseInsensitive)) { + ui->message->setText("Unable to find qmake at this location"); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + qmakeOk = false; +#ifdef Q_WS_WIN + updateMakeFields(); +#endif + return; + } + + QStringList arguments; + arguments << "--version"; + QProcess process; + process.start(qmake, arguments); + if (!process.waitForStarted(2000)) { + ui->message->setText("Incorrect qmake executable"); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + qmakeOk = false; +#ifdef Q_WS_WIN + updateMakeFields(); +#endif + return; + } + process.waitForFinished(1000); + QString read = process.readAll(); + if (!read.startsWith("qmake version", Qt::CaseInsensitive)) { + ui->message->setText("Incorrect qmake executable"); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + qmakeOk = false; +#ifdef Q_WS_WIN + updateMakeFields(); +#endif + return; + } + + QString testsDir = QDir::fromNativeSeparators(qmake); + QString toRemove = "/bin/qmake"; +#ifdef Q_WS_WIN + toRemove += QString(".exe"); +#endif + testsDir.replace(toRemove, QString("/tests")); + QFileInfo dir(testsDir); + if (!dir.exists() || !dir.isDir()) { + ui->message->setText("Unable to find tests directory in this Qt version"); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + qmakeOk = false; +#ifdef Q_WS_WIN + updateMakeFields(); +#endif + return; + } + +#ifdef Q_WS_WIN + makespec = ""; + QString qtdir = qmake; + qtdir.replace("/bin/qmake.exe", ""); + QFile cache(qtdir + "/.qmake.cache"); + if (cache.open(QIODevice::ReadOnly | QIODevice::Text)) { + while (!cache.atEnd()) { + QString line = cache.readLine(); + if (line.contains("QMAKESPEC")) { + if (line.contains("msvc")) { + makespec = "msvc"; + makename = "nmake"; + } else if (line.contains("g++")) { + makespec = "g++"; + makename = "mingw32-make"; + } else if (line.contains("symbian")) { + makespec = "symbian"; + makename = "mingw32-make"; + } + break; + } + } + } + if (makespec.isEmpty()) { + ui->message->setText("Unsupported compiler used with this Qt version"); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + qmakeOk = false; + updateMakeFields(); + return; + } +#endif + + qmakeOk = true; + +#ifdef Q_WS_WIN + updateMakeFields(); + + if (uimake->text().isEmpty()) { + ui->message->setText("Please select " + makename + " location"); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + return; + } + + QFileInfo filemake(uimake->text()); + if (!filemake.exists() || !filemake.fileName().startsWith(makename, Qt::CaseInsensitive)) { + ui->message->setText("Unable to find " + makename + " at this location"); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + return; + } +#endif + + QString version = read.mid(read.indexOf("Using ") + 6, 16); + ui->message->setStyleSheet("color:green"); + ui->message->setText(QString("Found %1").arg(version)); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); +} + +/************************************************************************************************** + **************************************************************************************************/ +#ifdef Q_WS_WIN +void QtDirectoryDialog::updateMakeFields() +{ + if (qmakeOk) { + if (!ui->gridLayout->itemAtPosition(2, 0)) { + ui->gridLayout->addWidget(uimakelabel, 2, 0, 1, 1); + ui->gridLayout->addWidget(uimake, 2, 1, 1, 1); + ui->gridLayout->addWidget(uibrowsemake, 2, 2, 1, 1); + } + if (makespec == "msvc") + uimakelabel->setText("nmake Location"); + else + uimakelabel->setText("MinGW make Location"); + } else { + if (ui->gridLayout->itemAtPosition(2, 0)) { + ui->gridLayout->removeWidget(uimakelabel); + ui->gridLayout->removeWidget(uimake); + ui->gridLayout->removeWidget(uibrowsemake); + uimakelabel->setParent(0); + uimake->setParent(0); + uibrowsemake->setParent(0); + } + } +} +#endif diff --git a/qtdirectorydialog.h b/qtdirectorydialog.h new file mode 100644 index 0000000..f849646 --- /dev/null +++ b/qtdirectorydialog.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTDIRECTORYDIALOG_H +#define QTDIRECTORYDIALOG_H + +#include <QDialog> + +namespace Ui { + class QtDirectoryDialog; +} + +class QLineEdit; +class QPushButton; +class QLabel; + +class QtDirectoryDialog : public QDialog +{ + Q_OBJECT + +public: + explicit QtDirectoryDialog(QWidget *parent = 0); + ~QtDirectoryDialog(); + + void promptDialog(); + QString name() const; + QString location() const; +#ifdef Q_WS_WIN + QString makeLocation() const; +#endif + +protected Q_SLOTS: + void on_browse_clicked(); + void on_name_textChanged(const QString &text); + void on_qmake_textChanged(const QString &text); +#ifdef Q_WS_WIN + void onuimake_textChanged(const QString &text); + void onuibrowsemake_clicked(); +#endif + +private: + Ui::QtDirectoryDialog *ui; + bool nameOk; + bool qmakeOk; +#ifdef Q_WS_WIN + bool makeOk; + QString makespec; + QString makename; + QLineEdit *uimake; + QPushButton *uibrowsemake; + QLabel *uimakelabel; + + void updateMakeFields(); +#endif + + void checkFields(); +}; + +#endif // QTDIRECTORYDIALOG_H diff --git a/qtdirectorydialog.ui b/qtdirectorydialog.ui new file mode 100644 index 0000000..dc8a322 --- /dev/null +++ b/qtdirectorydialog.ui @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtDirectoryDialog</class> + <widget class="QDialog" name="QtDirectoryDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>428</width> + <height>141</height> + </rect> + </property> + <property name="windowTitle"> + <string>New Qt Version</string> + </property> + <property name="modal"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QGridLayout" name="gridLayout"> + <property name="horizontalSpacing"> + <number>5</number> + </property> + <property name="verticalSpacing"> + <number>8</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Version Name:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="name"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>qmake Location:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="qmake"/> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="browse"> + <property name="text"> + <string>Browse...</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="message"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>25</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>25</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">color: rgb(212, 0, 0)</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + <property name="centerButtons"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>QtDirectoryDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>QtDirectoryDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..5574d71 --- /dev/null +++ b/test.cpp @@ -0,0 +1,532 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include "test.h" +#include "qtdirectory.h" +#include "logger.h" +#include <QtCore> +#include <QtXml> + +bool Test::runQMake = true; +QString Test::m_globalArguments; + +/************************************************************************************************** + **************************************************************************************************/ +Test::Test(const QString &name, const QString &path, const QString &pro) + : QObject(), + testName(name), + testPath(path), + teststatus(Initial), + p(0), + testPro(pro), + testIsAvailable(true) +{ + testExecutable = path +#ifdef Q_WS_WIN + + QString("/debug") +#endif + + QString("/tst_") + name +#ifdef Q_WS_WIN + + QString(".exe") +#endif + ; +} + +/************************************************************************************************** + **************************************************************************************************/ +Test::~Test() +{ + if (p) + delete p; +} + +/************************************************************************************************** + **************************************************************************************************/ +void Test::run() +{ + Logger::getIt()->log(QString("<b>Running auto-test %1</b>").arg(testName)); + + //clear previous results + r.pass = false; + + setStatus(Initial); + int status = buildTest(); + if (status != 0 || teststatus == Skipped) { + emit statusChanged(teststatus); + return; + } + + runTest(); + + Logger::getIt()->log(" "); + // make sure the test view is in a coherent state + emit statusChanged(teststatus); +} + +/************************************************************************************************** + **************************************************************************************************/ +void Test::cancelRun() +{ + if (p && p->state() != QProcess::NotRunning) + p->kill(); + + setStatus(Skipped); +} + +/************************************************************************************************** + **************************************************************************************************/ +int Test::buildTest() +{ + setStatus(Building); + + QString absoluteTestPath = MainWindow::getCurrentQtVersion()->testBuildDir() + testPath; + + QDir testDir(absoluteTestPath); + if (!testDir.exists()) + testDir.mkpath(absoluteTestPath); + int ret = 0; + if (runQMake) + ret = runExecutable(absoluteTestPath, + MainWindow::getCurrentQtVersion()->qmake(), + MainWindow::getCurrentQtVersion()->binDir(), + QStringList() << "-r" << MainWindow::getCurrentQtVersion()->testSrcDir() + testPro); + +#ifdef Q_WS_WIN + if (ret == 0) { + ret = runExecutable(absoluteTestPath, + MainWindow::getCurrentQtVersion()->make(), + MainWindow::getCurrentQtVersion()->makeDir(), + QStringList() << MainWindow::getCurrentProfile()->variant()); + } +#else + ret = runExecutable(absoluteTestPath, "/usr/bin/make", "", QStringList()); +#endif + if (ret != 0 && teststatus != Skipped) { + setStatus(BuildError); + } + +#ifdef Q_WS_MAC + QFileInfo f(MainWindow::getCurrentQtVersion()->testBuildDir() + testExecutable); + if (!f.exists()) + testExecutable += QString(".app"); +#endif + + return ret; +} + +/************************************************************************************************** + **************************************************************************************************/ +int Test::runTest() +{ + setStatus(Running); + + int code = 0; + if (!MainWindow::getCurrentQtVersion()->isCrossCompiled()) { + //A desktop platform, run the test directly and capture it's stdout + QString exe; +#ifdef Q_WS_WIN + //prepend debug or release if needed + if (!MainWindow::getCurrentProfile()->variant().isEmpty()) + exe = MainWindow::getCurrentProfile()->variant() + QLatin1Char('/') + testExecutable; + else +#endif + exe = testExecutable; + //append OS specific suffix +#ifdef Q_WS_MAC + QFileInfo f(testExecutable); + if (!f.exists()) + exe += QString(".app"); +#endif +#ifdef Q_WS_WIN + exe += QLatin1String(".exe"); +#endif + + code = runExecutable(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath, + MainWindow::getCurrentQtVersion()->testBuildDir() + testExecutable, + MainWindow::getCurrentQtVersion()->libDir(), + QStringList() << "-xml" << "-flush" << m_globalArguments.split(" ", QString::SkipEmptyParts), true); + } + else if (MainWindow::getCurrentQtVersion()->remoteOS() == "symbian") { + QFile results; + if (!MainWindow::getCurrentProfile()->variant().contains("winscw")) { + //make sis package for phone testing +#ifdef Q_WS_WIN + code = runExecutable(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath, + MainWindow::getCurrentQtVersion()->make(), + MainWindow::getCurrentQtVersion()->makeDir() + ";" + MainWindow::getCurrentQtVersion()->binDir(), + QStringList() << "sis"); +#else + code = runExecutable(testPath, "/usr/bin/make", "", QStringList() << "sis"); +#endif + //remote test using runonphone + if(code == 0) { + code = runExecutable(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath, "runonphone", + MainWindow::getCurrentQtVersion()->libDir(), + QStringList() << "--sis" + << QDir::cleanPath(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath) + QString("/tst_" + testName + ".sis") + << "--download" + << "c:\\data\\testresults.xml" + << QDir::cleanPath(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath) + QString("/testresults.xml") + << QString("tst_") + testName + ".exe" + << "-flush" + << "-o" + << "c:\\data\\testresults.xml" + << "-xml"); + } + results.setFileName(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath + "/testresults.xml"); + } else { + //run emulator executable + QDir epocroot(qgetenv("EPOCROOT")); + QDir path = epocroot; + path.cd(QLatin1String("epoc32/release/winscw/")); + if(MainWindow::getCurrentProfile()->variant().contains("debug")) + path.cd(QLatin1String("udeb/")); + else + path.cd(QLatin1String("urel/")); + code = runExecutable(MainWindow::getCurrentQtVersion()->testBuildDir() + testPath, + path.absoluteFilePath(QString("tst_" + testName + ".exe")), + MainWindow::getCurrentQtVersion()->libDir(), + QStringList() << "--" + << "-flush" + << "-xml" + << "-o" + << "c:\\data\\testresults.xml"); + results.setFileName(epocroot.absoluteFilePath(QLatin1String("epoc32/winscw/c/data/testresults.xml"))); + } + + //read xml results file + if(results.open(QIODevice::ReadOnly)) { + QString tmp = results.readAll(); + output += tmp; + results.close(); + results.remove(); + Logger::getIt()->log(makePrintable(tmp)); + } else { + setStatus(RunError); + return -1; + } + + } //else if wince, do something with cetest + + if (code < 0) { + if (teststatus == Skipped) { + Logger::getIt()->log(QString("<b>%1 has been skipped</b>").arg(testName)); + } else { + setStatus(RunError); + Logger::getIt()->log(QString("<b>An error has occured while running %1</b>").arg(testName)); + } + return -1; + } + + processResult(output); + if (r.pass) + setStatus(TestPass); + else + setStatus(TestFail); + + return 0; +} + +/************************************************************************************************** + **************************************************************************************************/ +int Test::runExecutable(const QString &workdir, const QString &executable, const QString &env, const QStringList &arguments, bool ignoreErrorChannel) +{ + Logger::getIt()->log(QString("<font color=blue>Starting \"%1\" %2</font>").arg(executable).arg(arguments.join(" "))); + delete p; + p = new QProcess; + connect(p, SIGNAL(readyRead()), this, SLOT(onProcessReadyRead())); + if (!ignoreErrorChannel) + p->setProcessChannelMode(QProcess::MergedChannels); + + QProcessEnvironment proEnv = QProcessEnvironment::systemEnvironment(); + if (!env.isEmpty()) { +#if defined(Q_WS_WIN) + proEnv.insert("PATH", QDir::toNativeSeparators(env) + ";" + proEnv.value("PATH")); +#elif defined(Q_WS_MAC) + proEnv.insert("DYLD_LIBRARY_PATH", env); +#else + proEnv.insert("LD_LIBRARY_PATH", env); +#endif + } + proEnv.insert("QTDIR", MainWindow::getCurrentQtVersion()->buildDir()); + p->setProcessEnvironment(proEnv); + + if (workdir != QString()) + p->setWorkingDirectory(workdir); + + if (arguments == QStringList()) + p->start(executable); + else + p->start(executable, arguments); + + if (p->waitForFinished(-1) == false) { + Logger::getIt()->log(QString("<font color=blue>\"%1\" exited with code %2</font>").arg(executable).arg(p->exitCode())); + return -1; + } + + if (p->error() == QProcess::Crashed) { + Logger::getIt()->log(QString("<font color=blue>\"%1\" exited with code %2</font>").arg(executable).arg(p->exitCode())); + return -1; + } + + int ret = p->exitCode(); + + Logger::getIt()->log(QString("<font color=blue>\"%1\" exited with code %2</font>").arg(executable).arg(ret)); + + return ret; +} + +/************************************************************************************************** + **************************************************************************************************/ +void Test::setStatus(Test::TestStatus st) +{ + if (st == teststatus) + return; + + teststatus = st; + emit statusChanged(teststatus); +} + +/************************************************************************************************** + **************************************************************************************************/ +void Test::processResult(const QString &result) +{ + r.pass = true; + r.failures.clear(); + r.messages.clear(); + + QString res = result; + res.remove(0, result.indexOf("<?xml")); + QDomDocument doc; + doc.setContent(res); + QDomElement docElem = doc.documentElement(); + + QDomNode testFuncNode = docElem.firstChild(); + while(!testFuncNode.isNull()) { + QDomElement testFunc = testFuncNode.toElement(); + if(!testFunc.isNull() && testFunc.tagName() == "TestFunction") { + QDomNode incidentNode = testFunc.firstChild(); + while(!incidentNode.isNull()) { + QDomElement incident = incidentNode.toElement(); + if(!incident.isNull()) { + if (incident.attribute("type") == "fail") + r.pass = false; + TestIncident i; + i.function = testFunc.attribute("name"); + i.type = incident.attribute("type"); + i.file = incident.attribute("file"); + i.line = incident.attribute("line").toInt(); + QDomNode descNode = incident.firstChild(); + while (!descNode.isNull()) { + QDomElement desc = descNode.toElement(); + if (!desc.isNull() && desc.tagName() == "Description") + i.message = desc.text(); + if(!desc.isNull() && desc.tagName() == "DataTag") + i.data = desc.text(); + descNode = descNode.nextSibling(); + } + if (i.type == "fail") + r.failures.append(i); + else if (!i.message.isEmpty()) + r.messages.append(i); + } + incidentNode = incidentNode.nextSibling(); + } + } + testFuncNode = testFuncNode.nextSibling(); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +bool Test::runInParallel() const +{ + QString profile = MainWindow::getCurrentQtVersion()->testSrcDir() + testPro; + static QHash<QString,bool> cache; + if (!cache.contains(profile)) { + QFile pro(profile); + bool found = false; + + if (pro.open(QIODevice::ReadOnly | QIODevice::Text)) { + while (!pro.atEnd()) { + QString line = pro.readLine(); + if (line.contains("parallel_test")) { + found = true; + break; + } + } + } else { + qWarning() << "Cannot open: " << profile; + } + + cache.insert(profile, found); + } + + return cache[profile]; +} + +/************************************************************************************************** + **************************************************************************************************/ +QString Test::makePrintable(const QString &out) +{ + static QString testName; + static QString currentFunction; + static QString file; + static QString linenb; + static QString desc; + static QString data; + static int passed; + static int failed; + static int skipped; + + if (out.isEmpty()) + return ""; + + QString res; + QString line_break; + QTextStream dummy(&line_break); + dummy << endl; + QStringList list = out.split(line_break); + bool inDescription = false; + foreach (const QString &line, list) { + QString trimmed = line.trimmed(); + if (trimmed.isEmpty() || trimmed.startsWith("<?")) { + continue; + } else if (!trimmed.startsWith("<")) { + if (inDescription) { + trimmed.replace("]]>", ""); + desc += "<br> " + trimmed; + } else { + res += trimmed + "<br>"; + } + } else if (trimmed == "</TestCase>") { + res += QString("Totals: %1 passed, %2 failed, %3 skipped<br>").arg(passed).arg(failed).arg(skipped); + res += "********* Finished testing of " + testName + " *********<br>"; + } else if (trimmed == "</Environment>") { + res += "<br>"; + } else if (trimmed == "</Incident>" || trimmed == "</Message>") { + res += ": " + testName + "::" + currentFunction + "(" + data + ") " + desc + "<br>"; + if (!file.isEmpty()) + res += " Loc: [" + file + "(" + linenb + ")]<br>"; + } else if (trimmed == "</Description>") { + inDescription = false; + } else { + QDomDocument doc; + doc.setContent(trimmed); + QDomElement docElem = doc.documentElement(); + QString tagName = docElem.tagName(); + if (tagName == "TestCase") { + passed = 0; + failed = 0; + skipped = 0; + testName = docElem.attribute("name"); + res += "********* Start testing of " + testName + " *********<br>"; + } else if (tagName == "Environment") { + res += "Config: "; + } else if (tagName == "QtVersion") { + res += "Using Qt " + docElem.text() + ", "; + } else if (tagName == "QTestVersion") { + res += "QTest library " + docElem.text(); + } else if (tagName == "TestFunction") { + currentFunction = docElem.attribute("name"); + desc = ""; + data = ""; + } else if (tagName == "Incident") { + QString type = docElem.attribute("type"); + file = docElem.attribute("file"); + linenb = docElem.attribute("line"); + if (type == "pass") { + ++passed; + res += "<font color=green>PASS </font>"; + } else if (type == "xfail") { + res += "<font color=#00FF00>XFAIL </font>"; + } else if (type == "fail") { + ++failed; + res += "<font color=red>FAIL! </font>"; + } + if (trimmed.endsWith("/>")) + res += ": " + testName + "::" + currentFunction + "()<br>"; + } else if (tagName == "Message") { + file = docElem.attribute("file"); + linenb = docElem.attribute("line"); + QString type = docElem.attribute("type"); + if (type == "qwarn") { + res += "<font color=#D1C005>QWARN </font>"; + } else if (type == "qdebug") { + res += "<font color=#D1C005>QDEBUG </font>"; + } else if (type == "warn") { + res += "<font color=#B3641A>WARNING</font>"; + } else if (type == "qfatal") { + res += "<font color=red>QFATAL </font>"; + } else if (type == "skip") { + ++skipped; + res += "SKIP "; + } + if (trimmed.endsWith("/>")) + res += ": " + testName + "::" + currentFunction + "()<br>"; + } else if (tagName == "DataTag") { + data = docElem.text(); + } else if (tagName == "Description") { + inDescription = true; + desc = docElem.text(); + if (desc.isEmpty()) { + desc = trimmed; + desc.replace("<Description><![CDATA[", ""); + } + } + } + } + + if (res.endsWith("<br>")) + res.chop(4); + + return res; +} + +/************************************************************************************************** + **************************************************************************************************/ +void Test::onProcessReadyRead() +{ + QString read = p->readAll(); + output += read; + Logger::getIt()->log(makePrintable(read)); +} @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEST_H +#define TEST_H + +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QString> +#include <QtCore/QMetaType> +#include <QHash> + +class QProcess; + +struct TestIncident +{ + QString function; + QString type; + QString data; + QString message; + QString file; + int line; +}; + +struct TestResult +{ + bool pass; + QList<TestIncident> failures; + QList<TestIncident> messages; +}; + +class Test : public QObject +{ + Q_OBJECT + +public: + enum TestStatus { + Initial, + Building, + BuildError, + Running, + RunError, + TestPass, + TestFail, + Skipped + }; + Q_ENUMS(TestStatus) + + Test(const QString& name, const QString& path, const QString &pro); + ~Test(); + + inline QString name() const + { return testName; } + inline QString path() const + { return testPath; } + inline QString executable() const + { return testExecutable; } + inline Test::TestStatus status() const + { return teststatus; } + inline void reset() + { setStatus(Initial); } + TestResult result() const + { return r; } + inline QString proFile() const + { return testPro; } + bool runInParallel() const; + + void run(); + void cancelRun(); + + inline static void setRunQMake(bool r) + { runQMake = r; } + static void setGlobalArguments(const QString &args) + { m_globalArguments = args; } + static QString globalArguments() + { return m_globalArguments; } + + inline bool isAvailable() const + { return testIsAvailable; } + inline void setAvailable(bool a) + { testIsAvailable = a; } + +Q_SIGNALS: + void statusChanged(Test::TestStatus status); + +protected: + int buildTest(); + int runTest(); + int runExecutable(const QString &workdir, + const QString &executable, + const QString &env, + const QStringList &arguments, + bool ignoreErrorChannel = false); + void setStatus(Test::TestStatus st); + void processResult(const QString &result); + QString makePrintable(const QString &out); + +protected Q_SLOTS: + void onProcessReadyRead(); + +private: + QString testName; + QString testPath; + QString testExecutable; + TestStatus teststatus; + TestResult r; + QProcess *p; + QString testPro; + static bool runQMake; + static QString m_globalArguments; + bool testIsAvailable; + QString output; +}; + +Q_DECLARE_METATYPE(Test::TestStatus); + +#endif // TEST_H diff --git a/testgraphicsview.cpp b/testgraphicsview.cpp new file mode 100644 index 0000000..b6b7091 --- /dev/null +++ b/testgraphicsview.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "testgraphicsview.h" +#include "testviewlayout.h" +#include "testview.h" +#include "testresultwidget.h" +#include "test.h" +#include <math.h> + +#include <QGraphicsWidget> +#include <QGraphicsDropShadowEffect> +#include <QStateMachine> +#include <QSignalTransition> +#include <QListWidget> +#include <QLayout> +#include <QLabel> +#include <QPropertyAnimation> +#include <QDebug> + +/************************************************************************************************** + **************************************************************************************************/ +TestGraphicsView::TestGraphicsView(QWidget *parent) + : QGraphicsView(parent), + currentZoomLevel(1.0) +{ + setOptimizationFlag(QGraphicsView::DontSavePainterState); + setCacheMode(QGraphicsView::CacheBackground); + setAlignment(Qt::AlignTop | Qt::AlignLeft); + setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + setScene(new QGraphicsScene); + + testsContainer = new QGraphicsWidget; + testsContainer->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + layout = new TestViewLayout(TestViewLayout::Vertical, 1); + layout->setScene(scene()); + layout->setHorizontalSpacing(ITEM_WIDTH + 15); + layout->setVerticalSpacing(ITEM_HEIGHT + 15); + layout->setContentsMargins(20, 20, 20, 20); + testsContainer->setLayout(layout); + scene()->addItem(testsContainer); + + widget = new TestResultWidget(this); + widget->setVisible(false); + QGraphicsDropShadowEffect *shad = new QGraphicsDropShadowEffect; + shad->setBlurRadius(10); + shad->setColor(QColor(50, 50, 50)); + widget->setGraphicsEffect(shad); + + machine = new QStateMachine; + detailsVisible = new QState; + detailsVisible->assignProperty(testsContainer, "opacity", 0.4); + detailsVisible->assignProperty(widget, "visible", true); + + detailsNotVisible = new QState; + detailsNotVisible->assignProperty(testsContainer, "opacity", 1); + detailsNotVisible->assignProperty(widget, "visible", false); + + QSignalTransition *transition1 = detailsVisible->addTransition(widget, SIGNAL(clicked()), detailsNotVisible); + QSignalTransition *transition2 = detailsNotVisible->addTransition(this, SIGNAL(detailsRequested()), detailsVisible); + transition1->addAnimation(new QPropertyAnimation(testsContainer, "opacity")); + transition2->addAnimation(new QPropertyAnimation(testsContainer, "opacity")); + + machine->addState(detailsVisible); + machine->addState(detailsNotVisible); + machine->setInitialState(detailsNotVisible); + machine->start(); +} + +/************************************************************************************************** + **************************************************************************************************/ +TestGraphicsView::~TestGraphicsView() +{ + delete machine; +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestGraphicsView::wheelEvent(QWheelEvent *event) +{ + if (event->modifiers() & Qt::ControlModifier) { + if (event->delta() > 0) + zoomIn(); + else + zoomOut(); + } else { + QGraphicsView::wheelEvent(event); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestGraphicsView::resizeEvent(QResizeEvent *event) +{ + QGraphicsView::resizeEvent(event); + + updateLayout(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestGraphicsView::updateLayout() +{ + QSizeF effectiveSize = mapToScene(rect()).boundingRect().size(); + + int length; + if (layout->flowDirection() == TestViewLayout::Vertical) + length = (effectiveSize.width() - 40) / layout->horizontalSpacing(); + else + length = (effectiveSize.height() - 40) / layout->verticalSpacing(); + layout->setLayoutLength(length); + + QLinearGradient grad(0, 0, 0, effectiveSize.height()); + grad.setColorAt(0, Qt::black); + grad.setColorAt(0.49, "dimgray"); + grad.setColorAt(0.51, "dimgray"); + grad.setColorAt(0.55, QColor(80, 80, 80)); + grad.setColorAt(1, Qt::black); + setBackgroundBrush(grad); + + if (widget->isVisible()) + adjustDetailsWidget(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestGraphicsView::showTestResultsDetail(TestView *t) +{ + widget->setTest(t); + adjustDetailsWidget(); + + emit detailsRequested(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestGraphicsView::adjustDetailsWidget() +{ + QSize contSize = widget->contentsSize(); + QMargins margins = widget->frame()->layout()->contentsMargins(); + int w = margins.left() + contSize.width() + margins.right() + 45; + int h = margins.top() + + widget->title()->height() + + widget->failCount()->height() + + widget->frame()->layout()->spacing() * 2 + + contSize.height() + + margins.bottom() + 15; + w = qMin(width() * 0.8, qreal(w)); + h = qMin(height() * 0.7, qreal(h)); + widget->resize(w, h); + + widget->move(width() / 2 - widget->width() / 2, + height() / 2 - widget->height() / 2); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestGraphicsView::zoomIn() +{ + setZoom(currentZoomLevel * ZOOM_IN_FACTOR); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestGraphicsView::zoomOut() +{ + setZoom(currentZoomLevel * ZOOM_OUT_FACTOR); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestGraphicsView::resetZoom() +{ + setZoom(1.0); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestGraphicsView::zoomFit() +{ + qreal ratio = qreal(width()) / qreal(height()); + int rows = ::sqrt(layout->count() / ratio) + 1; + + qreal fitFactor = height() / layout->verticalSpacing() / rows; + qreal margin = 40 * fitFactor; + fitFactor = (height() - margin) / layout->verticalSpacing() / rows; + + fitFactor = qMax(qMin(fitFactor, ZOOM_MAX), ZOOM_MIN); + setZoom(fitFactor); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestGraphicsView::setZoom(qreal z) +{ + if (currentZoomLevel == z || z > ZOOM_MAX || z < ZOOM_MIN) + return; + + currentZoomLevel = z; + setTransform(QTransform().scale(currentZoomLevel, currentZoomLevel)); + updateLayout(); + emit zoomLevelChanged(currentZoomLevel); +} diff --git a/testgraphicsview.h b/testgraphicsview.h new file mode 100644 index 0000000..c6e9166 --- /dev/null +++ b/testgraphicsview.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTGRAPHICSVIEW_H +#define TESTGRAPHICSVIEW_H + +#include <QGraphicsView> +#include <QGraphicsProxyWidget> + +#define ZOOM_IN_FACTOR 1.18921 +#define ZOOM_OUT_FACTOR 0.840896 +#define ZOOM_MIN 0.29 +#define ZOOM_MAX 2.4 + +class TestViewLayout; +class TestResultWidget; +class TestView; +class QStateMachine; +class QState; +class QGraphicsOpacityEffect; +class QGraphicsDropShadowEffect; + +class TestGraphicsView : public QGraphicsView +{ + Q_OBJECT + +public: + TestGraphicsView(QWidget *parent = 0); + ~TestGraphicsView(); + + TestViewLayout *itemsLayout() const + { return layout; } + + void showTestResultsDetail(TestView *t); + void zoomIn(); + void zoomOut(); + void resetZoom(); + void zoomFit(); + + inline qreal zoom() const + { return currentZoomLevel; } + void setZoom(qreal z); + +Q_SIGNALS: + void detailsRequested(); + void zoomLevelChanged(qreal zoomLevel); + +protected: + void wheelEvent(QWheelEvent *event); + void resizeEvent(QResizeEvent *event); + void adjustDetailsWidget(); + void updateLayout(); + +private: + TestViewLayout *layout; + QGraphicsWidget *testsContainer; + TestResultWidget *widget; + + QStateMachine *machine; + QState *detailsVisible; + QState *detailsNotVisible; + qreal currentZoomLevel; +}; + +#endif // TESTGRAPHICSVIEW_H diff --git a/testprofile.cpp b/testprofile.cpp new file mode 100644 index 0000000..1dc0afa --- /dev/null +++ b/testprofile.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "testprofile.h" +#include "test.h" + +#include <QtConcurrentRun> + +#define PARALLEL_TESTS 15 + +/************************************************************************************************** + **************************************************************************************************/ +TestProfile::TestProfile(const QString &name) + : QObject(), + profileName(name), + profileState(Saved), + runState(NotRunning), + currentTestToRun(0), + currentParallelTestToRun(0), + runningTestCount(0), + stopRun(false) +{ + QThreadPool::globalInstance()->setMaxThreadCount(PARALLEL_TESTS); +} + +/************************************************************************************************** + **************************************************************************************************/ +TestProfile::~TestProfile() +{ + foreach (Test* test, tests.values()) + delete test; + + foreach(Test* test, unsavedAddedTests) + delete test; +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestProfile::addTest(Test *test) +{ + if (!test) + return; + + if (unsavedRemovedTests.contains(test->name())) + unsavedRemovedTests.removeAll(test->name()); + + if (tests.contains(test->name()) || unsavedAddedTests.contains(test->name())) + return; + + unsavedAddedTests.insert(test->name(), test); + + if (profileState == Saved) { + profileState = Unsaved; + emit stateChanged(profileState); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestProfile::removeTest(const QString &testName) +{ + if (tests.contains(testName)) { + unsavedRemovedTests.append(testName); + } else if (unsavedAddedTests.contains(testName)) { + delete unsavedAddedTests[testName]; + unsavedAddedTests.remove(testName); + } + + if (profileState == Saved) { + profileState = Unsaved; + emit stateChanged(profileState); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestProfile::run(Test *test) +{ + if (runState != NotRunning || !test->isAvailable()) + return; + + runState = Running; + emit runningStateChanged(); + + testsToRun.clear(); + parallelTestsToRun.clear(); + testsToRun.insert(test->name(), test); + + foreach (Test* test, testsToRun.values()) + test->reset(); + + currentTestToRun = 0; + currentParallelTestToRun = 0; + runNextTest(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestProfile::run() +{ + runState = Running; + emit runningStateChanged(); + + testsToRun.clear(); + parallelTestsToRun.clear(); + QList<Test *> toRun = tests.values() + unsavedAddedTests.values(); + foreach (Test *test, toRun) { + if (test->isAvailable() && !unsavedRemovedTests.contains(test->name())) { + if (test->runInParallel()) { + parallelTestsToRun.insert(test->name(), test); + } else { + testsToRun.insert(test->name(), test); + } + } + } + foreach (Test* test, testsToRun.values()) + test->reset(); + foreach (Test* test, parallelTestsToRun.values()) + test->reset(); + + currentTestToRun = 0; + currentParallelTestToRun = 0; + runNextTest(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestProfile::cancelRun(bool cancelOnlyCurrentTest) +{ + stopRun = !cancelOnlyCurrentTest; + if (currentTestToRun - 1 >= 0) + testsToRun.values().at(currentTestToRun - 1)->cancelRun(); + + if (stopRun) { + foreach (Test *test, tests) { + if (test->status() == Test::Building || + test->status() == Test::Running) + test->cancelRun(); + } + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestProfile::runNextTest() +{ + if (((currentTestToRun >= testsToRun.count() && currentParallelTestToRun >= parallelTestsToRun.count()) || stopRun) && runningTestCount == 0) { + stopRun = false; + runState = NotRunning; + emit runningStateChanged(); + return; + } + + if (stopRun) + return; + + while ((testsToRun.count() > currentTestToRun || + parallelTestsToRun.count() > currentParallelTestToRun) + && runningTestCount < PARALLEL_TESTS) { + + Test *test = 0; + + if (parallelTestsToRun.count() > currentParallelTestToRun) { + test = parallelTestsToRun.values().at(currentParallelTestToRun); + currentParallelTestToRun++; + } else if (testsToRun.count() > currentTestToRun) { + if (runningTestCount != 0) + return; // We can't run in parallel with another test + test = testsToRun.values().at(currentTestToRun); + currentTestToRun++; + } else { + return; + } + + connect(test, SIGNAL(statusChanged(Test::TestStatus)), + this, SLOT(onRunningTestStatusChanged(Test::TestStatus))); + runningTestCount++; + QtConcurrent::run(test, &Test::run); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestProfile::onRunningTestStatusChanged(Test::TestStatus status) +{ + Q_UNUSED(status); + + Test *test = qobject_cast<Test *>(sender()); + if (!test) + return; + + if (test->status() != Test::Building && test->status() != Test::Running) { + disconnect(test, SIGNAL(statusChanged(Test::TestStatus)), + this, SLOT(onRunningTestStatusChanged(Test::TestStatus))); + runningTestCount--; + runNextTest(); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestProfile::save() +{ + foreach (Test *test, unsavedAddedTests.values()) + tests.insert(test->name(), test); + unsavedAddedTests.clear(); + foreach (const QString &test, unsavedRemovedTests) { + delete tests[test]; + tests.remove(test); + } + unsavedRemovedTests.clear(); + + profileState = Saved; + emit stateChanged(profileState); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestProfile::restore() +{ + foreach(Test* test, unsavedAddedTests) + delete test; + unsavedAddedTests.clear(); + unsavedRemovedTests.clear(); + + profileState = Saved; + emit stateChanged(profileState); +} + +void TestProfile::setVariant(const QString& var) +{ + buildVariant = var; +} diff --git a/testprofile.h b/testprofile.h new file mode 100644 index 0000000..b3ee3fd --- /dev/null +++ b/testprofile.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTPROFILE_H +#define TESTPROFILE_H + +#include <QObject> +#include <QHash> +#include <QMap> +#include <QSet> +#include <QStringList> +#include "test.h" + +class QtDirectory; + +class TestProfile : public QObject +{ + Q_OBJECT + +public: + enum ProfileState { + Saved, + Unsaved + }; + + enum RunningState { + NotRunning, + Running + }; + + explicit TestProfile(const QString &name = QString()); + ~TestProfile(); + + inline QString name() const + { return profileName; } + inline void setName(const QString& name) + { profileName = name; } + inline ProfileState state() const + { return profileState; } + inline QStringList testNames() const + { return (tests.keys().toSet() + unsavedAddedTests.keys().toSet() - unsavedRemovedTests.toSet()).toList(); } + inline int testCount() const + { return tests.count() + unsavedAddedTests.count() - unsavedRemovedTests.count(); } + inline QList<Test*> savedTests() const + { return tests.values(); } + inline void addTestToList(Test *test) + { + tests.insert(test->name(), test); + } + inline TestProfile::RunningState runningState() const + { return runState; } + inline Test *test(const QString &name) const + { + Test *t = tests.value(name); + if (t) + return t; + else + return unsavedAddedTests.value(name); + } + inline QString variant() const + { return buildVariant; } + + void setVariant(const QString &var); + + void addTest(Test *test); + void removeTest(const QString &testName); + void restore(); + + void run(); + void run(Test *); + void cancelRun(bool cancelOnlyCurrentTest = false); + void save(); + +Q_SIGNALS: + void stateChanged(TestProfile::ProfileState newState); + void runningStateChanged(); + +protected Q_SLOTS: + void onRunningTestStatusChanged(Test::TestStatus status); + +protected: + void runNextTest(); + +private: + QString profileName; + QHash<QString, Test*> tests; + QHash<QString, Test*> unsavedAddedTests; + QStringList unsavedRemovedTests; + ProfileState profileState; + RunningState runState; + + QMap<QString, Test*> testsToRun; + QMap<QString, Test*> parallelTestsToRun; + + QString buildVariant; + int currentTestToRun; + int currentParallelTestToRun; + int runningTestCount; + + bool stopRun; +}; + +#endif // TESTPROFILE_H diff --git a/testresultwidget.cpp b/testresultwidget.cpp new file mode 100644 index 0000000..6d82f2d --- /dev/null +++ b/testresultwidget.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "testresultwidget.h" +#include "ui_testresultwidget.h" +#include "test.h" +#include "testview.h" +#include <QDebug> + +/************************************************************************************************** + **************************************************************************************************/ +TestResultWidget::TestResultWidget(QWidget *parent) : + QWidget(parent), + ui(new Ui::TestResultWidget) +{ + ui->setupUi(this); + setAttribute(Qt::WA_NoSystemBackground); +} + +/************************************************************************************************** + **************************************************************************************************/ +TestResultWidget::~TestResultWidget() +{ + delete ui; +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestResultWidget::setTest(TestView *t) +{ + connect(t, SIGNAL(destroyed()), this, SLOT(onTestChanged())); + connect(t->test(), SIGNAL(statusChanged(Test::TestStatus)), this, SLOT(onTestChanged())); + test = t; + + ui->title->setText(t->test()->name()); + TestResult r = t->test()->result(); + QString incidentCount; + if (r.failures.count() > 0) + incidentCount = QString::number(r.failures.count()) + QLatin1String(" failed"); + if (r.messages.count() > 0) { + incidentCount += QLatin1String(r.failures.count() > 0 ? ", " : "") + + QString::number(r.messages.count()) + + QLatin1String(r.messages.count() > 1 ? " messages" : " message"); + } + ui->failcount->setText(incidentCount); + + // Yes, this is the problem with using style sheets... + ui->frame->setStyleSheet( + r.pass ? "QFrame#frame {\nbackground-color: qlineargradient(spread:pad, x1:0.42, y1:0, x2:0.76, y2:1, stop:0 rgba(75, 75, 20, 255), stop:1 rgba(255, 255, 71, 255));\nborder: 2px solid rgba(205, 205, 92, 255);\nborder-radius: 20px;\n}" + : "QFrame#frame {\nbackground-color: qlineargradient(spread:pad, x1:0.42, y1:0, x2:0.76, y2:1, stop:0 rgba(75, 29, 20, 255), stop:1 rgba(255, 99, 71, 255));\nborder: 2px solid rgba(205, 92, 92, 255);\nborder-radius: 20px;\n}" + ); + + ui->listWidget->clear(); + listSize = QSize(0, 0); + QFontMetrics f(ui->listWidget->font()); + QPixmap pix(":/images/error.png"); + + QList<TestIncident> allIncidents = r.failures + r.messages; + foreach (const TestIncident &incident, allIncidents) { + QString firstL = incident.type.toUpper() + ": " + incident.function + "(" + incident.data + ")"; + QString secondL = incident.message; + QString thirdL; + if (!incident.file.isEmpty() || incident.line) + thirdL = "Loc: [" + incident.file + "(" + QString::number(incident.line) + ")]"; + QIcon ico(":/images/error.png"); + ui->listWidget->addItem(new QListWidgetItem(ico, firstL.toLatin1())); + ui->listWidget->addItem(secondL.toLatin1()); + if (!thirdL.isEmpty()) + ui->listWidget->addItem(thirdL.toLatin1()); + ui->listWidget->addItem(""); + + int lines = qMax(secondL.count('\n'), qMax(secondL.count("\r\n"), secondL.count('\r'))) + 1; + int height = qMax(pix.height(), f.height()) + (lines + (thirdL.isEmpty() ? 1 : 2)) * f.height(); + int maxWidth = qMax(f.width(pix.width() + firstL), qMax(f.width(secondL), f.width(thirdL))); + listSize.setHeight(listSize.height() + height); + listSize.setWidth(qMax(listSize.width(), maxWidth)); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +QListWidget *TestResultWidget::listWidget() const +{ + return ui->listWidget; +} + +/************************************************************************************************** + **************************************************************************************************/ +QFrame *TestResultWidget::frame() const +{ + return ui->frame; +} + +/************************************************************************************************** + **************************************************************************************************/ +QLabel *TestResultWidget::title() const +{ + return ui->title; +} + +/************************************************************************************************** + **************************************************************************************************/ +QLabel *TestResultWidget::failCount() const +{ + return ui->failcount; +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestResultWidget::mouseReleaseEvent(QMouseEvent *e) +{ + Q_UNUSED(e); + + emit clicked(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestResultWidget::onTestChanged() +{ + disconnect(test, SIGNAL(destroyed()), this, SLOT(onTestChanged())); + disconnect(test->test(), SIGNAL(statusChanged(Test::TestStatus)), this, SLOT(onTestChanged())); + + emit clicked(); +} diff --git a/testresultwidget.h b/testresultwidget.h new file mode 100644 index 0000000..7a9c38f --- /dev/null +++ b/testresultwidget.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTRESULTWIDGET_H +#define TESTRESULTWIDGET_H + +#include <QWidget> + +class TestView; +class QListWidget; +class QFrame; +class QLabel; + +namespace Ui { + class TestResultWidget; +} + +class TestResultWidget : public QWidget { + Q_OBJECT + +public: + explicit TestResultWidget(QWidget *parent = 0); + ~TestResultWidget(); + + void setTest(TestView *t); + QListWidget *listWidget() const; + QFrame *frame() const; + QLabel *title() const; + QLabel *failCount() const; + QSize contentsSize() const + { return listSize; } + +Q_SIGNALS: + void clicked(); + +protected: + void mouseReleaseEvent(QMouseEvent *); + +protected Q_SLOTS: + void on_listWidget_clicked() + { emit clicked(); } + void onTestChanged(); + +private: + Ui::TestResultWidget *ui; + TestView *test; + QSize listSize; +}; + +#endif // TESTRESULTWIDGET_H diff --git a/testresultwidget.ui b/testresultwidget.ui new file mode 100644 index 0000000..2f159d9 --- /dev/null +++ b/testresultwidget.ui @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>TestResultWidget</class> + <widget class="QWidget" name="TestResultWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>365</width> + <height>305</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QFrame" name="frame"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="styleSheet"> + <string notr="true">QFrame#frame { +background-color: qlineargradient(spread:pad, x1:0.42, y1:0, x2:0.76, y2:1, stop:0 rgba(75, 29, 20, 255), stop:1 rgba(255, 99, 71, 255)); +border: 2px solid rgba(205, 92, 92, 255); +border-radius: 20px; +}</string> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>8</number> + </property> + <property name="leftMargin"> + <number>15</number> + </property> + <property name="topMargin"> + <number>8</number> + </property> + <property name="rightMargin"> + <number>15</number> + </property> + <property name="bottomMargin"> + <number>15</number> + </property> + <item> + <widget class="QLabel" name="title"> + <property name="font"> + <font> + <family>Sans Serif</family> + <pointsize>15</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="styleSheet"> + <string notr="true">color: white</string> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string/> + </property> + <property name="pixmap"> + <pixmap resource="images.qrc">:/images/warn.png</pixmap> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="failcount"> + <property name="font"> + <font> + <pointsize>14</pointsize> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="styleSheet"> + <string notr="true">color: white</string> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QListWidget" name="listWidget"> + <property name="styleSheet"> + <string notr="true">QListWidget#listWidget { + background-color: rgba(255, 255, 255, 0); +} + +QListWidget#listWidget:item { + color: white; +}</string> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="showDropIndicator" stdset="0"> + <bool>false</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::NoSelection</enum> + </property> + <item> + <property name="text"> + <string>Test</string> + </property> + </item> + <item> + <property name="text"> + <string> Test2</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources> + <include location="images.qrc"/> + </resources> + <connections/> +</ui> diff --git a/testview.cpp b/testview.cpp new file mode 100644 index 0000000..3f60869 --- /dev/null +++ b/testview.cpp @@ -0,0 +1,341 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include "testview.h" +#include "testprofile.h" +#include "testgraphicsview.h" + +#include <QPen> +#include <QFontMetricsF> +#include <QGraphicsDropShadowEffect> +#include <QGraphicsSceneContextMenuEvent> +#include <QMenu> +#include <QVariant> +#include <QGraphicsScene> +#include <QDebug> + +/************************************************************************************************** + **************************************************************************************************/ +TestView::TestView(Test *test, QGraphicsItem *parent) + : QGraphicsWidget(parent), + t(test) +{ + setObjectName(test->name()); + setCacheMode(QGraphicsItem::ItemCoordinateCache); + + backItem = new QGraphicsPathItem; + QPainterPath path; + path.addRoundedRect(0, 0, ITEM_WIDTH, ITEM_HEIGHT, 15, 15); + backItem->setPath(path); + backItem->setPen(QPen(QColor(BORDER_COLOR_INIT), 2)); + backItem->setBrush(createGradientFromColor(COLOR_INIT)); + backItem->setParentItem(this); + + setVisible(false); + resize(ITEM_WIDTH, ITEM_HEIGHT); + setToolTip(test->name()); + + setTransformOriginPoint(ITEM_WIDTH / 2.0, ITEM_HEIGHT / 2.0); + + QString testname = test->name(); + + // Hardcore some softbreaks... + if (testname.startsWith("qdeclarative")) + testname = testname.left(12)+QChar(0x200B)+testname.mid(12); + else if (testname.startsWith("qgraphics")) + testname = testname.left(9)+QChar(0x200B)+testname.mid(9); + else if (testname.startsWith("qscript")) + testname = testname.left(7)+QChar(0x200B)+testname.mid(7); + else if (testname.startsWith("qnetwork")) + testname = testname.left(8)+QChar(0x200B)+testname.mid(8); + + title = new QGraphicsTextItem(testname, this); + title->setTextInteractionFlags(Qt::NoTextInteraction); + title->setDefaultTextColor(Qt::white); + QFont f("Monotype", 9, QFont::DemiBold); + QFontMetricsF fm(f); + if (fm.width(test->name()) >= ITEM_WIDTH - 10) { + title->setTextWidth(ITEM_WIDTH - 10); + } + title->setFont(f); + title->setPos(geometry().width() / 2 - title->boundingRect().width() / 2, 2); + + QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect; + shadow->setBlurRadius(4); + shadow->setOffset(2, 3); + shadow->setColor(QColor(50, 50, 50)); + title->setGraphicsEffect(shadow); + + imgContainer = new QGraphicsWidget(this); + img = new QGraphicsPixmapItem(imgContainer); + img->setPos(0, 0); + + QFont f2("Monotype", 8/*, QFont::DemiBold*/); + QFont f3("Monotype", 14, QFont::DemiBold); + nbFail = new QGraphicsTextItem(imgContainer); + nbFail->setTextInteractionFlags(Qt::NoTextInteraction); + nbFail->setFont(f3); + nbFail->setDefaultTextColor(Qt::white); + message = new QGraphicsTextItem(this); + message->setTextInteractionFlags(Qt::NoTextInteraction); + message->setFont(f2); + message->setDefaultTextColor(Qt::white); + + connect(t, SIGNAL(statusChanged(Test::TestStatus)), this, SLOT(onTestStatusChanged(Test::TestStatus))); +} + +/************************************************************************************************** + **************************************************************************************************/ +TestView::~TestView() +{ + +} + +/************************************************************************************************** + **************************************************************************************************/ +QLinearGradient TestView::createGradientFromColor(const QColor &c) +{ + QLinearGradient linearGrad(QPointF(backItem->boundingRect().width() * 0.42, 0), + QPointF(backItem->boundingRect().width() * 0.76, backItem->boundingRect().height())); + QColor dark; + dark.setCmyk(c.cyan(), c.magenta(), c.yellow(), 180); + linearGrad.setColorAt(0, dark); + linearGrad.setColorAt(1, c); + return linearGrad; +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestView::onTestStatusChanged(Test::TestStatus status) +{ + QColor bc; + QColor c; + + img->setOpacity(1); + + int failCount = t->result().failures.count(); + int warnCount = 0; + foreach (const TestIncident &incident, t->result().messages) { + if (incident.type == "qwarning") + warnCount++; + } + nbFail->setPlainText(""); + nbFail->setVisible(false); + + switch (status) { + case Test::Initial: + bc = BORDER_COLOR_INIT; + c = COLOR_INIT; + img->setVisible(false); + message->setVisible(false); + break; + case Test::Building: + img->setPixmap(QPixmap(":/images/build.png").scaled(QSize(32, 32), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + img->setVisible(true); + message->setVisible(false); + bc = BORDER_COLOR_RUN; + c = COLOR_RUN; + break; + case Test::Running: + img->setPixmap(QPixmap(":/images/gear.png").scaled(QSize(32, 32), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + img->setVisible(true); + message->setVisible(false); + bc = BORDER_COLOR_RUN; + c = COLOR_RUN; + break; + case Test::BuildError: + img->setPixmap(QPixmap(":/images/warn.png")); + img->setVisible(true); + bc = BORDER_COLOR_FAIL; + c = COLOR_FAIL; + message->setPlainText("Build Error"); + message->setVisible(true); + break; + case Test::RunError: + img->setPixmap(QPixmap(":/images/warn.png")); + img->setVisible(true); + message->setPlainText("Run Error"); + message->setVisible(true); + bc = BORDER_COLOR_FAIL; + c = COLOR_FAIL; + break; + case Test::TestFail: + case Test::TestPass: + if (failCount == 0 && t->result().messages.count() == 0) { + img->setPixmap(QPixmap(":/images/check.png").scaled(QSize(32, 32), Qt::KeepAspectRatio, Qt::SmoothTransformation)); + img->setOpacity(0.5); + message->setVisible(false); + } else { + img->setPixmap(QPixmap(":/images/warn.png")); + nbFail->setPlainText(QString::number(failCount == 0 ? t->result().messages.count() : failCount)); + nbFail->setPos(img->boundingRect().width() + 2, -6); + nbFail->setVisible(true); + message->setPlainText(QLatin1String(failCount == 0 ? + QLatin1String(t->result().messages.count() > 1 ? "messages" : "message") + : QLatin1String(failCount > 1 ? "failures" : "failure"))); + message->setVisible(true); + } + img->setVisible(true); + if (failCount > 0) { + bc = BORDER_COLOR_FAIL; + c = COLOR_FAIL; + } else if (warnCount > 0) { + bc = BORDER_COLOR_WARN; + c = COLOR_WARN; + } else { + bc = BORDER_COLOR_PASS; + c = COLOR_PASS; + } + break; + case Test::Skipped: + img->setVisible(false); + message->setPlainText("Skipped"); + message->setVisible(true); + bc = BORDER_COLOR_SKIP; + c = COLOR_SKIP; + break; + } + + qreal imgContainerX = backItem->boundingRect().width() / 2 - img->boundingRect().width() / 2; + qreal imgContainerY = backItem->boundingRect().height() / 2 - img->boundingRect().height() / 2 + 5; + if (nbFail->isVisible()) { + imgContainerX -= nbFail->boundingRect().width() / 2; + imgContainerY -= 5; + } + imgContainer->setPos(imgContainerX, imgContainerY); + imgContainer->setTransformOriginPoint(img->boundingRect().width() / 2, img->boundingRect().height() / 2); + backItem->setPen(QPen(QColor(bc), 2)); + backItem->setBrush(createGradientFromColor(c)); + message->setPos(backItem->boundingRect().width() / 2 - message->boundingRect().width() / 2, + imgContainer->pos().y() + img->boundingRect().height() + 1); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestView::moveEvent(QGraphicsSceneMoveEvent *event) +{ + QGraphicsWidget::moveEvent(event); + + if (!isVisible()) + setVisible(true); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestView::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_UNUSED(event); + + if (event->button() != Qt::LeftButton) { + QGraphicsWidget::mousePressEvent(event); + return; + } + + if (scene()->views().count() == 0) + return; + + TestGraphicsView *view = dynamic_cast<TestGraphicsView *>(scene()->views().at(0)); + if (!view) + return; + + if (t->result().failures.isEmpty() && t->result().messages.isEmpty()) + return; + + view->showTestResultsDetail(this); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestView::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) +{ + QMenu menu; + + QAction *a = menu.addAction(t->name()); + a->setEnabled(false); + menu.addSeparator(); + if ((MainWindow::getCurrentProfile()->runningState() == TestProfile::NotRunning || t->runInParallel()) + && t->status() != Test::Building + && t->status() != Test::Running) { + a = menu.addAction(QIcon::fromTheme("media-playback-start", QIcon(QPixmap(":/images/start.png").scaled(14, 14, Qt::KeepAspectRatio, Qt::SmoothTransformation))), "Run"); + QObject::connect(a, SIGNAL(triggered()), this, SLOT(runTest())); + } + if (t->status() == Test::Building || t->status() == Test::Running) { + a = menu.addAction(QIcon::fromTheme("media-playback-stop", QIcon(QPixmap(":/images/stop.png").scaled(14, 14, Qt::KeepAspectRatio, Qt::SmoothTransformation))), "Cancel"); + QObject::connect(a, SIGNAL(triggered()), this, SLOT(cancelTest())); + } + if (MainWindow::getCurrentProfile()->runningState() == TestProfile::NotRunning) { + menu.addSeparator(); + a = menu.addAction(QIcon::fromTheme("edit-delete", QIcon(QPixmap(":/images/delete.png").scaled(12, 12))), "Remove"); + QObject::connect(a, SIGNAL(triggered()), this, SLOT(removeTest())); + } + + menu.exec(event->screenPos()); + + event->accept(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestView::runTest() +{ + if ((MainWindow::getCurrentProfile()->runningState() == TestProfile::NotRunning || t->runInParallel()) + && t->status() != Test::Building + && t->status() != Test::Running) { + MainWindow::getCurrentProfile()->run(t); + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestView::cancelTest() +{ + if (t->status() == Test::Building || t->status() == Test::Running) + t->cancelRun(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestView::removeTest() +{ + if (MainWindow::getCurrentProfile()->runningState() == TestProfile::NotRunning) + MainWindow::removeTest(t->name()); +} diff --git a/testview.h b/testview.h new file mode 100644 index 0000000..ba90467 --- /dev/null +++ b/testview.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTVIEW_H +#define TESTVIEW_H + +#include <QGraphicsWidget> +#include "test.h" + +#define ITEM_WIDTH 105 +#define ITEM_HEIGHT 105 + +#define BORDER_COLOR_INIT "gray" +#define COLOR_INIT "darkgray" +#define BORDER_COLOR_PASS "olivedrab" +#define COLOR_PASS "yellowgreen" +#define BORDER_COLOR_WARN "#dddd00" +#define COLOR_WARN "yellow" +#define BORDER_COLOR_FAIL "indianred" +#define COLOR_FAIL "tomato" +#define BORDER_COLOR_SKIP "tan" +#define COLOR_SKIP "khaki" +#define BORDER_COLOR_RUN "floralwhite" +#define COLOR_RUN "white" + +class TestView : public QGraphicsWidget +{ + Q_OBJECT + +public: + TestView(Test *test, QGraphicsItem * parent = 0); + ~TestView(); + + Test *test() const + { return t; } + QRectF boundingRect() const + { return backItem->boundingRect(); } + void setGeometry(const QRectF &rect) + { QGraphicsWidget::setGeometry(QRectF(rect.topLeft(), backItem->boundingRect().size())); } + +protected: + QLinearGradient createGradientFromColor(const QColor &c); + void moveEvent(QGraphicsSceneMoveEvent *event); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); + +protected Q_SLOTS: + void onTestStatusChanged(Test::TestStatus status); + void runTest(); + void cancelTest(); + void removeTest(); + +private: + Test *t; + QGraphicsPathItem *backItem; + QGraphicsTextItem *title; + QGraphicsWidget *imgContainer; + QGraphicsPixmapItem *img; + QGraphicsTextItem *nbFail; + QGraphicsTextItem *message; +}; + +#endif // TESTVIEW_H diff --git a/testviewlayout.cpp b/testviewlayout.cpp new file mode 100644 index 0000000..6a177b3 --- /dev/null +++ b/testviewlayout.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "testviewlayout.h" +#include "testview.h" + +#include <QGraphicsScene> +#include <QDebug> + +/************************************************************************************************** + **************************************************************************************************/ +TestViewLayout::TestViewLayout(TestViewLayout::FlowDirection d, int l) + : QGraphicsGridLayout(), + direction(d), + layLength(l) +{ +} + +/************************************************************************************************** + **************************************************************************************************/ +TestViewLayout::~TestViewLayout() +{ +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestViewLayout::setFlowDirection(TestViewLayout::FlowDirection d) +{ + if (direction == d) + return; + + direction = d; + relayout(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestViewLayout::setLayoutLength(int l) +{ + if (layLength == l) + return; + + layLength = l; + relayout(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestViewLayout::addItem(TestView *item) +{ + if (items.contains(item->objectName())) + return; + + items.insert(item->objectName(), item); + QList<TestView *> list = items.values(); + int insIndex = list.indexOf(item); + for (int i = count() - 1; i >= insIndex; i--) { + removeItem(list.at(i + 1)); + int newIndex = i + 1; + int row = newIndex / layLength; + int column = newIndex - row * layLength; + if (direction == Horizontal) + qSwap(row, column); + QGraphicsGridLayout::addItem(list.at(i + 1), row, column); + } + + int row = insIndex / layLength; + int column = insIndex - row * layLength; + if (direction == Horizontal) + qSwap(row, column); + QGraphicsGridLayout::addItem(item, row, column); + + adjustSceneRect(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestViewLayout::removeTestView(TestView *item) +{ + if (!items.contains(item->objectName())) + return; + + QList<TestView *> list = items.values(); + int delIndex = list.indexOf(item); + removeItem(item); + for (int i = delIndex + 1; i < list.count(); i++) { + int newIndex = i - 1; + removeItem(list.at(i)); + int row = newIndex / layLength; + int column = newIndex - row * layLength; + if (direction == Horizontal) + qSwap(row, column); + QGraphicsGridLayout::addItem(list.at(i), row, column); + } + items.remove(item->objectName()); + + adjustSceneRect(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestViewLayout::relayout() +{ + QList<TestView *> list = items.values(); + for (int i = 0; i < list.count(); i++) { + removeItem(list.at(i)); + } + + for (int i = 0; i < list.count(); i++) { + int row = i / layLength; + int column = i - row * layLength; + if (direction == Horizontal) + qSwap(row, column); + QGraphicsGridLayout::addItem(list.at(i), row, column); + } + + for (int i = 0; i < columnCount(); ++i) { + setColumnMinimumWidth(i, 0); + setColumnMaximumWidth(i, 0); + } + for (int i = 0; i < rowCount(); ++i) { + setRowMinimumHeight(i, 0); + setRowMaximumHeight(i, 0); + } + + adjustSceneRect(); +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestViewLayout::removeItem(QGraphicsLayoutItem *item) +{ + for (int i = count() - 1; i >= 0; --i) { + QGraphicsLayoutItem *itemTmp = itemAt(i); + if (item == itemTmp) { + removeAt(i); + invalidate(); + return; + } + } +} + +/************************************************************************************************** + **************************************************************************************************/ +void TestViewLayout::adjustSceneRect() +{ + qreal lmargin, tmargin, rmargin, bmargin; + getContentsMargins(&lmargin, &tmargin, &rmargin, &bmargin); + qreal width = columnCount() * horizontalSpacing() + lmargin + rmargin; + qreal height = rowCount() * verticalSpacing() + tmargin + bmargin; + scene->setSceneRect(0, 0, width, height); +} diff --git a/testviewlayout.h b/testviewlayout.h new file mode 100644 index 0000000..9c0d9c9 --- /dev/null +++ b/testviewlayout.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Autotester project. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTVIEWLAYOUT_H +#define TESTVIEWLAYOUT_H + +#include <QGraphicsGridLayout> + +class TestView; + +class TestViewLayout : public QGraphicsGridLayout +{ +public: + enum FlowDirection { + Horizontal, + Vertical + }; + + TestViewLayout(TestViewLayout::FlowDirection d, int l); + ~TestViewLayout(); + + TestViewLayout::FlowDirection flowDirection() const + { return direction; } + int layoutLength() const + { return layoutLength(); } + + void setFlowDirection(TestViewLayout::FlowDirection d); + void setLayoutLength(int l); + void addItem(TestView *item); + void removeTestView(TestView *item); + + int count() const + { return QGraphicsGridLayout::count(); } + void invalidate() + { QGraphicsGridLayout::invalidate(); } + void removeItem(QGraphicsLayoutItem *item); + void relayout(); + QGraphicsLayoutItem *itemAt(int index) const + { return QGraphicsGridLayout::itemAt(index); } + QGraphicsLayoutItem *itemAt(int row, int column) const + { return QGraphicsGridLayout::itemAt(row, column); } + void removeAt(int index) + { QGraphicsGridLayout::removeAt(index); } + + void clearItems() + { items.clear(); } + + void setScene(QGraphicsScene *s) + { scene = s; } + + +private: + void adjustSceneRect(); + + FlowDirection direction; + int layLength; + QMap<QString, TestView *> items; + QGraphicsScene *scene; +}; + +#endif // TESTVIEWLAYOUT_H |