I would suggest that our artifacts directory look something like this :<br><br><span style="font-family: courier new,monospace;"><project>/build-<label>/build.log</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> /build_status.<status><br> /sections/change_set.html<br> /.change_set.html<br> /test_results.html
<br> /something.html<br> /something else.txt<br> /unit test coverage/...<br> /other stuff.html<br><br></span>so, the only "special" files are
build.log, build_status.*, and the sections directory<br><br>anything else in the artifacts directory will be displayed as a link from the build page (maybe these should all live in a folder /links/?) - this is implemented now
<br><br>anything in /sections will be displayed as a collapsible section on the build page, if it's an html file, we include it outright, otherwise we either put it in a pre section or format it so that it looks as if we did.
<br><br>how's that sound?<br><br>Jeremy<br><br>PS. Thanks for the patch, Randall!<br><br style="font-family: courier new,monospace;"><div><span class="gmail_quote">On 2/10/07, <b class="gmail_sendername">Alexey Verkhovsky
</b> <<a href="mailto:averkhov@thoughtworks.com">averkhov@thoughtworks.com</a>> wrote:</span><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<br><font face="sans-serif" size="2">Thanks a lot!</font>
<span class="q"><br>
<br><tt><font size="2">> Is this the appropriate way to submit patches?</font></tt>
<br></span><font face="sans-serif" size="2">Yes, until we have a more appropriate
way (aka a bug tracker).</font>
<br>
<br><font face="sans-serif" size="2">This submission raises a question that
was at the back of my mind ever since we had Test::Unit results formatter
in this codebase.</font>
<br><font face="sans-serif" size="2">Custom formatting of build log is going
to be in demand. How do we pluginize it? </font>
<br>
<br><font face="sans-serif" size="2">How about this solution:</font>
<br><font face="sans-serif" size="2">* Let's make formatting of build log
a builder's responsibility.</font>
<br><font face="sans-serif" size="2">* This can be done by a builder plugin
in response to :build_finished event</font>
<br>
<br><font face="sans-serif" size="2">Another question then arises: what do
we want to do with intermediate build products? We don't really want to
delete them (troubleshooting), but we also don't want to display links
to them on the dashboard. I think, use hidden files for that. </font>
<br>
<br><font face="sans-serif" size="2">I.e., you start with build.log file.
Format it and write the output into build.log.html (without html and body
tags though, because we are displaying it on the dashboard page). It's
also a good time to merge it with the changeset etc. Finally, hide the
originals from the dashboard by renaming build.log to .build.log and changeset.log
to .changeset.log.</font>
<br>
<br><font face="sans-serif" size="2">Thoughts?</font>
<br>
<br><font face="sans-serif" size="2">Best regards,</font>
<br><font face="sans-serif" size="2">Alex</font>
<br>
<br>
<br>
<br>
<br>
<br>
<table width="100%">
<tbody><tr valign="top">
<td width="40%"><font face="sans-serif" size="1"><b>Randall Potter <<a href="mailto:rpotter@anl.gov" target="_blank" onclick="return top.js.OpenExtLink(window,event,this)">rpotter@anl.gov</a>></b>
</font>
<br><font face="sans-serif" size="1">Sent by: <a href="mailto:cruisecontrolrb-developers-bounces@rubyforge.org" target="_blank" onclick="return top.js.OpenExtLink(window,event,this)">cruisecontrolrb-developers-bounces@rubyforge.org
</a></font>
<p><font face="sans-serif" size="1">02/10/2007 12:23 AM</font>
</p></td><td width="59%">
<table width="100%">
<tbody><tr valign="top">
<td>
<div align="right"><font face="sans-serif" size="1">To</font></div>
</td><td><font face="sans-serif" size="1"><a href="mailto:cruisecontrolrb-developers@rubyforge.org" target="_blank" onclick="return top.js.OpenExtLink(window,event,this)">cruisecontrolrb-developers@rubyforge.org</a></font>
</td></tr><tr valign="top">
<td>
<div align="right"><font face="sans-serif" size="1">cc</font></div>
</td><td>
<br></td></tr><tr valign="top">
<td>
<div align="right"><font face="sans-serif" size="1">Subject</font></div>
</td><td><font face="sans-serif" size="1">[Cruisecontrolrb-developers] [patch]
rspec support (partial)</font></td></tr></tbody></table>
<br>
<table>
<tbody><tr valign="top">
<td>
<br></td><td><br></td></tr></tbody></table>
<br></td></tr></tbody></table><div><span class="e" id="q_110aca6f1796f164_3">
<br>
<br>
<br><tt><font size="2">story:<br>
As a Dev, I would like CC.rb to display rspec failures in
'Test <br>
Failures and Errors', so that I can use rspec.<br>
<br>
Is this the appropriate way to submit patches?<br>
<br>
Thank you in advance for your time,<br>
<br>
rpotter<br>
<br>
--- app/models/test_failure_parser.rb (revision 181)<br>
+++ app/models/test_failure_parser.rb (working copy)<br>
@@ -1,25 +1,51 @@<br>
class TestFailureParser <br>
- TEST_NAME_REGEX = /\S+/<br>
- MESSAGE_REGEX = /\]\:\n([\s\S]+)/<br>
- STACK_TRACE_REGEX = /\[([\s\S]*?)\]\:/<br>
- TEST_FAILURE_BLOCK_REGEX = /^\s+\d+\) Failure:\n([\S\s]*?)\n\n/<br>
+<br>
+ TEST_FAILURE_BLOCK_REGEX = /^\s+?\d+\)(( Failure:)?\n[\S\s]*?)\n\n/<br>
+ <br>
+ SPEC_FAILURE_FORMAT = /^'[\s\S]+'\sFAILED[\s\S]*?/<br>
+ TEST_UNIT_FAILURE_FORMAT = / <br>
Failure:\n(\S+)\n?\s+?\[([\s\S]*?)\]:\n([\s\S]+)/<br>
+ <br>
def get_test_failures(log)<br>
testFailures = Array.new<br>
<br>
log.gsub(TEST_FAILURE_BLOCK_REGEX) do |text|<br>
content = $1<br>
- <br>
- begin<br>
- test_name = content.match(TEST_NAME_REGEX).to_s
<br>
- message = content.match(MESSAGE_REGEX)[1]<br>
- stack_trace = content.match(STACK_TRACE_REGEX)[1]<br>
- <br>
- testFailures << TestErrorEntry.create_failure(test_name,
<br>
message, stack_trace)<br>
- rescue<br>
- # Do Nothing, Pattern does not match<br>
- end <br>
+ <br>
+ if content.match(TEST_UNIT_FAILURE_FORMAT)<br>
+ testFailures << parse_as_test_unit(content)<br>
+ elsif content.gsub("\n",'').match(SPEC_FAILURE_FORMAT)<br>
+ testFailures << parse_as_rspec(content)<br>
+ else<br>
+ # no failures or we don't recognize
it<br>
+ end<br>
+ <br>
end<br>
<br>
testFailures<br>
end<br>
-end<br>
\ No newline at end of file<br>
+ <br>
+ private<br>
+ <br>
+ # parse Test::Unit log message and return TestErrorEntry<br>
+ def parse_as_test_unit(content)<br>
+ <br>
+ test_name = content.match(TEST_UNIT_FAILURE_FORMAT)[1].to_s
<br>
+ message = content.match(TEST_UNIT_FAILURE_FORMAT)[3]<br>
+ stack_trace = content.match(TEST_UNIT_FAILURE_FORMAT)[2]<br>
+ <br>
+ TestErrorEntry.create_failure(test_name, message, stack_trace)<br>
+ end<br>
+ <br>
+ # parse rspec log message and return TestErrorEntry<br>
+ def parse_as_rspec(content)<br>
+ <br>
+ spec_arr = content.split("\n")<br>
+ <br>
+ test_name = spec_arr[1].match(/^'([\s\S]+)' FAILED$/)[1].to_s
<br>
+ message = spec_arr[2].to_s<br>
+ stack_trace = spec_arr.slice(3..spec_arr.length).join("\n")<br>
+ <br>
+ TestErrorEntry.create_failure(test_name, message, stack_trace)<br>
+ end<br>
+ <br>
+end<br>
<br>
--- test/unit/test_failure_parser_test.rb (revision 181)<br>
+++ working_copy/test/unit/test_failure_parser_test.rb (working
copy)<br>
@@ -77,6 +77,34 @@<br>
83 tests, 185 assertions, 2 failures, 0 errors<br>
EOF<br>
<br>
+LOG_OUTPUT_WITH_NO_SPEC_FAILURE = <<EOF<br>
+<br>
+.............................................................<br>
+<br>
+Finished in 1.199703 seconds<br>
+<br>
+61 specifications, 0 failures<br>
+EOF<br>
+<br>
+LOG_OUTPUT_WITH_SPEC_FAILURE = <<EOF<br>
+/usr/local/bin/spec:17:Warning: require_gem is obsolete. Use gem instead.<br>
+<br>
+....F....<br>
+<br>
+1)<br>
+'Given a request to edit with a week and year the controller should <br>
return a hash of one blank row if data ! found' FAILED<br>
+nil should be a kind of Array<br>
+./spec/controllers/consultant_controller_spec.rb:47:<br>
+/cruisecontrol/builds/ccs/work/config/../lib/tasks/cruise.rake:36:in `run'<br>
+/cruisecontrol/builds/ccs/work/config/../lib/tasks/cruise.rake:47:<br>
+/cruisecontrol/tasks/cc_build.rake:32:<br>
+-e:1:<br>
+<br>
+Finished in 0.484439 seconds<br>
+<br>
+9 specifications, 1 failure<br>
+EOF<br>
+<br>
def test_should_not_find_test_failures_with_a_build_with_test_errors<br>
testFailures = <br>
TestFailureParser.new.get_test_failures(LOG_OUTPUT_WITH_TEST_ERRORS)<br>
assert_equal 0, testFailures.length<br>
@@ -87,19 +115,30 @@<br>
assert_equal 0, testFailures.length
<br>
end<br>
<br>
+ def test_should_find_no_spec_failures_with_successful_build<br>
+ testFailures = <br>
TestFailureParser.new.get_test_failures(LOG_OUTPUT_WITH_NO_SPEC_FAILURE)<br>
+ assert_equal 0, testFailures.length
<br>
+ end<br>
+ <br>
def test_should_find_test_failures<br>
testFailures = <br>
TestFailureParser.new.get_test_failures(LOG_OUTPUT_WITH_TEST_FAILURE)<br>
assert_equal 2, testFailures.length<br>
assert_equal expected_first_test_failure, testFailures[0]<br>
assert_equal expected_second_test_failure, testFailures[1]<br>
end<br>
- <br>
+ <br>
+ def test_should_find_spec_failures<br>
+ testFailures = <br>
TestFailureParser.new.get_test_failures(LOG_OUTPUT_WITH_SPEC_FAILURE)<br>
+ assert_equal 1, testFailures.length<br>
+ assert_equal expected_first_spec_failure, testFailures[0]<br>
+ end<br>
+ <br>
def test_should_correctly_parse_mocha_test_failures<br>
testFailures = <br>
TestFailureParser.new.get_test_failures(LOG_OUTPUT_WITH_MOCK_TEST_FAILURE)<br>
assert_equal 1, testFailures.length<br>
assert_equal expected_mock_test_failure, testFailures[0]<br>
end<br>
- <br>
+ <br>
def expected_first_test_failure<br>
<br>
TestErrorEntry.create_failure("test_should_fail(SubversionLogParserTest)",<br>
"<1> expected
but was\n<\"abc\">.",<br>
@@ -121,5 +160,15 @@<br>
<br>
"#<Mocha::Mock:0x-245ec74a>.force_build_if_requested - expected
calls: <br>
1, actual calls: 2",<br>
<br>
"./test/unit/polling_scheduler_test.rb:44")<br>
end<br>
+<br>
+ def expected_first_spec_failure<br>
+ TestErrorEntry.create_failure("Given a request to edit
with a week <br>
and year the controller should return a hash of one blank row if data !
<br>
found",<br>
+
"nil should be a kind
of Array",<br>
+
<br>
"./spec/controllers/consultant_controller_spec.rb:47:\n" +<br>
+
<br>
"/cruisecontrol/builds/ccs/work/config/../lib/tasks/cruise.rake:36:in
<br>
`run'\n" +<br>
+
<br>
"/cruisecontrol/builds/ccs/work/config/../lib/tasks/cruise.rake:47:\n"
+<br>
+
<br>
"/cruisecontrol/tasks/cc_build.rake:32:\n" +<br>
+
"-e:1:")<br>
+ end<br>
<br>
end<br>
\ No newline at end of file<br>
<br>
<br>
_______________________________________________<br>
Cruisecontrolrb-developers mailing list<br>
<a href="mailto:Cruisecontrolrb-developers@rubyforge.org" target="_blank" onclick="return top.js.OpenExtLink(window,event,this)">Cruisecontrolrb-developers@rubyforge.org</a><br>
<a href="http://rubyforge.org/mailman/listinfo/cruisecontrolrb-developers" target="_blank" onclick="return top.js.OpenExtLink(window,event,this)">http://rubyforge.org/mailman/listinfo/cruisecontrolrb-developers</a><br>
</font></tt>
<br></span></div><br>_______________________________________________<br>Cruisecontrolrb-developers mailing list<br><a onclick="return top.js.OpenExtLink(window,event,this)" href="mailto:Cruisecontrolrb-developers@rubyforge.org">
Cruisecontrolrb-developers@rubyforge.org</a><br><a onclick="return top.js.OpenExtLink(window,event,this)" href="http://rubyforge.org/mailman/listinfo/cruisecontrolrb-developers" target="_blank">http://rubyforge.org/mailman/listinfo/cruisecontrolrb-developers
</a><br><br></blockquote></div><br>