start c++ cleanup

This commit is contained in:
John Bintz 2011-08-01 13:09:08 -04:00
parent 316383671a
commit 60713fc88f
11 changed files with 436 additions and 410 deletions

3
.gitignore vendored
View File

@ -6,3 +6,6 @@ Makefile
specrunner.moc
specrunner.o
ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner
*.o
moc_*.*

View File

@ -4,7 +4,7 @@
#
guard 'shell' do
watch(%r{ext/jasmine-webkit-specrunner/specrunner.cpp}) { compile }
watch(%r{ext/jasmine-webkit-specrunner/.*\.(cpp|h)}) { compile }
end
# A sample Guardfile
# More info at https://github.com/guard/guard#readme

View File

@ -0,0 +1,30 @@
#include <QtGui>
#include <QtWebKit>
#include "Page.h"
namespace HeadlessSpecRunner {
Page::Page() : QWebPage(), confirmResult(true) {}
void Page::javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID) {
emit consoleLog(message, lineNumber, sourceID);
}
bool Page::javaScriptConfirm(QWebFrame *frame, const QString &msg) {
if (confirmResult) {
emit internalLog("TODO", "jasmine-headless-webkit can't handle confirm() yet! You should mock window.confirm for now. Returning true.");
return true;
} else {
confirmResult = true;
return false;
}
}
void Page::javaScriptAlert(QWebFrame *frame, const QString &msg) {
emit internalLog("alert", msg);
}
void Page::oneFalseConfirm() {
confirmResult = false;
}
}

View File

@ -0,0 +1,25 @@
#ifndef JHW_PAGE
#define JHW_PAGE
#include <QtGui>
#include <QtWebKit>
namespace HeadlessSpecRunner {
class Page: public QWebPage {
Q_OBJECT
public:
Page();
void oneFalseConfirm();
signals:
void consoleLog(const QString &msg, int lineNumber, const QString &sourceID);
void internalLog(const QString &note, const QString &msg);
protected:
void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID);
bool javaScriptConfirm(QWebFrame *frame, const QString &msg);
void javaScriptAlert(QWebFrame *frame, const QString &msg);
private:
bool confirmResult;
};
}
#endif

View File

