halfway in the middle of the reporting change

This commit is contained in:
John Bintz 2011-09-02 15:52:19 -04:00
parent 68a2888d35
commit e5510f13d0
13 changed files with 398 additions and 219 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ moc_*.*
.DS_Store
hydra-runner.log
jhw-test
.jhw-cache/

View File

@ -26,7 +26,8 @@ guard 'jasmine-headless-webkit', :all_on_start => false do
end
def compile
system %{cd ext/jasmine-webkit-specrunner && ruby test.rb && ruby extconf.rb}
#system %{cd ext/jasmine-webkit-specrunner && ruby test.rb && ruby extconf.rb}
system %{cd ext/jasmine-webkit-specrunner && ruby extconf.rb}
end
compile

View File

@ -1,10 +1,54 @@
#include "ReportFileOutput.h"
ReportFileOutput::ReportFileOutput() : QObject() {
using namespace std;
ReportFileOutput::ReportFileOutput() : QObject() {
reset();
}
void ReportFileOutput::reset() {
buffer = new stringstream();
outputIO = buffer;
}
void ReportFileOutput::passed(const QString &specDetail) {
*outputIO << "PASS||" << qPrintable(specDetail) << std::endl;
successes.push(specDetail);
}
void ReportFileOutput::failed(const QString &specDetail) {
*outputIO << "FAIL||" << qPrintable(specDetail) << std::endl;
failures.push(specDetail);
}
void ReportFileOutput::errorLog(const QString &msg, int lineNumber, const QString &sourceID) {
*outputIO << "ERROR||" << qPrintable(msg) << "||" << qPrintable(sourceID) << ":" << lineNumber << std::endl;
}
void ReportFileOutput::consoleLog(const QString &msg) {
*outputIO << "CONSOLE||" << qPrintable(msg) << std::endl;
}
void ReportFileOutput::internalLog(const QString &, const QString &) {}
void ReportFileOutput::logSpecFilename(const QString &) {}
void ReportFileOutput::logSpecResult(const QString &) {}
void ReportFileOutput::reportFailure(const QString &totalTests, const QString &failedTests, const QString &duration) {
reportTotals(totalTests, failedTests, duration, false);
}
void ReportFileOutput::reportSuccess(const QString &totalTests, const QString &failedTests, const QString &duration) {
reportTotals(totalTests, failedTests, duration, false);
}
void ReportFileOutput::reportSuccessWithJSErrors(const QString &totalTests, const QString &failedTests, const QString &duration) {
reportTotals(totalTests, failedTests, duration, true);
}
void ReportFileOutput::reportTotals(const QString &totalTests, const QString &failedTests, const QString &duration, bool hasJavaScriptError) {
*outputIO << "TOTAL||" << qPrintable(totalTests) << "||" << qPrintable(failedTests) << "||" << qPrintable(duration) << "||";
*outputIO << (hasJavaScriptError ? "T" : "F");
*outputIO << std::endl;
}

View File

@ -4,6 +4,9 @@
#include <QObject>
#include <iostream>
#include <QStack>
#include <sstream>
using namespace std;
class ReportFileOutput : public QObject {
public:
@ -21,9 +24,14 @@ class ReportFileOutput : public QObject {
void reportSuccess(const QString &totalTests, const QString &failedTests, const QString &duration);
void reportSuccessWithJSErrors(const QString &totalTests, const QString &failedTests, const QString &duration);
std::ostream *outputIO;
void reset();
stringstream *buffer;
stringstream *outputIO;
QStack<QString> successes;
QStack<QString> failures;
private:
void reportTotals(const QString &totalTests, const QString &failedTests, const QString &duration, bool hasJavaScriptError);
};
#endif

View File

@ -23,11 +23,66 @@ void ReportFileOutputTest::testFailed() {
ReportFileOutput output;
output.outputIO = &buffer;
output.passed("test||done||file.js:23");
output.failed("test||done||file.js:23");
QVERIFY(buffer.str() == "FAIL||test||done||file.js:23\n");
QVERIFY(output.successes.size() == 0);
QVERIFY(output.failures.size() == 1);
}
void ReportFileOutputTest::testErrorLog() {
stringstream buffer;
ReportFileOutput output;
output.outputIO = &buffer;
output.errorLog("JS Error", 23, "file.js");
QVERIFY(buffer.str() == "ERROR||JS Error||file.js:23\n");
}
void ReportFileOutputTest::testConsoleLog() {
stringstream buffer;
ReportFileOutput output;
output.outputIO = &buffer;
output.consoleLog("Console");
QVERIFY(buffer.str() == "CONSOLE||Console\n");
}
void ReportFileOutputTest::testStubMethods() {
stringstream buffer;
ReportFileOutput output;
output.outputIO = &buffer;
output.internalLog("Internal", "Log");
output.logSpecFilename("Filename");
output.logSpecResult("REsult");
}
void ReportFileOutputTest::testReportFailure() {
stringstream buffer;
ReportFileOutput output;
output.outputIO = &buffer;
output.reportFailure("5", "2", "1.5");
QVERIFY(buffer.str() == "TOTAL||5||2||1.5||F\n");
}
void ReportFileOutputTest::testReportSuccess() {
stringstream buffer;
ReportFileOutput output;
output.outputIO = &buffer;
output.reportSuccess("5", "0", "1.5");
QVERIFY(buffer.str() == "TOTAL||5||0||1.5||F\n");
}
void ReportFileOutputTest::testReportSuccessWithJSErrors() {
stringstream buffer;
ReportFileOutput output;
output.outputIO = &buffer;
output.reportSuccessWithJSErrors("5", "0", "1.5");
QVERIFY(buffer.str() == "TOTAL||5||0||1.5||T\n");
}
QTEST_MAIN(ReportFileOutputTest);

View File

@ -15,6 +15,12 @@ class ReportFileOutputTest : public QObject {
private slots:
void testPassed();
void testFailed();
void testErrorLog();
void testConsoleLog();
void testStubMethods();
void testReportFailure();
void testReportSuccess();
void testReportSuccessWithJSErrors();
};
#endif

View File

@ -7,7 +7,9 @@
#include "Runner.h"
Runner::Runner() : QObject()
using namespace std;
Runner::Runner() : QObject()
, m_runs(0)
, hasErrors(false)
, usedConsole(false)
@ -18,32 +20,32 @@
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) {
void Runner::addFile(const QString &spec) {
runnerFiles.enqueue(spec);
}
}
void Runner::go()
{
void Runner::go()
{
m_ticker.stop();
m_page.setPreferredContentsSize(QSize(1024, 600));
addJHW();
loadSpec();
}
void Runner::addJHW()
{
}
void Runner::addJHW()
{
m_page.mainFrame()->addToJavaScriptWindowObject("JHW", this);
}
}
void Runner::loadSpec()
{
void Runner::loadSpec()
{
m_page.mainFrame()->load(runnerFiles.dequeue());
m_ticker.start(200, this);
}
}
void Runner::watch(bool ok)
{
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;
@ -53,112 +55,108 @@
}
m_ticker.start(200, this);
}
}
bool Runner::hasElement(const char *select)
{
bool Runner::hasElement(const char *select)
{
return !m_page.mainFrame()->findFirstElement(select).isNull();
}
}
void Runner::setColors(bool colors)
{
void Runner::setColors(bool colors) {
consoleOutput.showColors = colors;
}
}
void Runner::reportFile(const QString &file)
{
reportFilename = file;
}
void Runner::reportFile(const QString &file) {
reportFileName = file;
}
bool Runner::hasError() {
bool Runner::hasError() {
return hasErrors;
}
}
void Runner::specPassed()
{
consoleOutput.passed("");
}
void Runner::specPassed(const QString &specDetail) {
consoleOutput.passed(specDetail);
reportFileOutput.passed(specDetail);
}
void Runner::specFailed(const QString &specDetail) {
consoleOutput.failed(specDetail);
reportFileOutput.failed(specDetail);
void Runner::specFailed(const QString &specDetail)
{
consoleOutput.failed("");
didFail = true;
failedSpecs.push(specDetail);
}
}
void Runner::errorLog(const QString &msg, int lineNumber, const QString &sourceID)
{
void Runner::errorLog(const QString &msg, int lineNumber, const QString &sourceID)
{
consoleOutput.errorLog(msg, lineNumber, sourceID);
reportFileOutput.errorLog(msg, lineNumber, sourceID);
hasErrors = true;
m_runs = 0;
m_ticker.start(200, this);
}
}
void Runner::internalLog(const QString &note, const QString &msg) {
void Runner::internalLog(const QString &note, const QString &msg) {
consoleOutput.internalLog(note, msg);
}
reportFileOutput.internalLog(note, msg);
}
void Runner::log(const QString &msg)
{
void Runner::log(const QString &msg)
{
usedConsole = true;
consoleOutput.consoleLog(msg);
}
reportFileOutput.consoleLog(msg);
}
void Runner::leavePageAttempt(const QString &msg)
{
void Runner::leavePageAttempt(const QString &msg)
{
consoleOutput.internalLog("error", msg);
m_page.oneFalseConfirm();
hasErrors = true;
}
}
void Runner::printName(const QString &name)
{
void Runner::printName(const QString &name)
{
consoleOutput.logSpecFilename(name);
}
}
void Runner::printResult(const QString &result)
{
void Runner::printResult(const QString &result)
{
consoleOutput.logSpecResult(result);
}
}
void Runner::finishSuite(const QString &duration, const QString &total, const QString& failed)
{
void Runner::finishSuite(const QString &duration, const QString &total, const QString& failed)
{
if (didFail) {
consoleOutput.reportFailure(total, failed, duration);
reportFileOutput.reportFailure(total, failed, duration);
} else {
if (hasErrors) {
consoleOutput.reportSuccessWithJSErrors(total, failed, duration);
reportFileOutput.reportSuccessWithJSErrors(total, failed, duration);
} else {
consoleOutput.reportSuccess(total, failed, duration);
reportFileOutput.reportSuccess(total, failed, duration);
}
}
if (!reportFilename.isEmpty()) {
QFile reportFH(reportFilename);
if (!reportFileName.isEmpty()) {
QFile outputFile(reportFileName);
outputFile.open(QIODevice::WriteOnly);
if (reportFH.open(QFile::WriteOnly)) {
QTextStream report(&reportFH);
report << qPrintable(total) << "/" << qPrintable(failed) << "/";
report << (usedConsole ? "T" : "F");
report << "/" << qPrintable(duration) << "\n";
QTextStream ts(&outputFile);
QString failedSpec;
ts << reportFileOutput.outputIO->str().c_str();
while (!failedSpecs.isEmpty()) {
failedSpec = failedSpecs.pop();
report << qPrintable(failedSpec) << "\n";
}
reportFH.close();
}
outputFile.close();
}
isFinished = true;
}
}
void Runner::timerEvent(QTimerEvent *event)
{
void Runner::timerEvent(QTimerEvent *event)
{
++m_runs;
if (event->timerId() != m_ticker.timerId())
@ -199,5 +197,5 @@
std::cout << "WARNING: too many runs and the test is still not finished!" << std::endl;
QApplication::instance()->exit(1);
}
}
}

