/*************************************************************************** * Copyright (c) 2006 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include "FCGlobal.h" #include #include #include #include #include #include #include "UnitTestImp.h" #include "ui_UnitTest.h" using namespace TestGui; /* TRANSLATOR TestGui::UnitTestDialog */ UnitTestDialog* UnitTestDialog::_instance = nullptr; /** * Creates and returns the one and only instance of this dialog. */ UnitTestDialog* UnitTestDialog::instance() { // not initialized? if (!_instance) { _instance = new UnitTestDialog(Gui::getMainWindow()); } return _instance; } /** * Destructs the instance of this dialog. */ void UnitTestDialog::destruct() { if (_instance) { UnitTestDialog* pTmp = _instance; _instance = nullptr; delete pTmp; } } /** * Returns \a true if an instance of this dialog exists, \a false otherwise. */ bool UnitTestDialog::hasInstance() { return _instance != nullptr; } /** * Constructs a TestGui::UnitTestDialog as a child of 'parent', with the * name 'name' and widget flags set to 'f'. * * The dialog will by default be modeless, unless you set 'modal' to * true to construct a modal dialog. */ UnitTestDialog::UnitTestDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f) , ui(new Ui_UnitTest) { ui->setupUi(this); setupConnections(); setProgressColor(QColor(40, 210, 43)); // a darker green ui->progressBar->setAlignment(Qt::AlignCenter); // red items QPalette palette; palette.setColor(ui->treeViewFailure->foregroundRole(), Qt::red); ui->treeViewFailure->setPalette(palette); } /** * Destroys the object and frees any allocated resources */ UnitTestDialog::~UnitTestDialog() = default; void UnitTestDialog::setupConnections() { connect( ui->treeViewFailure, &QTreeWidget::itemDoubleClicked, this, &UnitTestDialog::onTreeViewFailureItemDoubleClicked ); connect(ui->helpButton, &QPushButton::clicked, this, &UnitTestDialog::onHelpButtonClicked); connect(ui->aboutButton, &QPushButton::clicked, this, &UnitTestDialog::onAboutButtonClicked); connect(ui->copyButton, &QPushButton::clicked, this, &UnitTestDialog::onCopyButtonClicked); connect(ui->startButton, &QPushButton::clicked, this, &UnitTestDialog::onStartButtonClicked); } /** * Sets the color to the progressbar to \a col. */ void UnitTestDialog::setProgressColor(const QColor& col) { QString qss = QStringLiteral( "QProgressBar {\n" " border: 2px solid grey;\n" " border-radius: 5px;\n" "}\n" "\n" "QProgressBar::chunk {\n" " background-color: %1;\n" "}" ) .arg(col.name()); ui->progressBar->setStyleSheet(qss); } /** * Opens a dialog to display a detailed description about the error. */ void UnitTestDialog::onTreeViewFailureItemDoubleClicked(QTreeWidgetItem* item, int column) { Q_UNUSED(column); QString text = item->data(0, Qt::UserRole).toString(); QMessageBox msgBox(this); msgBox.setIcon(QMessageBox::Information); msgBox.setWindowTitle(item->text(0)); msgBox.setDetailedText(text); // truncate the visible text when it's too long if (text.count(QLatin1Char('\n')) > 20) { QStringList lines = text.split(QLatin1Char('\n')); lines.erase(lines.begin() + 20, lines.end()); text = lines.join(QLatin1String("\n")); } if (text.size() > 1000) { text = text.left(1000); } msgBox.setText(text); msgBox.exec(); } /** * Shows the help dialog. */ void UnitTestDialog::onHelpButtonClicked() { QMessageBox::information( this, tr("Help"), tr("Enter the name of a callable object which, when called, will return a TestCase.\n" "Click 'start', and the test thus produced will be run.\n\n" "Double click on an error in the tree view to see more information about it, " "including the stack trace.") ); } /** * Shows the about dialog. */ void UnitTestDialog::onAboutButtonClicked() { QMessageBox::information( this, tr("About FreeCAD UnitTest"), tr("Copyright (c) Werner Mayer\n\n" "FreeCAD UnitTest is part of FreeCAD and supports writing Unit Tests for " "ones own modules.") ); } void UnitTestDialog::onCopyButtonClicked() { QString text; QTreeWidgetItemIterator it(ui->treeViewFailure); while (*it) { text += (*it)->data(0, Qt::UserRole).toString() + QStringLiteral("\n\n"); ++it; } if (text.isEmpty()) { return; } QApplication::clipboard()->setText(text); setStatusText(tr("Errors copied to clipboard")); } /** * Runs the unit tests. */ void UnitTestDialog::onStartButtonClicked() { reset(); setProgressColor(QColor(40, 210, 43)); // a darker green ui->startButton->setDisabled(true); try { Base::Interpreter().runString( "import qtunittest, gc\n" "__qt_test__=qtunittest.QtTestRunner(0,\"\")\n" "__qt_test__.runClicked()\n" "del __qt_test__\n" "gc.collect()\n" ); } catch (const Base::PyException& e) { std::string msg = e.what(); msg += "\n\n"; msg += e.getStackTrace(); showErrorDialog("Exception", msg.c_str()); } catch (const Base::Exception& e) { showErrorDialog("Exception", e.what()); } catch (const std::exception& e) { showErrorDialog("C++ standard exception", e.what()); } catch (...) { showErrorDialog("Unknown exception", "Unknown exception raised"); } ui->startButton->setEnabled(true); } /** * Shows an error dialog with \a title and \a message. */ void UnitTestDialog::showErrorDialog(const char* title, const char* message) { QMessageBox::critical(this, QString::fromLatin1(title), QString::fromLatin1(message)); } /** * Closes and resets this dialog. */ void UnitTestDialog::reject() { reset(); QDialog::reject(); } /** * Resets this dialog. */ void UnitTestDialog::reset() { ui->progressBar->reset(); ui->treeViewFailure->clear(); ui->textLabelRunCt->setText(QStringLiteral("0")); ui->textLabelFailCt->setText(QStringLiteral("0")); ui->textLabelErrCt->setText(QStringLiteral("0")); ui->textLabelRemCt->setText(QStringLiteral("0")); } /** * Adds a unit test. If a test with name \a unit already is added then nothing happens. */ void UnitTestDialog::addUnitTest(const QString& unit) { int ct = ui->comboTests->count(); for (int i = 0; i < ct; i++) { if (ui->comboTests->itemText(i) == unit) { return; } } ui->comboTests->addItem(unit); } /** * Sets the unit test. If the item is not there yet it gets added. */ void UnitTestDialog::setUnitTest(const QString& unit) { addUnitTest(unit); for (int i = 0; i < ui->comboTests->count(); i++) { if (ui->comboTests->itemText(i) == unit) { ui->comboTests->setCurrentIndex(i); break; } } } /** * Clears the unit tests. */ void UnitTestDialog::clearUnitTests() { ui->comboTests->clear(); } /** * Returns the unit test. */ QString UnitTestDialog::getUnitTest() const { return ui->comboTests->currentText(); } /** * Runs the currently selected test and closes the dialog afterwards. * It returns true if all tests have passed and false otherwise. */ bool UnitTestDialog::runCurrentTest() { clearErrorList(); onStartButtonClicked(); int count = ui->treeViewFailure->topLevelItemCount(); reject(); return (count == 0); } /** * Sets the text in the status bar. */ void UnitTestDialog::setStatusText(const QString& text) { QFontMetrics fm(font()); int labelWidth = ui->textLabelStatus->width() - 10; ui->textLabelStatus->setText(fm.elidedText(text, Qt::ElideMiddle, labelWidth)); } /** * Sets the progress of the progress bar whereas fraction is in between 0.0 and 1.0. * It also sets the color of the progress bar to red if a failure or error in the unit * test occurred. */ void UnitTestDialog::setProgressFraction(float fraction, const QString& color) { if (fraction == 0.0f) { ui->progressBar->setRange(0, 100); } else { if (color == QLatin1String("red")) { setProgressColor(Qt::red); } ui->progressBar->setValue((int)(100 * fraction)); } } /** * Empties the error listview. */ void UnitTestDialog::clearErrorList() { ui->treeViewFailure->clear(); } /** * Inserts an item with text \a failure to the listview. \a details will be shown * when double-clicking on the matching listitem. */ void UnitTestDialog::insertError(const QString& failure, const QString& details) { QTreeWidgetItem* item = new QTreeWidgetItem(ui->treeViewFailure); item->setText(0, failure); item->setForeground(0, Qt::red); item->setData(0, Qt::UserRole, QVariant(details)); } /** * Sets the number of performed tests to \a ct. */ void UnitTestDialog::setRunCount(int ct) { ui->textLabelRunCt->setText(QStringLiteral("%1").arg(ct)); } /** * Sets the number of failed tests to \a ct. */ void UnitTestDialog::setFailCount(int ct) { ui->textLabelFailCt->setText(QStringLiteral("%1").arg(ct)); } /** * Sets the number of faulty tests to \a ct. */ void UnitTestDialog::setErrorCount(int ct) { ui->textLabelErrCt->setText(QStringLiteral("%1").arg(ct)); } /** * Sets the number of remaining tests to \a ct. */ void UnitTestDialog::setRemainCount(int ct) { ui->textLabelRemCt->setText(QStringLiteral("%1").arg(ct)); } #include "moc_UnitTestImp.cpp"