@ -0,0 +1,266 @@
#include <QtGui>
#include <QtWebKit>
#include <QFile>
#include <QTextStream>
#include <iostream>
#include <QQueue>
#include "Runner.h"
namespace HeadlessSpecRunner {
Runner::Runner() : QObject()
, m_runs(0)
, hasErrors(false)
, usedConsole(false)
, showColors(false)
, isFinished(false)
, didFail(false)
, consoleNotUsedThisRun(false) {
m_page.settings()->enablePersistentStorage();
connect(&m_page, SIGNAL(loadFinished(bool)), this, SLOT(watch(bool)));
connect(&m_page, SIGNAL(consoleLog(QString, int, QString)), this, SLOT(errorLog(QString, int, QString)));
connect(&m_page, SIGNAL(internalLog(QString, QString)), this, SLOT(internalLog(QString, QString)));
connect(m_page.mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(addJHW()));
}
void Runner::addFile(const QString &spec) {
runnerFiles.enqueue(spec);
}
void Runner::go()
{
m_ticker.stop();
m_page.setPreferredContentsSize(QSize(1024, 600));
addJHW();
loadSpec();
}
void Runner::addJHW()
{
m_page.mainFrame()->addToJavaScriptWindowObject("JHW", this);
}
void Runner::loadSpec()
{
m_page.mainFrame()->load(runnerFiles.dequeue());
m_ticker.start(200, this);
}
void Runner::watch(bool ok)
{
if (!ok) {
std::cerr << "Can't load " << qPrintable(m_page.mainFrame()->url().toString()) << ", the file may be broken." << std::endl;
std::cerr << "Out of curiosity, did your tests try to submit a form and you haven't prevented that?" << std::endl;
std::cerr << "Try running your tests in your browser with the Jasmine server and see what happens." << std::endl;
QApplication::instance()->exit(1);
return;
}
m_ticker.start(200, this);
}
bool Runner::hasElement(const char *select)
{
return !m_page.mainFrame()->findFirstElement(select).isNull();
}
void Runner::setColors(bool colors)
{
showColors = colors;
}
void Runner::reportFile(const QString &file)
{
reportFilename = file;
}
void Runner::red()
{
if (showColors) std::cout << "\033[0;31m";
}
void Runner::green()
{
if (showColors) std::cout << "\033[0;32m";
}
void Runner::yellow()
{
if (showColors) std::cout << "\033[0;33m";
}
void Runner::clear()
{
if (showColors) std::cout << "\033[m";
}
void Runner::specPassed()
{
consoleNotUsedThisRun = true;
green();
std::cout << '.';
clear();
fflush(stdout);
}
void Runner::specFailed(const QString &specDetail)
{
consoleNotUsedThisRun = true;
didFail = true;
red();
std::cout << 'F';
failedSpecs.push(specDetail);
clear();
fflush(stdout);
}
void Runner::errorLog(const QString &msg, int lineNumber, const QString &sourceID)
{
red();
std::cout << "[error] ";
clear();
std::cout << qPrintable(sourceID) << ":" << lineNumber << " : " << qPrintable(msg);
std::cout << std::endl;
hasErrors = true;
m_runs = 0;
m_ticker.start(200, this);
}
void Runner::internalLog(const QString &note, const QString &msg) {
red();
std::cout << "[" << qPrintable(note) << "] ";
clear();
std::cout << qPrintable(msg);
std::cout << std::endl;
}
void Runner::log(const QString &msg)
{
usedConsole = true;
green();
if (consoleNotUsedThisRun) {
std::cout << std::endl;
consoleNotUsedThisRun = false;
}
std::cout << "[console] ";
clear();
if (msg.contains("\n"))
std::cout << std::endl;
std::cout << qPrintable(msg);
std::cout << std::endl;
}
void Runner::leavePageAttempt(const QString &msg)
{
red();
std::cout << "[error] ";
clear();
std::cout << qPrintable(msg) << std::endl;
m_page.oneFalseConfirm();
hasErrors = true;
}
void Runner::printName(const QString &name)
{
std::cout << std::endl << std::endl;
red();
std::cout << qPrintable(name) << std::endl;
clear();
}
void Runner::printResult(const QString &result)
{
red();
std::cout << " " << qPrintable(result) << std::endl;
clear();
}
void Runner::finishSuite(const QString &duration, const QString &total, const QString& failed)
{
std::cout << std::endl;
if (didFail) {
red();
std::cout << "FAIL: ";
} else {
green();
std::cout << "PASS";
if (hasErrors) {
std::cout << " with JS errors";
}
std::cout << ": ";
}
std::cout << qPrintable(total) << " tests, " << qPrintable(failed) << " failures, " << qPrintable(duration) << " secs.";
clear();
std::cout << std::endl;
if (!reportFilename.isEmpty()) {
QFile reportFH(reportFilename);
if (reportFH.open(QFile::WriteOnly)) {
QTextStream report(&reportFH);
report << qPrintable(total) << "/" << qPrintable(failed) << "/";
report << (usedConsole ? "T" : "F");
report << "/" << qPrintable(duration) << "\n";
QString failedSpec;
while (!failedSpecs.isEmpty()) {
failedSpec = failedSpecs.pop();
report << qPrintable(failedSpec) << "\n";
}
reportFH.close();
}
}
isFinished = true;
}
void Runner::timerEvent(QTimerEvent *event)
{
++m_runs;
if (event->timerId() != m_ticker.timerId())
return;
if (hasErrors && m_runs > 2)
QApplication::instance()->exit(1);
if (isFinished) {
int exitCode = 0;
if (didFail || hasErrors) {
exitCode = 1;
} else {
if (usedConsole) {
exitCode = 2;
}
}
bool runAgain = true;
if (runnerFiles.count() == 0) {
runAgain = false;
} else {
if (exitCode == 1) {
runAgain = false;
}
}
if (runAgain) {
isFinished = false;
loadSpec();
} else {
QApplication::instance()->exit(exitCode);
}
}
if (m_runs > 30) {
std::cout << "WARNING: too many runs and the test is still not finished!" << std::endl;
QApplication::instance()->exit(1);
}
}
}

