summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYoann Lopes <yoann.lopes@nokia.com>2010-10-27 11:02:23 +0200
committerYoann Lopes <yoann.lopes@nokia.com>2010-10-27 11:02:23 +0200
commit57f428ed3327dc5c9ad10a46daa6c597b6f73729 (patch)
tree5eb8908398a999e216fdff77ba72764ce824ec43
Initial commit.
-rw-r--r--clearablelineedit.cpp72
-rw-r--r--clearablelineedit.h66
-rw-r--r--images.qrc21
-rw-r--r--images/autotester.icnsbin0 -> 39258 bytes
-rw-r--r--images/autotester.pngbin0 -> 6531 bytes
-rw-r--r--images/build.pngbin0 -> 2163 bytes
-rw-r--r--images/check.pngbin0 -> 3346 bytes
-rw-r--r--images/delete.pngbin0 -> 1405 bytes
-rw-r--r--images/dir.pngbin0 -> 862 bytes
-rw-r--r--images/edit-clear.pngbin0 -> 1682 bytes
-rw-r--r--images/error.pngbin0 -> 498 bytes
-rw-r--r--images/filesave.pngbin0 -> 1133 bytes
-rw-r--r--images/gear.pngbin0 -> 2154 bytes
-rw-r--r--images/minus.pngbin0 -> 258 bytes
-rw-r--r--images/next.pngbin0 -> 494 bytes
-rw-r--r--images/plus.pngbin0 -> 541 bytes
-rw-r--r--images/reload.pngbin0 -> 1414 bytes
-rw-r--r--images/start.pngbin0 -> 739 bytes
-rw-r--r--images/stop.pngbin0 -> 314 bytes
-rw-r--r--images/test.pngbin0 -> 367 bytes
-rw-r--r--images/warn.pngbin0 -> 679 bytes
-rw-r--r--logger.cpp72
-rw-r--r--logger.h67
-rw-r--r--main.cpp62
-rw-r--r--mainwindow.cpp1161
-rw-r--r--mainwindow.h163
-rw-r--r--mainwindow.ui787
-rw-r--r--qt-autotester.pro46
-rw-r--r--qtdirectory.cpp275
-rw-r--r--qtdirectory.h120
-rw-r--r--qtdirectorydialog.cpp325
-rw-r--r--qtdirectorydialog.h97
-rw-r--r--qtdirectorydialog.ui129
-rw-r--r--test.cpp532
-rw-r--r--test.h155
-rw-r--r--testgraphicsview.cpp241
-rw-r--r--testgraphicsview.h103
-rw-r--r--testprofile.cpp271
-rw-r--r--testprofile.h141
-rw-r--r--testresultwidget.cpp163
-rw-r--r--testresultwidget.h88
-rw-r--r--testresultwidget.ui166
-rw-r--r--testview.cpp341
-rw-r--r--testview.h101
-rw-r--r--testviewlayout.cpp190
-rw-r--r--testviewlayout.h99
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
new file mode 100644
index 0000000..a5defd0
--- /dev/null
+++ b/images/autotester.icns
Binary files differ
diff --git a/images/autotester.png b/images/autotester.png
new file mode 100644
index 0000000..88bbb1f
--- /dev/null
+++ b/images/autotester.png
Binary files differ
diff --git a/images/build.png b/images/build.png
new file mode 100644
index 0000000..30a2c62
--- /dev/null
+++ b/images/build.png
Binary files differ
diff --git a/images/check.png b/images/check.png
new file mode 100644
index 0000000..1cced5b
--- /dev/null
+++ b/images/check.png
Binary files differ
diff --git a/images/delete.png b/images/delete.png
new file mode 100644
index 0000000..e42dc2c
--- /dev/null
+++ b/images/delete.png
Binary files differ
diff --git a/images/dir.png b/images/dir.png
new file mode 100644
index 0000000..57cec6b
--- /dev/null
+++ b/images/dir.png
Binary files differ
diff --git a/images/edit-clear.png b/images/edit-clear.png
new file mode 100644
index 0000000..5542948
--- /dev/null
+++ b/images/edit-clear.png
Binary files differ
diff --git a/images/error.png b/images/error.png
new file mode 100644
index 0000000..bb40db9
--- /dev/null
+++ b/images/error.png
Binary files differ
diff --git a/images/filesave.png b/images/filesave.png
new file mode 100644
index 0000000..8f8d38c
--- /dev/null
+++ b/images/filesave.png
Binary files differ
diff --git a/images/gear.png b/images/gear.png
new file mode 100644
index 0000000..de10854
--- /dev/null
+++ b/images/gear.png
Binary files differ
diff --git a/images/minus.png b/images/minus.png
new file mode 100644
index 0000000..4466844
--- /dev/null
+++ b/images/minus.png
Binary files differ
diff --git a/images/next.png b/images/next.png
new file mode 100644
index 0000000..45b21fb
--- /dev/null
+++ b/images/next.png
Binary files differ
diff --git a/images/plus.png b/images/plus.png
new file mode 100644
index 0000000..be8c961
--- /dev/null
+++ b/images/plus.png
Binary files differ
diff --git a/images/reload.png b/images/reload.png
new file mode 100644
index 0000000..9c336d9
--- /dev/null
+++ b/images/reload.png
Binary files differ
diff --git a/images/start.png b/images/start.png
new file mode 100644
index 0000000..4a3788c
--- /dev/null
+++ b/images/start.png
Binary files differ
diff --git a/images/stop.png b/images/stop.png
new file mode 100644
index 0000000..1063d08
--- /dev/null
+++ b/images/stop.png
Binary files differ
diff --git a/images/test.png b/images/test.png
new file mode 100644
index 0000000..61ad743
--- /dev/null
+++ b/images/test.png
Binary files differ
diff --git a/images/warn.png b/images/warn.png
new file mode 100644
index 0000000..645cc5f
--- /dev/null
+++ b/images/warn.png
Binary files differ
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>&nbsp;</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>&amp;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>&amp;File</string>
+ </property>
+ <addaction name="actionSave_Results"/>
+ <addaction name="separator"/>
+ <addaction name="actionQuit"/>
+ </widget>
+ <widget class="QMenu" name="menuView">
+ <property name="title">
+ <string>&amp;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>&amp;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>&amp;Save Results...</string>
+ </property>
+ </action>
+ <action name="actionQuit">
+ <property name="text">
+ <string>&amp;Quit</string>
+ </property>
+ </action>
+ <action name="actionDefault_Zoom">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Default Zoom</string>
+ </property>
+ </action>
+ <action name="actionZoom_to_Fit">
+ <property name="text">
+ <string>Zoom to &amp;Fit</string>
+ </property>
+ </action>
+ <action name="actionZoom_In">
+ <property name="text">
+ <string>Zoom &amp;In</string>
+ </property>
+ </action>
+ <action name="actionZoom_Out">
+ <property name="text">
+ <string>Zoom &amp;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>&amp;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>&amp;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&amp;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>&amp;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>&nbsp;&nbsp;&nbsp;" + 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 += "&nbsp;&nbsp;&nbsp;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&nbsp;&nbsp;&nbsp;</font>";
+ } else if (type == "xfail") {
+ res += "<font color=#00FF00>XFAIL&nbsp;&nbsp;</font>";
+ } else if (type == "fail") {
+ ++failed;
+ res += "<font color=red>FAIL!&nbsp;&nbsp;</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&nbsp;&nbsp;</font>";
+ } else if (type == "qdebug") {
+ res += "<font color=#D1C005>QDEBUG&nbsp;</font>";
+ } else if (type == "warn") {
+ res += "<font color=#B3641A>WARNING</font>";
+ } else if (type == "qfatal") {
+ res += "<font color=red>QFATAL&nbsp;</font>";
+ } else if (type == "skip") {
+ ++skipped;
+ res += "SKIP&nbsp;&nbsp;&nbsp;";
+ }
+ 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));
+}
diff --git a/test.h b/test.h
new file mode 100644
index 0000000..ffcc170
--- /dev/null
+++ b/test.h
@@ -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