torkify 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -1
- data/.travis.yml +6 -0
- data/README.md +8 -2
- data/VERSION +1 -1
- data/lib/torkify/conductor.rb +25 -14
- data/lib/torkify/{events/event.rb → event/basic_event.rb} +4 -4
- data/lib/torkify/event/dispatcher.rb +91 -0
- data/lib/torkify/event/echo_event.rb +23 -0
- data/lib/torkify/{events/event_message.rb → event/message.rb} +2 -2
- data/lib/torkify/event/parser.rb +70 -0
- data/lib/torkify/{events → event}/pass_or_fail_event.rb +15 -3
- data/lib/torkify/event/ran_all_test_files_event.rb +30 -0
- data/lib/torkify/{events → event}/status_change_event.rb +3 -3
- data/lib/torkify/{events → event}/test_event.rb +4 -3
- data/lib/torkify/listener.rb +17 -8
- data/lib/torkify/log/line_matcher.rb +33 -0
- data/lib/torkify/log/log_reader.rb +32 -0
- data/lib/torkify/log/parser.rb +96 -0
- data/lib/torkify/log/test_error.rb +7 -0
- data/lib/torkify/reader.rb +7 -5
- data/lib/torkify/version.rb +1 -1
- data/lib/torkify.rb +2 -3
- data/spec/conductor_spec.rb +3 -4
- data/spec/event/basic_event_spec.rb +30 -0
- data/spec/event/dispatcher_spec.rb +190 -0
- data/spec/event/echo_event_spec.rb +22 -0
- data/spec/event/parser_spec.rb +288 -0
- data/spec/{pass_or_fail_event_spec.rb → event/pass_or_fail_event_spec.rb} +2 -2
- data/spec/event/ran_all_test_files_event_spec.rb +42 -0
- data/spec/{status_change_event_spec.rb → event/status_change_event_spec.rb} +3 -3
- data/spec/{test_event_spec.rb → event/test_event_spec.rb} +2 -2
- data/spec/log/integration_spec.rb +222 -0
- data/spec/log/line_matcher_spec.rb +247 -0
- data/spec/log/log_reader_spec.rb +54 -0
- data/spec/log/logs/invalid_ruby_error_1.log +1 -0
- data/spec/log/logs/rspec_failure_1.log +19 -0
- data/spec/log/logs/rspec_failure_2.log +57 -0
- data/spec/log/logs/ruby_error_1.log +16 -0
- data/spec/log/logs/ruby_error_2.log +17 -0
- data/spec/log/logs/ruby_error_3.log +18 -0
- data/spec/log/logs/test_unit_error_1.log +22 -0
- data/spec/log/logs/test_unit_failure_1.log +22 -0
- data/spec/log/test_error_spec.rb +36 -0
- data/torkify.gemspec +1 -1
- metadata +63 -40
- data/lib/torkify/event_parser.rb +0 -36
- data/lib/torkify/observer_set.rb +0 -62
- data/spec/event_parser_spec.rb +0 -154
- data/spec/event_spec.rb +0 -18
- data/spec/observer_set_spec.rb +0 -100
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d3a7e8564007cfc37702bcc37cbba490154c0fcd
|
4
|
+
data.tar.gz: e757b6552cb0cdb63434839d22c3ab9395759f5e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3059816687b7ae835341dd7100ed6bcba3c1f7fd4c6465f8010a3179839c7e552d1897b8bebf3eb85f3f21775d5f562d4c0eea4f72aae56cb257524eda060b46
|
7
|
+
data.tar.gz: 9f13edf48f04710e380723f5b9c43ec4f596bdb2b8d730dca33163b6eae93ec09cf3d70cdb74001c078b37779802add673bc8e33d8feb76faa1e0acc2589f119
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
# Torkify
|
2
2
|
|
3
|
-
|
3
|
+
[![Build Status](https://travis-ci.org/joonty/torkify.png?branch=master)](https://travis-ci.org/joonty/torkify)
|
4
4
|
|
5
|
-
Torkify
|
5
|
+
Torkify aims to be a one-stop shop for testing ruby applications, and handling callbacks after test execution for things like notifications.
|
6
|
+
|
7
|
+
Torkify integrates with [tork][1], which is a solution for automating test execution, as you change your source files.
|
8
|
+
|
9
|
+
Torkify hooks in to tork's remote events, and allows you to add gems and build callbacks that run when tests fail or pass. This makes it easy for you to write code that triggers cool stuff when your tests explode (your imagination is the limit).
|
10
|
+
|
11
|
+
Plus, [tork][1] is a fantastic tool that makes it very easy to run tests immediately and automatically in a pre-loaded environment.
|
6
12
|
|
7
13
|
## An example
|
8
14
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/lib/torkify/conductor.rb
CHANGED
@@ -1,33 +1,44 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_relative '
|
1
|
+
require_relative 'event/basic_event'
|
2
|
+
require_relative 'event/parser'
|
3
|
+
require_relative 'event/dispatcher'
|
4
|
+
require_relative 'log/parser'
|
3
5
|
|
4
6
|
module Torkify
|
5
7
|
|
6
8
|
# Connect the socket reader and observers, and dispatch events.
|
7
9
|
class Conductor
|
8
|
-
attr_accessor :observers
|
9
|
-
|
10
10
|
# Create with a set of observers.
|
11
11
|
def initialize(observers)
|
12
|
-
@
|
12
|
+
@dispatcher = Event::Dispatcher.new observers
|
13
|
+
end
|
14
|
+
|
15
|
+
def observers
|
16
|
+
@dispatcher.observers
|
17
|
+
end
|
18
|
+
|
19
|
+
def observers=(*args)
|
20
|
+
@dispatcher.send :observers=, *args
|
13
21
|
end
|
14
22
|
|
15
23
|
# Start reading from the reader, which is an IO-like object.
|
16
24
|
#
|
17
25
|
# Parse each line and dispatch it as an event object to all observers.
|
18
26
|
def start(reader)
|
19
|
-
dispatch Event.new 'startup'
|
20
|
-
parser =
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
27
|
+
dispatch Event::BasicEvent.new 'startup'
|
28
|
+
parser = Event::Parser.new
|
29
|
+
|
30
|
+
while line = reader.gets
|
31
|
+
Torkify.logger.debug { "Read line: #{line}" }
|
32
|
+
events = parser.parse line
|
33
|
+
dispatch(*events)
|
34
|
+
end
|
35
|
+
|
36
|
+
dispatch Event::BasicEvent.new 'shutdown'
|
26
37
|
end
|
27
38
|
|
28
39
|
protected
|
29
|
-
def dispatch(
|
30
|
-
@
|
40
|
+
def dispatch(*events)
|
41
|
+
events.each { |event| @dispatcher.dispatch(event) }
|
31
42
|
end
|
32
43
|
end
|
33
44
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'message'
|
2
2
|
|
3
|
-
module Torkify
|
3
|
+
module Torkify::Event
|
4
4
|
|
5
5
|
# Event used for all events that have no associated data.
|
6
6
|
#
|
@@ -10,8 +10,8 @@ module Torkify
|
|
10
10
|
# - shutdown
|
11
11
|
# - startup
|
12
12
|
# - anything else...
|
13
|
-
class
|
14
|
-
include
|
13
|
+
class BasicEvent < Struct.new(:type)
|
14
|
+
include Message
|
15
15
|
|
16
16
|
def to_s
|
17
17
|
type
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative 'ran_all_test_files_event'
|
2
|
+
|
3
|
+
module Torkify::Event
|
4
|
+
class Dispatcher
|
5
|
+
attr_accessor :observers
|
6
|
+
|
7
|
+
def initialize(observers)
|
8
|
+
@observers = observers
|
9
|
+
end
|
10
|
+
|
11
|
+
def dispatch(event)
|
12
|
+
observers.each do |o|
|
13
|
+
ObserverEventDispatcher.new(o, event).dispatch
|
14
|
+
end
|
15
|
+
act_if_required event
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def act_if_required(event)
|
21
|
+
case event.type
|
22
|
+
when :run_all_test_files
|
23
|
+
@ran_all_tests_event = RanAllTestFilesEvent.new(:ran_all_test_files, [], [])
|
24
|
+
when :pass
|
25
|
+
if @ran_all_tests_event
|
26
|
+
@ran_all_tests_event.passed << event
|
27
|
+
end
|
28
|
+
when :fail
|
29
|
+
if @ran_all_tests_event
|
30
|
+
@ran_all_tests_event.failed << event
|
31
|
+
end
|
32
|
+
when :idle
|
33
|
+
if @ran_all_tests_event
|
34
|
+
@ran_all_tests_event.stop!
|
35
|
+
dispatch @ran_all_tests_event
|
36
|
+
@ran_all_tests_event = nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class ObserverEventDispatcher
|
43
|
+
def initialize(observer, event)
|
44
|
+
@observer = observer
|
45
|
+
@event = event
|
46
|
+
end
|
47
|
+
|
48
|
+
def dispatch
|
49
|
+
dispatch_and_rescue
|
50
|
+
rescue => e
|
51
|
+
Torkify.logger.error { "Caught exception from observer #{observer.inspect} during ##{event.message}: #{e}\n\t#{e.backtrace.join("\n\t")}" }
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
attr_reader :observer, :event
|
56
|
+
|
57
|
+
def dispatch_and_rescue
|
58
|
+
send_and_trap arguments_for_event_message
|
59
|
+
rescue NameError
|
60
|
+
send_args = arguments_by_arity 1
|
61
|
+
send_and_trap send_args
|
62
|
+
end
|
63
|
+
|
64
|
+
def send_and_trap(args)
|
65
|
+
observer.send event.message, *args
|
66
|
+
rescue NoMethodError => e
|
67
|
+
if e.message.include? event.message.to_s
|
68
|
+
Torkify.logger.debug { "No method #{event.message} defined on #{observer.inspect}" }
|
69
|
+
else
|
70
|
+
raise
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def arguments_for_event_message
|
75
|
+
arguments_by_arity observer.method(event.message).arity
|
76
|
+
end
|
77
|
+
|
78
|
+
def arguments_by_arity(method_arity)
|
79
|
+
case method_arity
|
80
|
+
when 0
|
81
|
+
[]
|
82
|
+
when -1, 1
|
83
|
+
[event]
|
84
|
+
else
|
85
|
+
[event].fill(nil, 1, method_arity - 1)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'message'
|
2
|
+
|
3
|
+
module Torkify::Event
|
4
|
+
|
5
|
+
# Event used for all events that have no associated data.
|
6
|
+
#
|
7
|
+
# Types:
|
8
|
+
#
|
9
|
+
# - absorb
|
10
|
+
# - shutdown
|
11
|
+
# - startup
|
12
|
+
# - anything else...
|
13
|
+
class EchoEvent < Struct.new(:type, :arguments)
|
14
|
+
include Message
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
type
|
18
|
+
end
|
19
|
+
|
20
|
+
alias :args :arguments
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'json'
|
2
|
+
require_relative 'basic_event'
|
3
|
+
require_relative 'test_event'
|
4
|
+
require_relative 'pass_or_fail_event'
|
5
|
+
require_relative 'status_change_event'
|
6
|
+
require_relative 'echo_event'
|
7
|
+
|
8
|
+
module Torkify::Event
|
9
|
+
|
10
|
+
# Parse raw strings passed by tork into event objects.
|
11
|
+
class Parser
|
12
|
+
|
13
|
+
# Parse a raw string and return an object based on the event type.
|
14
|
+
#
|
15
|
+
# E.g. a raw string like:
|
16
|
+
# > '["test","spec/reader_spec.rb",[],"spec/reader_spec.rb.log",3]'
|
17
|
+
def parse(line)
|
18
|
+
raw = JSON.load line
|
19
|
+
event_from_data raw
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
# Create an event object from the array of data.
|
24
|
+
def event_from_data(data)
|
25
|
+
case data.first
|
26
|
+
when 'test'
|
27
|
+
[ TestEvent.new(*data) ]
|
28
|
+
when 'pass', 'fail'
|
29
|
+
[ PassOrFailEvent.new(*data) ]
|
30
|
+
when 'pass_now_fail', 'fail_now_pass'
|
31
|
+
[ StatusChangeEvent.new(data[0], data[1], event_from_data(data[2]).first) ]
|
32
|
+
when 'echo'
|
33
|
+
parse_echo_event data
|
34
|
+
else
|
35
|
+
[ BasicEvent.new(data.first) ]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_echo_event(data)
|
40
|
+
events = [ EchoEvent.new(*data) ]
|
41
|
+
event = event_from_echo(*data[1])
|
42
|
+
events << event if event
|
43
|
+
events
|
44
|
+
end
|
45
|
+
|
46
|
+
def event_from_echo(action, *action_args)
|
47
|
+
if echo_commands_requiring_args.include? action
|
48
|
+
return if action_args.empty? || action_args.first.empty?
|
49
|
+
end
|
50
|
+
if known_echo_commands.include? action
|
51
|
+
BasicEvent.new action
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def known_echo_commands
|
56
|
+
[ 'run_all_test_files',
|
57
|
+
'run_test_files',
|
58
|
+
'run_test_file',
|
59
|
+
'stop_running_test_files',
|
60
|
+
'stop_running_test_files',
|
61
|
+
'rerun_passed_test_files',
|
62
|
+
'rerun_failed_test_files' ]
|
63
|
+
end
|
64
|
+
|
65
|
+
def echo_commands_requiring_args
|
66
|
+
[ 'run_test_files',
|
67
|
+
'run_test_file' ]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'message'
|
2
2
|
|
3
|
-
module Torkify
|
3
|
+
module Torkify::Event
|
4
4
|
|
5
5
|
# Event used for test passes or failures.
|
6
6
|
#
|
@@ -9,7 +9,7 @@ module Torkify
|
|
9
9
|
# - pass
|
10
10
|
# - fail
|
11
11
|
class PassOrFailEvent < Struct.new(:type, :file, :lines, :log_file, :worker, :exit_code, :exit_info)
|
12
|
-
include
|
12
|
+
include Message
|
13
13
|
|
14
14
|
# Get the PID from the exit info.
|
15
15
|
def pid
|
@@ -17,9 +17,21 @@ module Torkify
|
|
17
17
|
matched.first.to_i if matched
|
18
18
|
end
|
19
19
|
|
20
|
+
def errors
|
21
|
+
@errors ||= parse_errors_from_log
|
22
|
+
end
|
23
|
+
|
20
24
|
def to_s
|
21
25
|
s = "#{type.upcase} #{file}"
|
22
26
|
s += lines.any? ? " (lines #{lines.join(', ')})" : ''
|
23
27
|
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def parse_errors_from_log
|
31
|
+
log = File.open log_file
|
32
|
+
parser = Torkify::Log::Parser.new log
|
33
|
+
parser.parse
|
34
|
+
parser.errors
|
35
|
+
end
|
24
36
|
end
|
25
37
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'message'
|
2
|
+
|
3
|
+
module Torkify::Event
|
4
|
+
|
5
|
+
# Event used when all tests have finished running.
|
6
|
+
#
|
7
|
+
# The time method gives the approximate time in seconds that the tests
|
8
|
+
# took to run.
|
9
|
+
class RanAllTestFilesEvent < Struct.new(:type, :passed, :failed)
|
10
|
+
include Message
|
11
|
+
|
12
|
+
def initialize(type, passed = [], failed = [])
|
13
|
+
@created = Time.now.to_f
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def stop!
|
18
|
+
if @time.nil?
|
19
|
+
@time = Time.now.to_f - @created
|
20
|
+
end
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def time
|
25
|
+
stop!
|
26
|
+
@time
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'message'
|
2
2
|
|
3
|
-
module Torkify
|
3
|
+
module Torkify::Event
|
4
4
|
|
5
5
|
# Event used for changes in test status.
|
6
6
|
#
|
@@ -11,7 +11,7 @@ module Torkify
|
|
11
11
|
#
|
12
12
|
# Includes the actual fail/pass event as a separate object.
|
13
13
|
class StatusChangeEvent < Struct.new(:type, :file, :event)
|
14
|
-
include
|
14
|
+
include Message
|
15
15
|
|
16
16
|
def to_s
|
17
17
|
"#{type.upcase.gsub('_',' ')} #{file}"
|
@@ -1,11 +1,12 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'message'
|
2
|
+
|
3
|
+
module Torkify::Event
|
2
4
|
|
3
|
-
module Torkify
|
4
5
|
# Event used when a test is started.
|
5
6
|
#
|
6
7
|
# This is currently only one type: 'test'
|
7
8
|
class TestEvent < Struct.new(:type, :file, :lines, :log_file, :worker)
|
8
|
-
include
|
9
|
+
include Message
|
9
10
|
|
10
11
|
def to_s
|
11
12
|
s = "#{type.upcase} #{file}"
|
data/lib/torkify/listener.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
+
require 'set'
|
1
2
|
require_relative 'conductor'
|
2
|
-
require_relative 'observer_set'
|
3
3
|
require_relative 'reader'
|
4
4
|
require_relative 'exceptions'
|
5
5
|
|
@@ -12,7 +12,7 @@ module Torkify
|
|
12
12
|
def initialize(command = 'tork-remote tork-engine', dir = Dir.pwd)
|
13
13
|
@command = command
|
14
14
|
@dir = dir
|
15
|
-
@conductor = Conductor.new
|
15
|
+
@conductor = Conductor.new Set.new
|
16
16
|
end
|
17
17
|
|
18
18
|
# Add an observer object to be notified of tork events.
|
@@ -72,20 +72,29 @@ module Torkify
|
|
72
72
|
# Start the torkify listener and tork itself.
|
73
73
|
#
|
74
74
|
# It forks the current process and runs torkify as the child. It then
|
75
|
-
# runs tork as the main process, allowing for stdin to be passed to tork.
|
75
|
+
# runs the tork CLI as the main process, allowing for stdin to be passed to tork.
|
76
76
|
#
|
77
77
|
# Calls #start_loop().
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
def start_with_tork(command = 'tork', tork_env = 'default')
|
78
|
+
def start_with_tork
|
79
|
+
load_tork
|
80
|
+
|
82
81
|
if fork
|
83
82
|
start_loop
|
84
83
|
else
|
85
84
|
# Run tork in main process to keep stdin
|
86
85
|
Torkify.logger.info { "Starting tork" }
|
87
|
-
|
86
|
+
|
87
|
+
$0 = File.basename Dir.pwd
|
88
|
+
Tork::CLIApp.new.loop
|
88
89
|
end
|
89
90
|
end
|
91
|
+
|
92
|
+
def load_tork
|
93
|
+
require 'tork/config'
|
94
|
+
require 'tork/cliapp'
|
95
|
+
rescue LoadError
|
96
|
+
Torkify.logger.fatal { "Could not load tork: try running `gem install tork`" }
|
97
|
+
raise
|
98
|
+
end
|
90
99
|
end
|
91
100
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
module Torkify::Log
|
3
|
+
class LineMatcher
|
4
|
+
PATTERNS = {
|
5
|
+
'tork_load_line' => /^Loaded suite tork[^\s]+\s(.+)/,
|
6
|
+
'error_description' => /^[\s#]*([^:]+):([0-9]+):in/,
|
7
|
+
'file_extraction' => /\[([^:]+):([0-9]+)\]/m,
|
8
|
+
'tork_error_line' => /^.+tork\/master\.rb:[0-9]+:in [^:]+:\s/,
|
9
|
+
'test_error_or_failure' => /^(\s+[0-9]+\)|Failure(?!s)|Error)/,
|
10
|
+
'test_summary' => /^([0-9]+\s[a-z]+,)+/,
|
11
|
+
'finished_line' => /^Finished/
|
12
|
+
}
|
13
|
+
|
14
|
+
def initialize(line)
|
15
|
+
self.line = line
|
16
|
+
end
|
17
|
+
|
18
|
+
PATTERNS.each do |name, reg|
|
19
|
+
define_method("#{name}?") { !(line =~ PATTERNS[name]).nil? }
|
20
|
+
define_method("#{name}") { PATTERNS[name].match(line) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def end_of_errors?
|
24
|
+
test_summary? || finished_line?
|
25
|
+
end
|
26
|
+
|
27
|
+
alias :ruby_error :error_description
|
28
|
+
alias :ruby_error? :error_description?
|
29
|
+
|
30
|
+
protected
|
31
|
+
attr_accessor :line
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'line_matcher'
|
2
|
+
|
3
|
+
module Torkify::Log
|
4
|
+
class LogReader
|
5
|
+
attr_reader :line
|
6
|
+
|
7
|
+
def initialize(stream)
|
8
|
+
@stream = stream
|
9
|
+
@line = stream.readline
|
10
|
+
end
|
11
|
+
|
12
|
+
def forward
|
13
|
+
self.line = stream.readline
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def matcher
|
19
|
+
@matcher ||= LineMatcher.new(line)
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
attr_reader :stream
|
24
|
+
attr_writer :line
|
25
|
+
|
26
|
+
def line=(line)
|
27
|
+
@line = line
|
28
|
+
@matcher = nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative 'test_error'
|
2
|
+
require_relative 'log_reader'
|
3
|
+
|
4
|
+
module Torkify::Log
|
5
|
+
module Error; end
|
6
|
+
class ParserError < StandardError; end
|
7
|
+
|
8
|
+
class Parser
|
9
|
+
attr_reader :errors
|
10
|
+
|
11
|
+
def initialize(stream)
|
12
|
+
@reader = LogReader.new stream
|
13
|
+
self.errors = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse
|
17
|
+
parse_log
|
18
|
+
self
|
19
|
+
rescue EOFError
|
20
|
+
# noop
|
21
|
+
self
|
22
|
+
rescue Exception => e
|
23
|
+
# Tag all exceptions with Torkify::Error
|
24
|
+
e.extend Error
|
25
|
+
raise e
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
attr_writer :errors
|
30
|
+
attr_reader :reader
|
31
|
+
|
32
|
+
def parse_log
|
33
|
+
loop do
|
34
|
+
if reader.matcher.ruby_error?
|
35
|
+
parse_ruby_error
|
36
|
+
if errors.empty?
|
37
|
+
raise ParserError, "Failed to read error from log file"
|
38
|
+
end
|
39
|
+
break
|
40
|
+
elsif tork_line_match = reader.matcher.tork_load_line
|
41
|
+
@file_fallback = tork_line_match[1].strip + ".rb"
|
42
|
+
elsif reader.matcher.test_error_or_failure?
|
43
|
+
parse_errors_or_failures
|
44
|
+
end
|
45
|
+
reader.forward
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_ruby_error
|
50
|
+
line = reader.line
|
51
|
+
if tork_match = reader.matcher.tork_error_line
|
52
|
+
line.slice! tork_match[0]
|
53
|
+
end
|
54
|
+
matches = line.split(':')
|
55
|
+
if matches.length >= 3
|
56
|
+
self.errors << TestError.new(matches.shift.strip,
|
57
|
+
matches.shift.strip,
|
58
|
+
matches.join(':').strip.gsub("'", "`"),
|
59
|
+
'E')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def apply_file_from_matches(error, matches)
|
64
|
+
if matches
|
65
|
+
matches = matches.to_a
|
66
|
+
matches.shift
|
67
|
+
error.filename = matches.shift.strip
|
68
|
+
error.lnum = matches.shift.strip
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_errors_or_failures
|
73
|
+
until reader.matcher.end_of_errors?
|
74
|
+
if reader.matcher.test_error_or_failure?
|
75
|
+
error = TestError.new(@file_fallback, '0', '', 'E')
|
76
|
+
self.errors << error
|
77
|
+
end
|
78
|
+
|
79
|
+
matches = reader.matcher.error_description
|
80
|
+
error.text << reader.line.gsub("'", "`")
|
81
|
+
|
82
|
+
apply_file_from_matches error, matches
|
83
|
+
reader.forward
|
84
|
+
end
|
85
|
+
|
86
|
+
errors.each do |err|
|
87
|
+
if err.lnum == '0'
|
88
|
+
matches = err.text.match(LineMatcher::PATTERNS['file_extraction'])
|
89
|
+
apply_file_from_matches err, matches
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
data/lib/torkify/reader.rb
CHANGED
@@ -12,10 +12,10 @@ module Torkify
|
|
12
12
|
# has been written to the command's STDERR stream.
|
13
13
|
def initialize(command = 'tork-remote tork-engine', run_in_dir = Dir.pwd)
|
14
14
|
Dir.chdir(run_in_dir) do
|
15
|
-
|
15
|
+
self.in, self.out, self.err, self.thread = Open3.popen3 command
|
16
16
|
|
17
|
-
if
|
18
|
-
raise TorkError,
|
17
|
+
if out.eof?
|
18
|
+
raise TorkError, err.read.strip
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
@@ -24,12 +24,14 @@ module Torkify
|
|
24
24
|
#
|
25
25
|
# This allows this class to be used in an IO like way.
|
26
26
|
def method_missing(method, *args, &blk)
|
27
|
-
|
27
|
+
out.send method, *args, &blk
|
28
28
|
end
|
29
29
|
|
30
30
|
# Allow respond_to? to work with method_missing.
|
31
31
|
def respond_to?(method, include_private = false)
|
32
|
-
|
32
|
+
out.respond_to? method, include_private
|
33
33
|
end
|
34
|
+
protected
|
35
|
+
attr_accessor :in, :out, :err, :thread
|
34
36
|
end
|
35
37
|
end
|
data/lib/torkify/version.rb
CHANGED