View File

@ -6,13 +6,16 @@
#include <QFile>
#include <QTextStream>
#include <iostream>
#include <fstream>
#include <QQueue>
#include "Page.h"
#include "ConsoleOutput.h"
#include "ReportFileOutput.h"
class Runner: public QObject {
using namespace std;
class Runner: public QObject {
Q_OBJECT
public:
Runner();
@ -24,7 +27,7 @@
void log(const QString &msg);
bool hasError();
void leavePageAttempt(const QString &msg);
void specPassed();
void specPassed(const QString &specDetail);
void specFailed(const QString &specDetail);
void printName(const QString &name);
void printResult(const QString &result);
@ -46,13 +49,14 @@
bool isFinished;
bool didFail;
QQueue<QString> runnerFiles;
QString reportFilename;
QStack<QString> failedSpecs;
ConsoleOutput consoleOutput;
ReportFileOutput reportFileOutput;
QString reportFileName;
void loadSpec();
};
};
#endif

View File

@ -56,11 +56,13 @@ int main(int argc, char** argv)
app.setApplicationName("jasmine-headless-webkit");
Runner runner;
runner.setColors(showColors);
runner.reportFile(reporter);
for (index = optind; index < argc; index++) {
runner.addFile(QString::fromLocal8Bit(argv[index]));
}
runner.go();
return app.exec();

