blob: 6980fb1f104ede1967dd62914f4c1b9ad6f2ed8b [file] [log] [blame]
require 'socket'
begin
# ssanders: Try to load RSpec based on the LOADPATH, this allows
# projects (e.g. Rails) to provide alternate versions via the buildpath
require 'spec'
rescue LoadError
# ssanders: Fallback to loading from the Gem
require 'rubygems'
gem 'rspec'
require 'spec'
end
require 'spec/runner/formatter/base_formatter'
module Spec
module Example
class ExampleGroup
alias_method :initialize_old, :initialize
def initialize(*args, &block)
result = initialize_old(*args, &block)
# ssanders: Override for "pending" examples
unless block
@from = caller
while !@from.empty? && /.*`it'/ !~ @from.first
@from.shift
end
@from.shift
end
result
end
def implementation_backtrace
if @from
@from
else
super
end
end
end
module ExampleMethods
IN_METHOD_RE = /^(.+):in `(.+)'$/
def rspecTestName
if @DLTK_backtrace.nil?
# tgrimm: This a workaround for a bug in 1.1.12:
filtered_backtrace = respond_to?(:backtrace) ? backtrace : implementation_backtrace
filtered_backtrace = filtered_backtrace.reject { |bt| bt =~ /(example_group_methods|dltk-rspec-runner.rb)/ }
@DLTK_backtrace = filtered_backtrace[0]
if @DLTK_backtrace =~ IN_METHOD_RE
@DLTK_backtrace = $1
end
end
description + '<' + @DLTK_backtrace
end
end
end
end
module Spec
module Example
module ExampleGroupMethods
def DLTK_examples_to_run
examples_to_run
end
end
end
end
unless ::Spec::VERSION::MAJOR > 1 || ::Spec::VERSION::MINOR > 0
module Spec
module DSL
class Example
alias_method :initialize_old, :initialize
def initialize(*args, &block)
result = initialize_old(*args, &block)
@from = caller(0)[3]
Description.description.examples << self
result
end
IN_METHOD_RE = /^(.+):in `(.+)'$/
def rspecTestName
if @DLTK_backtrace.nil?
backtrace = @from
if backtrace =~ IN_METHOD_RE
backtrace = $1
end
@DLTK_backtrace = backtrace
end
description + '<' + @DLTK_backtrace
end
end
end
end
module Spec
module DSL
class Description
def self.description
@@description
end
alias_method :initialize_old, :initialize
def initialize(*args, &block)
result = initialize_old(*args, &block)
@@description = self
result
end
def description_text
description
end
def examples
@examples ||= []
end
end
end
end
end
module DLTK
module RSpec
module EnvVars
# environment variable name to pass communication port number
# to the launched script
PORT = "RUBY_TESTING_PORT"
PATH = "RUBY_TESTING_PATH"
end # of EnvVars
module MessageIds
# Notification that a test run has started.
# MessageIds.TEST_RUN_START + testCount.toString + " " + version
TEST_RUN_START = "%TESTC "
# Notification that a test run has ended.
# TEST_RUN_END + elapsedTime.toString().
TEST_RUN_END = "%RUNTIME"
# Notification about a test inside the test suite.
# TEST_TREE + testId + "," + testName + "," + isSuite + "," + testcount
# isSuite = "true" or "false"
TEST_TREE = "%TSTTREE"
#Notification that a test has started.
# MessageIds.TEST_START + testID + "," + testName
TEST_START = "%TESTS "
# Notification that a test has ended.
# TEST_END + testID + "," + testName
TEST_END = "%TESTE "
# Notification that a test had a error.
# TEST_ERROR + testID + "," + testName.
# After the notification follows the stack trace.
TEST_ERROR = "%ERROR "
# Notification that a test had a failure.
# TEST_FAILED + testID + "," + testName.
# After the notification follows the stack trace.
TEST_FAILED = "%FAILED "
# Notification that a test trace has started.
# The end of the trace is signaled by a TRACE_END
# message. In between the TRACE_START and TRACE_END
# the stack trace is submitted as multiple lines.
TRACE_START = "%TRACES "
# Notification that a trace ends.
TRACE_END = "%TRACEE "
# Notification that the expected result has started.
# The end of the expected result is signaled by a EXPECTED_END.
EXPECTED_START = "%EXPECTS"
# Notification that an expected result ends.
EXPECTED_END = "%EXPECTE"
# Notification that the actual result has started.
# The end of the actual result is signaled by a ACTUAL_END.
ACTUAL_START = "%ACTUALS"
# Notification that an actual result ends.
ACTUAL_END = "%ACTUALE"
#Test identifier prefix for ignored tests.
IGNORED_TEST_PREFIX = "@Ignore: "
end # of MessageIds
class SocketConnection
def disconnect
if @socket
#debug "Closing socket"
begin
@socket.close
rescue
debug $!.to_s
end
@socket = nil
#debug "Socket closed"
end
end
def connectSocket(port)
return false unless port > 0
#debug "Opening socket on #{port}"
for i in 1..10
#debug "Iteration #{i}"
begin
@socket = TCPSocket.new('localhost', port)
#debug "Socket opened"
return true
rescue
#debug $!.to_s
end
sleep 1
end
false
end
def sendMessage(message)
#puts message
if @socket
@socket.puts message
end
end
def notifyTestTreeEntry(testId, testName, hasChildren, testCount)
sendMessage MessageIds::TEST_TREE + testId + ',' + escapeComma(testName) + ',' + hasChildren.to_s + ',' + testCount.to_s
end
def notifyTestStarted(testId, testName)
sendMessage MessageIds::TEST_START + testId + "," + escapeComma(testName)
end
def notifyTestFailure(testId, testName, status)
sendMessage status + testId + "," + escapeComma(testName)
end
def notifyTestEnded(testId, testName)
sendMessage MessageIds::TEST_END + testId + "," + escapeComma(testName)
end
def notifyTestRunStarted(testCount)
sendMessage MessageIds::TEST_RUN_START + testCount.to_s + " " + "v2"
end
def notifyTestRunEnded(elapsedTime)
sendMessage MessageIds::TEST_RUN_END + elapsedTime.to_s
end
def escapeComma(s)
s.gsub(/([\\,])/, '\\\\\1')
end
private :escapeComma
end
class DLTKFormatter < Spec::Runner::Formatter::BaseFormatter
def initialize(*args)
super
@connection = SocketConnection.new()
end
def start(example_count)
@connection.connectSocket ENV[EnvVars::PORT].to_i
@connection.notifyTestRunStarted example_count
end
# tgrimm: Since rspec 1.2.4 add_example_group was renamed to example_groupe_started
def add_example_group(example_group)
example_group_started(example_group)
if ::Spec::VERSION::MAJOR > 1 || ::Spec::VERSION::MINOR > 0
super
end
end
def example_group_started(example_group)
options = @options ? @options : ::Spec::Runner.options
examples_to_run = example_group.examples
examples_to_run = examples_to_run.reject do |example|
matcher = ::Spec::Example::ExampleMatcher.new(example_group.description.to_s, example.description)
!matcher.matches?(options.examples)
end unless options.examples.empty?
if examples_to_run.size > 0 then
# ssanders: Ensure that description is never blank
description = example_group.description || example_group.to_s
@connection.notifyTestTreeEntry getTestId(example_group), description, true, examples_to_run.size
examples_to_run.each do |e|
@connection.notifyTestTreeEntry getTestId(e), getTestName(e), false, 1
end
end
end
unless ::Spec::VERSION::MAJOR > 1 || ::Spec::VERSION::MINOR > 0
def add_behaviour(description)
add_example_group(description)
end
end
def example_started(example)
@connection.notifyTestStarted getTestId(example), getTestName(example)
end
def example_passed(example)
@connection.notifyTestEnded getTestId(example), getTestName(example)
end
def example_pending(behaviour, example, message = nil)
# ssanders: In older versions and for "pending" the example is actually passed as behaviour
example = behaviour if example.is_a?(String)
@connection.notifyTestEnded getTestId(example), MessageIds::IGNORED_TEST_PREFIX + getTestName(example)
end
EXPECTED_GOT_RE = /^expected: (.+),\n\s+got: (.+) \(using (==|===)\)$/s
def example_failed(example, counter, failure)
testId = getTestId(example)
testName = getTestName(example)
f = failure.exception
if failure.expectation_not_met?
@connection.notifyTestFailure testId, testName, MessageIds::TEST_FAILED
if f.message =~ EXPECTED_GOT_RE
@connection.sendMessage MessageIds::EXPECTED_START
@connection.sendMessage $1
@connection.sendMessage MessageIds::EXPECTED_END
@connection.sendMessage MessageIds::ACTUAL_START
@connection.sendMessage $2
@connection.sendMessage MessageIds::ACTUAL_END
end
else
@connection.notifyTestFailure testId, testName, MessageIds::TEST_ERROR
end
@connection.sendMessage MessageIds::TRACE_START
@connection.sendMessage f.message
f.backtrace.each { |line| @connection.sendMessage line }
@connection.sendMessage MessageIds::TRACE_END
@connection.notifyTestEnded testId, testName
end
def dump_summary(duration, example_count, failure_count, pending_count)
@connection.notifyTestRunEnded((duration * 1000).to_i)
end
def close
@connection.disconnect
@connection = nil
end
# internal
def getTestId(example)
return example.__id__.to_s
end
def getTestName(example)
if example.respond_to?(:rspecTestName)
name = example.rspecTestName
elsif example.description
name = example.description ? example.description : "NO NAME"
if example.respond_to?(:location)
name += "<" + example.location if example.location
else
name += "<" + example.backtrace if example.backtrace
end
end
return name.to_s
end
end
end
end
#if __FILE__ == $0
# ARGV.push 'bowling_spec.rb'
#end
ARGV.push '--format'
ARGV.push 'DLTK::RSpec::DLTKFormatter'
# tgrimm: RSpec 1.2.1 was release 7 days after 1.2.0, so there's no real reason to support 1.2.0
raise "RSpec 1.2.0 is not supported, please update RSpec" if ::Spec::VERSION::STRING == '1.2.0'
if ::Spec::VERSION::MAJOR > 1 || ::Spec::VERSION::MINOR > 0
if ::Spec.constants.include?("Extensions") && ::Spec::Extensions::Main.private_method_defined?(:rspec_options)
options = rspec_options
else # ssanders: RSpec > 1.1.4
options = ::Spec::Runner.options
end
exit ::Spec::Runner::CommandLine.run(options)
else # ssanders: RSpec < 1.1
exit ::Spec::Runner::CommandLine.run(ARGV, STDERR, STDOUT)
end