View File

@ -0,0 +1,60 @@
#ifndef JHW_RUNNER
#define JHW_RUNNER
#include <QtGui>
#include <QtWebKit>
#include <QFile>
#include <QTextStream>
#include <iostream>
#include <QQueue>
#include "Page.h"
namespace HeadlessSpecRunner {
class Runner: public QObject {
Q_OBJECT
public:
Runner();
void setColors(bool colors);
void reportFile(const QString &file);
void addFile(const QString &spec);
void go();
public slots:
void log(const QString &msg);
void leavePageAttempt(const QString &msg);
void specPassed();
void specFailed(const QString &specDetail);
void printName(const QString &name);
void printResult(const QString &result);
void finishSuite(const QString &duration, const QString &total, const QString& failed);
private slots:
void watch(bool ok);
void errorLog(const QString &msg, int lineNumber, const QString &sourceID);
void internalLog(const QString &note, const QString &msg);
void addJHW();
protected:
bool hasElement(const char *select);
void timerEvent(QTimerEvent *event);
private:
HeadlessSpecRunner::Page m_page;
QBasicTimer m_ticker;
int m_runs;
bool hasErrors;
bool usedConsole;
bool showColors;
bool isFinished;
bool didFail;
bool consoleNotUsedThisRun;
QQueue<QString> runnerFiles;
QString reportFilename;
QStack<QString> failedSpecs;
void red();
void green();
void yellow();
void clear();
void loadSpec();
};
}
#endif

View File

@ -1,3 +1,5 @@
require 'fileutils'
$: << File.expand_path("../../../lib", __FILE__)
require 'qt/qmake'

View File