View File

@ -8,19 +8,19 @@ class window.HeadlessReporterResult
@results.push(message)
print: ->
output = @name
bestChoice = this._findSpecLine()
bestChoice = HeadlessReporterResult.findSpecLine(@splitName)
output += " (#{bestChoice.file}:#{bestChoice.lineNumber})" if bestChoice.file
JHW.printName(output)
for result in @results
JHW.printResult(result)
_findSpecLine: ->
@findSpecLine: (splitName) ->
bestChoice = { accuracy: 0, file: null, lineNumber: null }
for file, lines of HeadlessReporterResult.specLineNumbers
index = 0
lineNumber = 0
while newLineNumberInfo = lines[@splitName[index]]
while newLineNumberInfo = lines[splitName[index]]
if newLineNumberInfo.length == 0
lineNumber = newLineNumberInfo[0]
else
@ -40,14 +40,20 @@ class window.HeadlessReporterResult
jasmine.Suite.prototype.getSuiteSplitName = ->
parts = if @parentSuite then @parentSuite.getSuiteSplitName() else []
parts.push(@description)
parts.push(String(@description).replace(/[\n\r]/g, ' '))
parts
jasmine.Spec.prototype.getSpecSplitName = ->
parts = @suite.getSuiteSplitName()
parts.push(@description)
parts.push(String(@description).replace(/[\n\r]/g, ' '))
parts
jasmine.Spec.prototype.getJHWSpecInformation = ->
parts = this.getSpecSplitName()
specLineInfo = HeadlessReporterResult.findSpecLine(parts)
parts.push("#{specLineInfo.file}:#{specLineInfo.lineNumber}")
parts.join("||")
class jasmine.HeadlessReporter
constructor: (@callback = null) ->
@results = []
@ -61,27 +67,31 @@ class jasmine.HeadlessReporter
this.callback() if @callback
JHW.finishSuite((new Date() - @startTime) / 1000.0, @length, @failedCount)
reportRunnerStarting: (runner) ->
@startTime = new Date()
reportSpecResults: (spec) ->
return if this.hasError()
results = spec.results()
@length++
if results.passed()
JHW.specPassed()
JHW.specPassed(spec.getJHWSpecInformation())
else
JHW.specFailed(spec.getSpecSplitName().join('||'))
JHW.specFailed(spec.getJHWSpecInformation())
@failedCount++
failureResult = new HeadlessReporterResult(spec.getFullName(), spec.getSpecSplitName())
for result in results.getItems()
if result.type == 'expect' and !result.passed_
failureResult.addResult(result.message)
@results.push(failureResult)
reportSpecStarting: (spec) ->
if this.hasError()
spec.finish()
spec.suite.finish()
reportSuiteResults: (suite) ->
hasError: ->
JHW.hasError()

