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 .DS_Store
hydra-runner.log hydra-runner.log
jhw-test jhw-test
.jhw-cache/

View File

@ -26,7 +26,8 @@ guard 'jasmine-headless-webkit', :all_on_start => false do
end end
def compile 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 end
compile compile

View File

@ -1,10 +1,54 @@
#include "ReportFileOutput.h" #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) { void ReportFileOutput::passed(const QString &specDetail) {
*outputIO << "PASS||" << qPrintable(specDetail) << std::endl; *outputIO << "PASS||" << qPrintable(specDetail) << std::endl;
successes.push(specDetail); 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 <QObject>
#include <iostream> #include <iostream>
#include <QStack> #include <QStack>
#include <sstream>
using namespace std;
class ReportFileOutput : public QObject { class ReportFileOutput : public QObject {
public: public:
@ -21,9 +24,14 @@ class ReportFileOutput : public QObject {
void reportSuccess(const QString &totalTests, const QString &failedTests, const QString &duration); void reportSuccess(const QString &totalTests, const QString &failedTests, const QString &duration);
void reportSuccessWithJSErrors(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> successes;
QStack<QString> failures; QStack<QString> failures;
private:
void reportTotals(const QString &totalTests, const QString &failedTests, const QString &duration, bool hasJavaScriptError);
}; };
#endif #endif

View File

@ -23,11 +23,66 @@ void ReportFileOutputTest::testFailed() {
ReportFileOutput output; ReportFileOutput output;
output.outputIO = &buffer; 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(buffer.str() == "FAIL||test||done||file.js:23\n");
QVERIFY(output.successes.size() == 0); QVERIFY(output.successes.size() == 0);
QVERIFY(output.failures.size() == 1); 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); QTEST_MAIN(ReportFileOutputTest);

View File

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

View File

@ -7,6 +7,8 @@
#include "Runner.h" #include "Runner.h"
using namespace std;
Runner::Runner() : QObject() Runner::Runner() : QObject()
, m_runs(0) , m_runs(0)
, hasErrors(false) , hasErrors(false)
@ -60,28 +62,27 @@
return !m_page.mainFrame()->findFirstElement(select).isNull(); return !m_page.mainFrame()->findFirstElement(select).isNull();
} }
void Runner::setColors(bool colors) void Runner::setColors(bool colors) {
{
consoleOutput.showColors = colors; consoleOutput.showColors = colors;
} }
void Runner::reportFile(const QString &file) void Runner::reportFile(const QString &file) {
{ reportFileName = file;
reportFilename = file;
} }
bool Runner::hasError() { bool Runner::hasError() {
return hasErrors; return hasErrors;
} }
void Runner::specPassed() void Runner::specPassed(const QString &specDetail) {
{ consoleOutput.passed(specDetail);
consoleOutput.passed(""); reportFileOutput.passed(specDetail);
} }
void Runner::specFailed(const QString &specDetail) void Runner::specFailed(const QString &specDetail) {
{ consoleOutput.failed(specDetail);
consoleOutput.failed(""); reportFileOutput.failed(specDetail);
didFail = true; didFail = true;
failedSpecs.push(specDetail); failedSpecs.push(specDetail);
} }
@ -89,6 +90,7 @@
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); consoleOutput.errorLog(msg, lineNumber, sourceID);
reportFileOutput.errorLog(msg, lineNumber, sourceID);
hasErrors = true; hasErrors = true;
m_runs = 0; m_runs = 0;
@ -97,12 +99,14 @@
void Runner::internalLog(const QString &note, const QString &msg) { void Runner::internalLog(const QString &note, const QString &msg) {
consoleOutput.internalLog(note, msg); consoleOutput.internalLog(note, msg);
reportFileOutput.internalLog(note, msg);
} }
void Runner::log(const QString &msg) void Runner::log(const QString &msg)
{ {
usedConsole = true; usedConsole = true;
consoleOutput.consoleLog(msg); consoleOutput.consoleLog(msg);
reportFileOutput.consoleLog(msg);
} }
void Runner::leavePageAttempt(const QString &msg) void Runner::leavePageAttempt(const QString &msg)
@ -126,32 +130,26 @@
{ {
if (didFail) { if (didFail) {
consoleOutput.reportFailure(total, failed, duration); consoleOutput.reportFailure(total, failed, duration);
reportFileOutput.reportFailure(total, failed, duration);
} else { } else {
if (hasErrors) { if (hasErrors) {
consoleOutput.reportSuccessWithJSErrors(total, failed, duration); consoleOutput.reportSuccessWithJSErrors(total, failed, duration);
reportFileOutput.reportSuccessWithJSErrors(total, failed, duration);
} else { } else {
consoleOutput.reportSuccess(total, failed, duration); consoleOutput.reportSuccess(total, failed, duration);
reportFileOutput.reportSuccess(total, failed, duration);
} }
} }
if (!reportFilename.isEmpty()) { if (!reportFileName.isEmpty()) {
QFile reportFH(reportFilename); QFile outputFile(reportFileName);
outputFile.open(QIODevice::WriteOnly);
if (reportFH.open(QFile::WriteOnly)) { QTextStream ts(&outputFile);
QTextStream report(&reportFH);
report << qPrintable(total) << "/" << qPrintable(failed) << "/";
report << (usedConsole ? "T" : "F");
report << "/" << qPrintable(duration) << "\n";
QString failedSpec; ts << reportFileOutput.outputIO->str().c_str();
while (!failedSpecs.isEmpty()) { outputFile.close();
failedSpec = failedSpecs.pop();
report << qPrintable(failedSpec) << "\n";
}
reportFH.close();
}
} }
isFinished = true; isFinished = true;

View File

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

View File

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

View File

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

View File

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

View File

@ -9,11 +9,8 @@ describe 'HeadlessReporterResult', ->
} }
} }
it 'should find the best spec lines', -> it 'should find the best spec lines', ->
result = new HeadlessReporterResult('test', [ 'name', 'of', 'test' ]) expect(HeadlessReporterResult.findSpecLine([ 'name', 'of', 'test' ]).lineNumber).toEqual(3)
expect(result._findSpecLine().lineNumber).toEqual(3) expect(HeadlessReporterResult.findSpecLine([ 'other', 'of', 'test' ]).lineNumber).toEqual(10)
result = new HeadlessReporterResult('test', [ 'other', 'of', 'test' ])
expect(result._findSpecLine().lineNumber).toEqual(10)
describe 'jasmine.HeadlessReporter', -> describe 'jasmine.HeadlessReporter', ->
reporter = null reporter = null
@ -36,3 +33,31 @@ describe 'jasmine.HeadlessReporter', ->
expect(spec.finish).toHaveBeenCalled() expect(spec.finish).toHaveBeenCalled()
expect(suite.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