@ -21,373 +21,13 @@
THE SOFTWARE.
*/
#include <QtGui>
#include <QtWebKit>
#include <QFile>
#include <QTextStream>
#include <iostream>
#include <QQueue>
#include "HeadlessSpecRunner/Page.h"
#include "HeadlessSpecRunner/Runner.h"
#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
#error Use Qt 4.7 or later version
#endif
class HeadlessSpecRunnerPage: public QWebPage
{
Q_OBJECT
public:
HeadlessSpecRunnerPage();
void oneFalseConfirm();
signals:
void consoleLog(const QString &msg, int lineNumber, const QString &sourceID);
void internalLog(const QString &note, const QString &msg);
protected:
void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID);
bool javaScriptConfirm(QWebFrame *frame, const QString &msg);
void javaScriptAlert(QWebFrame *frame, const QString &msg);
private:
bool confirmResult;
};
HeadlessSpecRunnerPage::HeadlessSpecRunnerPage()
: QWebPage()
, confirmResult(true)
{
}
void HeadlessSpecRunnerPage::javaScriptConsoleMessage(const QString &message, int lineNumber, const QString &sourceID)
{
emit consoleLog(message, lineNumber, sourceID);
}
bool HeadlessSpecRunnerPage::javaScriptConfirm(QWebFrame *frame, const QString &msg)
{
if (confirmResult) {
emit internalLog("TODO", "jasmine-headless-webkit can't handle confirm() yet! You should mock window.confirm for now. Returning true.");
return true;
} else {
confirmResult = true;
return false;
}
}
void HeadlessSpecRunnerPage::javaScriptAlert(QWebFrame *frame, const QString &msg)
{
emit internalLog("alert", msg);
}
void HeadlessSpecRunnerPage::oneFalseConfirm()
{
confirmResult = false;
}
class HeadlessSpecRunner: public QObject
{
Q_OBJECT
public:
HeadlessSpecRunner();
void setColors(bool colors);
void reportFile(const QString &file);
void addFile(const QString &spec);
void go();
public slots:
void log(const QString &msg);
void leavePageAttempt(const QString &msg);
void specPassed();
void specFailed(const QString &specDetail);
void printName(const QString &name);
void printResult(const QString &result);
void finishSuite(const QString &duration, const QString &total, const QString& failed);
private slots:
void watch(bool ok);
void errorLog(const QString &msg, int lineNumber, const QString &sourceID);
void internalLog(const QString &note, const QString &msg);
void addJHW();
protected:
bool hasElement(const char *select);
void timerEvent(QTimerEvent *event);
private:
HeadlessSpecRunnerPage m_page;
QBasicTimer m_ticker;
int m_runs;
bool hasErrors;
bool usedConsole;
bool showColors;
bool isFinished;
bool didFail;
bool consoleNotUsedThisRun;
QQueue<QString> runnerFiles;
QString reportFilename;
QStack<QString> failedSpecs;
void red();
void green();
void yellow();
void clear();
void loadSpec();
};
HeadlessSpecRunner::HeadlessSpecRunner()
: QObject()
, m_runs(0)
, hasErrors(false)
, usedConsole(false)
, showColors(false)
, isFinished(false)
, didFail(false)
, consoleNotUsedThisRun(false)
{
m_page.settings()->enablePersistentStorage();
connect(&m_page, SIGNAL(loadFinished(bool)), this, SLOT(watch(bool)));
connect(&m_page, SIGNAL(consoleLog(QString, int, QString)), this, SLOT(errorLog(QString, int, QString)));
connect(&m_page, SIGNAL(internalLog(QString, QString)), this, SLOT(internalLog(QString, QString)));
connect(m_page.mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(addJHW()));
}
void HeadlessSpecRunner::addFile(const QString &spec)
{
runnerFiles.enqueue(spec);
}
void HeadlessSpecRunner::go()
{
m_ticker.stop();
m_page.setPreferredContentsSize(QSize(1024, 600));
addJHW();
loadSpec();
}
void HeadlessSpecRunner::addJHW()
{
m_page.mainFrame()->addToJavaScriptWindowObject("JHW", this);
}
void HeadlessSpecRunner::loadSpec()
{
m_page.mainFrame()->load(runnerFiles.dequeue());
m_ticker.start(200, this);
}
void HeadlessSpecRunner::watch(bool ok)
{
if (!ok) {
std::cerr << "Can't load " << qPrintable(m_page.mainFrame()->url().toString()) << ", the file may be broken." << std::endl;
std::cerr << "Out of curiosity, did your tests try to submit a form and you haven't prevented that?" << std::endl;
std::cerr << "Try running your tests in your browser with the Jasmine server and see what happens." << std::endl;
QApplication::instance()->exit(1);
return;
}
m_ticker.start(200, this);
}
bool HeadlessSpecRunner::hasElement(const char *select)
{
return !m_page.mainFrame()->findFirstElement(select).isNull();
}
void HeadlessSpecRunner::setColors(bool colors)
{
showColors = colors;
}
void HeadlessSpecRunner::reportFile(const QString &file)
{
reportFilename = file;
}
void HeadlessSpecRunner::red()
{
if (showColors) std::cout << "\033[0;31m";
}
void HeadlessSpecRunner::green()
{
if (showColors) std::cout << "\033[0;32m";
}
void HeadlessSpecRunner::yellow()
{
if (showColors) std::cout << "\033[0;33m";
}
void HeadlessSpecRunner::clear()
{
if (showColors) std::cout << "\033[m";
}
void HeadlessSpecRunner::specPassed()
{
consoleNotUsedThisRun = true;
green();
std::cout << '.';
clear();
fflush(stdout);
}
void HeadlessSpecRunner::specFailed(const QString &specDetail)
{
consoleNotUsedThisRun = true;
didFail = true;
red();
std::cout << 'F';
failedSpecs.push(specDetail);
clear();
fflush(stdout);
}
void HeadlessSpecRunner::errorLog(const QString &msg, int lineNumber, const QString &sourceID)
{
red();
std::cout << "[error] ";
clear();
std::cout << qPrintable(sourceID) << ":" << lineNumber << " : " << qPrintable(msg);
std::cout << std::endl;
hasErrors = true;
m_runs = 0;
m_ticker.start(200, this);
}
void HeadlessSpecRunner::internalLog(const QString &note, const QString &msg) {
red();
std::cout << "[" << qPrintable(note) << "] ";
clear();
std::cout << qPrintable(msg);
std::cout << std::endl;
}
void HeadlessSpecRunner::log(const QString &msg)
{
usedConsole = true;
green();
if (consoleNotUsedThisRun) {
std::cout << std::endl;
consoleNotUsedThisRun = false;
}
std::cout << "[console] ";
clear();
if (msg.contains("\n"))
std::cout << std::endl;
std::cout << qPrintable(msg);
std::cout << std::endl;
}
void HeadlessSpecRunner::leavePageAttempt(const QString &msg)
{
red();
std::cout << "[error] ";
clear();
std::cout << qPrintable(msg) << std::endl;
m_page.oneFalseConfirm();
hasErrors = true;
}
void HeadlessSpecRunner::printName(const QString &name)
{
std::cout << std::endl << std::endl;
red();
std::cout << qPrintable(name) << std::endl;
clear();
}
void HeadlessSpecRunner::printResult(const QString &result)
{
red();
std::cout << " " << qPrintable(result) << std::endl;
clear();
}
void HeadlessSpecRunner::finishSuite(const QString &duration, const QString &total, const QString& failed)
{
std::cout << std::endl;
if (didFail) {
red();
std::cout << "FAIL: ";
} else {
green();
std::cout << "PASS";
if (hasErrors) {
std::cout << " with JS errors";
}
std::cout << ": ";
}
std::cout << qPrintable(total) << " tests, " << qPrintable(failed) << " failures, " << qPrintable(duration) << " secs.";
clear();
std::cout << std::endl;
if (!reportFilename.isEmpty()) {
QFile reportFH(reportFilename);
if (reportFH.open(QFile::WriteOnly)) {
QTextStream report(&reportFH);
report << qPrintable(total) << "/" << qPrintable(failed) << "/";
report << (usedConsole ? "T" : "F");
report << "/" << qPrintable(duration) << "\n";
QString failedSpec;
while (!failedSpecs.isEmpty()) {
failedSpec = failedSpecs.pop();
report << qPrintable(failedSpec) << "\n";
}
reportFH.close();
}
}
isFinished = true;
}
void HeadlessSpecRunner::timerEvent(QTimerEvent *event)
{
++m_runs;
if (event->timerId() != m_ticker.timerId())
return;
if (hasErrors && m_runs > 2)
QApplication::instance()->exit(1);
if (isFinished) {
int exitCode = 0;
if (didFail || hasErrors) {
exitCode = 1;
} else {
if (usedConsole) {
exitCode = 2;
}
}
bool runAgain = true;
if (runnerFiles.count() == 0) {
runAgain = false;
} else {
if (exitCode == 1) {
runAgain = false;
}
}
if (runAgain) {
isFinished = false;
loadSpec();
} else {
QApplication::instance()->exit(exitCode);
}
}
if (m_runs > 30) {
std::cout << "WARNING: too many runs and the test is still not finished!" << std::endl;
QApplication::instance()->exit(1);
}
}
#include "specrunner.moc"
int main(int argc, char** argv)
{
char *reporter = NULL;
@ -414,7 +54,7 @@ int main(int argc, char** argv)
QApplication app(argc, argv);
app.setApplicationName("jasmine-headless-webkit");
HeadlessSpecRunner runner;
HeadlessSpecRunner::Runner runner;
runner.setColors(showColors);
runner.reportFile(reporter);
@ -426,4 +66,3 @@ int main(int argc, char** argv)
return app.exec();
}

View File

@ -1,7 +1,8 @@
TEMPLATE = app
CONFIG -= app_bundle
TARGET = jasmine-webkit-specrunner
SOURCES = specrunner.cpp
SOURCES = HeadlessSpecRunner/Page.cpp HeadlessSpecRunner/Runner.cpp specrunner.cpp
HEADERS = HeadlessSpecRunner/Page.h HeadlessSpecRunner/Runner.h
QT += network webkit
QMAKE_INFO_PLIST = Info.plist
QMAKESPEC = macx-gcc