View File

@ -14,7 +14,7 @@
HeadlessReporterResult.prototype.print = function() {
var bestChoice, output, result, _i, _len, _ref, _results;
output = this.name;
bestChoice = this._findSpecLine();
bestChoice = HeadlessReporterResult.findSpecLine(this.splitName);
if (bestChoice.file) {
output += " (" + bestChoice.file + ":" + bestChoice.lineNumber + ")";
}
@ -27,7 +27,7 @@
}
return _results;
};
HeadlessReporterResult.prototype._findSpecLine = function() {
HeadlessReporterResult.findSpecLine = function(splitName) {
var bestChoice, file, index, lastLine, line, lineNumber, lines, newLineNumberInfo, _i, _len, _ref;
bestChoice = {
accuracy: 0,
@ -39,7 +39,7 @@
lines = _ref[file];
index = 0;
lineNumber = 0;
while (newLineNumberInfo = lines[this.splitName[index]]) {
while (newLineNumberInfo = lines[splitName[index]]) {
if (newLineNumberInfo.length === 0) {
lineNumber = newLineNumberInfo[0];
} else {
@ -70,15 +70,22 @@
jasmine.Suite.prototype.getSuiteSplitName = function() {
var parts;
parts = this.parentSuite ? this.parentSuite.getSuiteSplitName() : [];
parts.push(this.description);
parts.push(String(this.description).replace(/[\n\r]/g, ' '));
return parts;
};
jasmine.Spec.prototype.getSpecSplitName = function() {
var parts;
parts = this.suite.getSuiteSplitName();
parts.push(this.description);
parts.push(String(this.description).replace(/[\n\r]/g, ' '));
return parts;
};
jasmine.Spec.prototype.getJHWSpecInformation = function() {
var parts, specLineInfo;
parts = this.getSpecSplitName();
specLineInfo = HeadlessReporterResult.findSpecLine(parts);
parts.push("" + specLineInfo.file + ":" + specLineInfo.lineNumber);
return parts.join("||");
};
jasmine.HeadlessReporter = (function() {
function HeadlessReporter(callback) {
this.callback = callback != null ? callback : null;
@ -112,9 +119,9 @@
results = spec.results();
this.length++;
if (results.passed()) {
return JHW.specPassed();
return JHW.specPassed(spec.getJHWSpecInformation());
} else {
JHW.specFailed(spec.getSpecSplitName().join('||'));
JHW.specFailed(spec.getJHWSpecInformation());
this.failedCount++;
failureResult = new HeadlessReporterResult(spec.getFullName(), spec.getSpecSplitName());
_ref = results.getItems();

View File

@ -9,11 +9,8 @@ describe 'HeadlessReporterResult', ->
}
}
it 'should find the best spec lines', ->
result = new HeadlessReporterResult('test', [ 'name', 'of', 'test' ])
expect(result._findSpecLine().lineNumber).toEqual(3)
result = new HeadlessReporterResult('test', [ 'other', 'of', 'test' ])
expect(result._findSpecLine().lineNumber).toEqual(10)
expect(HeadlessReporterResult.findSpecLine([ 'name', 'of', 'test' ]).lineNumber).toEqual(3)
expect(HeadlessReporterResult.findSpecLine([ 'other', 'of', 'test' ]).lineNumber).toEqual(10)
describe 'jasmine.HeadlessReporter', ->
reporter = null
@ -36,3 +33,31 @@ describe 'jasmine.HeadlessReporter', ->
expect(spec.finish).toHaveBeenCalled()
expect(suite.finish).toHaveBeenCalled()
describe 'jasmine.Suite.prototype.getSuiteSplitName', ->
it 'should flatten the description', ->
suite = new jasmine.Suite({});
suite.description = "hello\ngoodbye\n";
expect(suite.getSuiteSplitName()).toEqual([ "hello goodbye " ])
it 'should not fail on missing description', ->
suite = new jasmine.Suite({});
suite.description = 1;
expect(suite.getSuiteSplitName()).toEqual([ "1" ])
describe 'jasmine.Spec.prototype.getSuiteSplitName', ->
it 'should flatten the description', ->
spec = new jasmine.Spec({}, {});
spec.suite = {
getSuiteSplitName: -> []
}
spec.description = "hello\ngoodbye\n";
expect(spec.getSpecSplitName()).toEqual([ "hello goodbye " ])
it 'should not fail on missing description', ->
spec = new jasmine.Spec({}, {});
spec.suite = {
getSuiteSplitName: -> []
}
spec.description = 1
expect(spec.getSpecSplitName()).toEqual([ "1" ])

View File

@ -0,0 +1,18 @@
require 'spec_helper'
describe Jasmine::Headless::Report do
include FakeFS::SpecHelpers
describe '.load' do
context 'no file' do
it 'should raise an exception' do
expect { described_class.load(file) }.to raise_error(Errno::ENOENT)
end
end
context 'file' do
end
end
end