gauge-ruby 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/api.pb.rb +0 -16
- data/lib/code_parser.rb +49 -34
- data/lib/connector.rb +4 -32
- data/lib/executor.rb +28 -9
- data/lib/gauge.rb +17 -16
- data/lib/gauge_runtime.rb +17 -13
- data/lib/log.rb +33 -0
- data/lib/message_processor.rb +5 -1
- data/lib/messages.pb.rb +102 -0
- data/lib/method_cache.rb +48 -26
- data/lib/processors/cache_file_processor.rb +43 -0
- data/lib/processors/execute_step_request_processor.rb +1 -1
- data/lib/processors/execution_handler.rb +13 -2
- data/lib/processors/execution_hook_processors.rb +5 -1
- data/lib/processors/implementation_file_list_processor.rb +29 -0
- data/lib/processors/refactor_step_request_processor.rb +11 -10
- data/lib/processors/step_name_request_processor.rb +13 -7
- data/lib/processors/step_positions_request_processor.rb +35 -0
- data/lib/processors/step_validation_request_processor.rb +19 -11
- data/lib/processors/stub_implementation_processor.rb +46 -0
- data/lib/spec.pb.rb +38 -4
- data/lib/static_loader.rb +103 -0
- data/lib/util.rb +17 -5
- metadata +50 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86bb6e44e272971e83b2bef3c38dd92106cae506
|
4
|
+
data.tar.gz: cfdf3a81e93b61c3acc54da0a123630781043860
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36640d7d7936fc4600b7dc1040b922f893bd72ff4e1a514251fefba70a16459009c508031c57e328c59b87da07e4845622409f5da1c4539a66426fa074660042
|
7
|
+
data.tar.gz: 220d4b472ee354d203bcbe35ab54f40f33be040d9eb8b664fc6e5001b0c5888f5000cd508fb4de196f6ed7a831641a2ee54f156247846a97af9846495016a341
|
data/lib/api.pb.rb
CHANGED
@@ -16,7 +16,6 @@ module Gauge
|
|
16
16
|
class GetAllStepsResponse < ::ProtocolBuffers::Message; end
|
17
17
|
class SpecsRequest < ::ProtocolBuffers::Message; end
|
18
18
|
class SpecsResponse < ::ProtocolBuffers::Message; end
|
19
|
-
class Error < ::ProtocolBuffers::Message; end
|
20
19
|
class GetAllConceptsRequest < ::ProtocolBuffers::Message; end
|
21
20
|
class GetAllConceptsResponse < ::ProtocolBuffers::Message; end
|
22
21
|
class ConceptInfo < ::ProtocolBuffers::Message; end
|
@@ -27,7 +26,6 @@ module Gauge
|
|
27
26
|
class ErrorResponse < ::ProtocolBuffers::Message; end
|
28
27
|
class PerformRefactoringRequest < ::ProtocolBuffers::Message; end
|
29
28
|
class PerformRefactoringResponse < ::ProtocolBuffers::Message; end
|
30
|
-
class ExtractConceptInfoRequest < ::ProtocolBuffers::Message; end
|
31
29
|
class ExtractConceptRequest < ::ProtocolBuffers::Message; end
|
32
30
|
class TextInfo < ::ProtocolBuffers::Message; end
|
33
31
|
class Step < ::ProtocolBuffers::Message; end
|
@@ -93,14 +91,6 @@ module Gauge
|
|
93
91
|
repeated ::Gauge::Messages::SpecsResponse::SpecDetail, :details, 1
|
94
92
|
end
|
95
93
|
|
96
|
-
class Error < ::ProtocolBuffers::Message
|
97
|
-
set_fully_qualified_name "gauge.messages.Error"
|
98
|
-
|
99
|
-
optional :string, :filename, 1
|
100
|
-
optional :int32, :lineNumber, 2
|
101
|
-
optional :string, :message, 3
|
102
|
-
end
|
103
|
-
|
104
94
|
class GetAllConceptsRequest < ::ProtocolBuffers::Message
|
105
95
|
set_fully_qualified_name "gauge.messages.GetAllConceptsRequest"
|
106
96
|
|
@@ -166,12 +156,6 @@ module Gauge
|
|
166
156
|
repeated :string, :filesChanged, 3
|
167
157
|
end
|
168
158
|
|
169
|
-
class ExtractConceptInfoRequest < ::ProtocolBuffers::Message
|
170
|
-
set_fully_qualified_name "gauge.messages.ExtractConceptInfoRequest"
|
171
|
-
|
172
|
-
optional :string, :text, 1
|
173
|
-
end
|
174
|
-
|
175
159
|
class ExtractConceptRequest < ::ProtocolBuffers::Message
|
176
160
|
set_fully_qualified_name "gauge.messages.ExtractConceptRequest"
|
177
161
|
|
data/lib/code_parser.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2018 ThoughtWorks, Inc.
|
2
2
|
#
|
3
3
|
# This file is part of Gauge-Ruby.
|
4
4
|
#
|
@@ -16,6 +16,7 @@
|
|
16
16
|
# along with Gauge-Ruby. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
|
18
18
|
require 'parser/current'
|
19
|
+
require 'unparser'
|
19
20
|
require 'method_source'
|
20
21
|
require 'fileutils'
|
21
22
|
require 'tempfile'
|
@@ -24,16 +25,15 @@ require 'util'
|
|
24
25
|
module Gauge
|
25
26
|
# @api private
|
26
27
|
class CodeParser
|
27
|
-
def self.step_args_from_code(
|
28
|
-
ast=code_to_ast(code)
|
28
|
+
def self.step_args_from_code(ast)
|
29
29
|
arg_node = ast.children[1]
|
30
30
|
arg_node.children
|
31
31
|
end
|
32
32
|
|
33
|
-
def self.
|
33
|
+
def self.process_node(node, param_positions, new_param_values, new_step_text)
|
34
34
|
new_params = []
|
35
|
-
args = step_args_from_code
|
36
|
-
param_positions.sort_by!(&:newPosition).each.with_index {
|
35
|
+
args = step_args_from_code node
|
36
|
+
param_positions.sort_by!(&:newPosition).each.with_index {|e, i|
|
37
37
|
if e.oldPosition == -1
|
38
38
|
param = Util.remove_special_chars new_param_values[e.newPosition].downcase.split.join('_')
|
39
39
|
if param == ''
|
@@ -44,41 +44,56 @@ module Gauge
|
|
44
44
|
new_params[e.newPosition] = args[e.oldPosition].children[0]
|
45
45
|
end
|
46
46
|
}
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
rewriter = Parser::Source::Rewriter.new(buffer)
|
54
|
-
.replace(ast.children[0].location.expression, "step '#{new_step_text}'")
|
55
|
-
|
56
|
-
# hack, could not find an easy way to manipulate the ast to include arguments, when none existed originally.
|
57
|
-
# it's just easy to add arguments via string substitution.
|
58
|
-
return include_args(rewriter.process, new_params_string) if ast.children[1].location.expression.nil?
|
59
|
-
|
60
|
-
#insert new arguments
|
61
|
-
rewriter.replace(ast.children[1].location.expression, new_params_string).process
|
47
|
+
args = new_params.map {|v| Parser::AST::Node.new(:arg, [v])}
|
48
|
+
step = [node.children[0].children[0], node.children[0].children[1], Parser::AST::Node.new(:str, [new_step_text])]
|
49
|
+
c1 = Parser::AST::Node.new(:send, step)
|
50
|
+
c2 = Parser::AST::Node.new(:args, args)
|
51
|
+
Parser::AST::Node.new(:block, [c1, c2, node.children[2]])
|
62
52
|
end
|
63
53
|
|
64
|
-
def self.
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
54
|
+
def self.replace(ast, &visitor)
|
55
|
+
return ast if ast.class != Parser::AST::Node
|
56
|
+
if ast && step_node?(ast)
|
57
|
+
visitor.call(ast)
|
58
|
+
else
|
59
|
+
children = ast.children.map {|node|
|
60
|
+
replace(node, &visitor)
|
61
|
+
}
|
62
|
+
return ast.updated(nil, children, nil)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.step_node?(node)
|
67
|
+
node.type == :block && node.children[0].children.size > 2 && node.children[0].children[1] == :step
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.refactor_args(step_text, ast, param_positions, new_param_values, new_step_text)
|
71
|
+
new_ast = replace ast do |node|
|
72
|
+
if node.children[0].children[2].children[0] == step_text
|
73
|
+
process_node(node, param_positions, new_param_values, new_step_text)
|
74
|
+
else
|
75
|
+
node
|
76
|
+
end
|
77
|
+
end
|
78
|
+
Unparser.unparse new_ast
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.refactor(step_info, param_positions, new_step)
|
82
|
+
ast = code_to_ast File.read(step_info[:locations][0][:file])
|
83
|
+
refactor_args(step_info[:step_text], ast, param_positions, new_step.parameters, new_step.parameterizedStepValue)
|
72
84
|
end
|
73
85
|
|
74
86
|
private
|
75
87
|
|
76
88
|
def self.code_to_ast(code)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
89
|
+
begin
|
90
|
+
buffer = Parser::Source::Buffer.new '(string)'
|
91
|
+
buffer.source = code
|
92
|
+
parser = Parser::CurrentRuby.new
|
93
|
+
return parser.parse(buffer)
|
94
|
+
rescue Exception => e
|
95
|
+
Gauge::Log.error e.message
|
96
|
+
end
|
82
97
|
end
|
83
98
|
|
84
99
|
def self.include_args(code_string, params)
|
data/lib/connector.rb
CHANGED
@@ -22,22 +22,16 @@ module Gauge
|
|
22
22
|
# @api private
|
23
23
|
module Connector
|
24
24
|
GAUGE_PORT_ENV = "GAUGE_INTERNAL_PORT"
|
25
|
-
API_PORT_ENV = "GAUGE_API_PORT"
|
26
25
|
HOST_NAME = 'localhost'
|
27
26
|
@@executionSocket = nil
|
28
|
-
@@apiSocket = nil
|
29
27
|
|
30
|
-
def self.apiSocket
|
31
|
-
@@apiSocket
|
32
|
-
end
|
33
28
|
|
34
|
-
def self.
|
29
|
+
def self.execution_socket
|
35
30
|
@@executionSocket
|
36
31
|
end
|
37
32
|
|
38
|
-
def self.
|
39
|
-
@@executionSocket = TCPSocket.open(HOST_NAME, Runtime.
|
40
|
-
@@apiSocket = TCPSocket.open(HOST_NAME, Runtime.portFromEnvVariable(API_PORT_ENV))
|
33
|
+
def self.make_connection
|
34
|
+
@@executionSocket = TCPSocket.open(HOST_NAME, Runtime.port_from_env_variable(GAUGE_PORT_ENV))
|
41
35
|
end
|
42
36
|
|
43
37
|
def self.message_length(socket)
|
@@ -45,29 +39,7 @@ module Gauge
|
|
45
39
|
end
|
46
40
|
|
47
41
|
def self.step_value text
|
48
|
-
|
49
|
-
apiMessage = Gauge::Messages::APIMessage.new(:messageType => Gauge::Messages::APIMessage::APIMessageType::GetStepValueRequest, :stepValueRequest => stepValueRequest)
|
50
|
-
response = get_api_response(apiMessage)
|
51
|
-
if (response.messageType == Gauge::Messages::APIMessage::APIMessageType::ErrorResponse)
|
52
|
-
puts "[Error] Failed to load step implementation. #{response.error.error}: \"#{text}\""
|
53
|
-
return ''
|
54
|
-
end
|
55
|
-
return response.stepValueResponse.stepValue.stepValue
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.get_api_response(apiMessage)
|
59
|
-
apiMessage.messageId = get_unique_id
|
60
|
-
dataLen = apiMessage.serialize_to_string.bytesize
|
61
|
-
ProtocolBuffers::Varint.encode @@apiSocket, dataLen
|
62
|
-
apiMessage.serialize(@@apiSocket)
|
63
|
-
|
64
|
-
responseLen = message_length(@@apiSocket)
|
65
|
-
data = @@apiSocket.read responseLen
|
66
|
-
message = Gauge::Messages::APIMessage.parse(data)
|
67
|
-
end
|
68
|
-
|
69
|
-
def self.get_unique_id
|
70
|
-
rand(2**63-1)
|
42
|
+
return text.gsub(/(<.*?>)/, "{}")
|
71
43
|
end
|
72
44
|
end
|
73
45
|
end
|
data/lib/executor.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2018 ThoughtWorks, Inc.
|
2
2
|
|
3
3
|
# This file is part of Gauge-Ruby.
|
4
4
|
|
@@ -14,28 +14,47 @@
|
|
14
14
|
|
15
15
|
# You should have received a copy of the GNU General Public License
|
16
16
|
# along with Gauge-Ruby. If not, see <http://www.gnu.org/licenses/>.
|
17
|
-
|
17
|
+
require 'ruby-debug-ide'
|
18
18
|
require_relative 'gauge'
|
19
19
|
|
20
|
+
ATTACH_DEBUGGER_EVENT = "Runner Ready for Debugging"
|
21
|
+
|
20
22
|
module Gauge
|
23
|
+
class DebugOptions
|
24
|
+
attr_accessor :host, :port, :notify_dispatcher
|
25
|
+
end
|
26
|
+
|
21
27
|
# @api private
|
22
28
|
module Executor
|
23
|
-
def self.load_steps(
|
24
|
-
|
29
|
+
def self.load_steps(dir)
|
30
|
+
start_debugger
|
31
|
+
Dir["#{dir}/**/*.rb"].each do |x|
|
25
32
|
begin
|
33
|
+
ENV['GAUGE_STEP_FILE'] = x
|
26
34
|
require x
|
27
35
|
rescue Exception => e
|
28
|
-
|
36
|
+
Gauge::Log.error "[ERROR] Cannot import #{x}. Reason: #{e.message}"
|
29
37
|
end
|
30
|
-
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.start_debugger
|
42
|
+
if ENV['DEBUGGING']
|
43
|
+
options = DebugOptions.new
|
44
|
+
options.host = '127.0.0.1'
|
45
|
+
options.port = ENV["DEBUG_PORT"].to_i
|
46
|
+
options.notify_dispatcher = false
|
47
|
+
Gauge::Log.info ATTACH_DEBUGGER_EVENT
|
48
|
+
Debugger.prepare_debugger(options)
|
49
|
+
end
|
31
50
|
end
|
32
51
|
|
33
52
|
def self.execute_step(step, args)
|
34
|
-
|
53
|
+
si = MethodCache.get_step_info step
|
35
54
|
if args.size == 1
|
36
|
-
block.call(args[0])
|
55
|
+
si[:block].call(args[0])
|
37
56
|
else
|
38
|
-
block.call(args)
|
57
|
+
si[:block].call(args)
|
39
58
|
end
|
40
59
|
end
|
41
60
|
|
data/lib/gauge.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2018 ThoughtWorks, Inc.
|
2
2
|
|
3
3
|
# This file is part of Gauge-Ruby.
|
4
4
|
|
@@ -32,7 +32,7 @@ module Kernel
|
|
32
32
|
# puts "I am the $1 hook"
|
33
33
|
# end
|
34
34
|
def hook(hook)
|
35
|
-
define_method hook do |options={}, &block|
|
35
|
+
define_method hook do |options = {}, &block|
|
36
36
|
Gauge::MethodCache.send("add_#{hook}_hook".to_sym, options, &block)
|
37
37
|
end
|
38
38
|
end
|
@@ -104,31 +104,32 @@ module Kernel
|
|
104
104
|
# @param block [block] the implementation block for given step.
|
105
105
|
def step(*args, &block)
|
106
106
|
opts = args.select {|x| x.is_a? Hash}
|
107
|
-
step_texts = args-opts
|
108
|
-
opts = {:
|
107
|
+
step_texts = args - opts
|
108
|
+
opts = { continue_on_failure: false }.merge opts.reduce({}, :merge)
|
109
109
|
step_texts.each do |text|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
110
|
+
step_value = Gauge::Connector.step_value(text)
|
111
|
+
si = { location: { file: ENV['GAUGE_STEP_FILE'], span: {} },
|
112
|
+
block: block, step_text: text,
|
113
|
+
recoverable: opts[:continue_on_failure] }
|
114
|
+
Gauge::MethodCache.add_step(step_value, si)
|
114
115
|
end
|
115
116
|
Gauge::MethodCache.add_step_alias(*step_texts)
|
116
117
|
end
|
117
118
|
|
118
119
|
# Invoked before execution of every step.
|
119
|
-
tagged_hook
|
120
|
+
tagged_hook 'before_step'
|
120
121
|
# Invoked after execution of every step.
|
121
|
-
tagged_hook
|
122
|
+
tagged_hook 'after_step'
|
122
123
|
# Invoked before execution of every specification.
|
123
|
-
tagged_hook
|
124
|
+
tagged_hook 'before_spec'
|
124
125
|
# Invoked after execution of every specification.
|
125
|
-
tagged_hook
|
126
|
+
tagged_hook 'after_spec'
|
126
127
|
# Invoked before execution of every scenario.
|
127
|
-
tagged_hook
|
128
|
+
tagged_hook 'before_scenario'
|
128
129
|
# Invoked after execution of every scenario.
|
129
|
-
tagged_hook
|
130
|
+
tagged_hook 'after_scenario'
|
130
131
|
# Invoked before execution of the entire suite.
|
131
|
-
hook
|
132
|
+
hook 'before_suite'
|
132
133
|
# Invoked after execution of the entire suite.
|
133
|
-
hook
|
134
|
+
hook 'after_suite'
|
134
135
|
end
|
data/lib/gauge_runtime.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2018 ThoughtWorks, Inc.
|
2
2
|
|
3
3
|
# This file is part of Gauge-Ruby.
|
4
4
|
|
@@ -20,14 +20,17 @@ require 'protocol_buffers'
|
|
20
20
|
|
21
21
|
require_relative 'messages.pb'
|
22
22
|
require_relative 'executor'
|
23
|
+
require_relative 'static_loader'
|
23
24
|
require_relative 'connector'
|
24
25
|
require_relative 'message_processor'
|
26
|
+
require_relative 'util'
|
27
|
+
require_relative 'log'
|
25
28
|
|
26
29
|
|
27
30
|
module Gauge
|
28
31
|
# @api private
|
29
32
|
module Runtime
|
30
|
-
DEFAULT_IMPLEMENTATIONS_DIR_PATH =
|
33
|
+
DEFAULT_IMPLEMENTATIONS_DIR_PATH = Util.get_step_implementation_dir
|
31
34
|
|
32
35
|
def self.dispatch_messages(socket)
|
33
36
|
while (!socket.eof?)
|
@@ -35,7 +38,7 @@ module Gauge
|
|
35
38
|
data = socket.read len
|
36
39
|
message = Messages::Message.parse(data)
|
37
40
|
handle_message(socket, message)
|
38
|
-
if
|
41
|
+
if message.messageType == Messages::Message::MessageType::KillProcessRequest || message.messageType == Messages::Message::MessageType::ExecutionEnding
|
39
42
|
socket.close
|
40
43
|
return
|
41
44
|
end
|
@@ -44,14 +47,14 @@ module Gauge
|
|
44
47
|
|
45
48
|
|
46
49
|
def self.handle_message(socket, message)
|
47
|
-
if
|
48
|
-
|
50
|
+
if !MessageProcessor.is_valid_message(message)
|
51
|
+
Gauge::Log.error "Invalid message received : #{message}"
|
49
52
|
execution_status_response = Messages::ExecutionStatusResponse.new(:executionResult => Messages::ProtoExecutionResult.new(:failed => true, :executionTime => 0))
|
50
53
|
message = Messages::Message.new(:messageType => Messages::Message::MessageType::ExecutionStatusResponse, :messageId => message.messageId, :executionStatusResponse => execution_status_response)
|
51
54
|
write_message(socket, message)
|
52
55
|
else
|
53
56
|
response = MessageProcessor.process_message message
|
54
|
-
write_message(socket, response)
|
57
|
+
write_message(socket, response) if response
|
55
58
|
end
|
56
59
|
end
|
57
60
|
|
@@ -62,18 +65,19 @@ module Gauge
|
|
62
65
|
socket.write serialized_message
|
63
66
|
end
|
64
67
|
|
65
|
-
def self.
|
66
|
-
port = ENV[
|
67
|
-
if
|
68
|
-
raise RuntimeError, "Could not find Env variable :#{
|
68
|
+
def self.port_from_env_variable(env_variable)
|
69
|
+
port = ENV[env_variable]
|
70
|
+
if port.nil?
|
71
|
+
raise RuntimeError, "Could not find Env variable :#{env_variable}"
|
69
72
|
end
|
70
73
|
return port
|
71
74
|
end
|
72
75
|
|
73
76
|
STDOUT.sync = true
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
+
GaugeLog.init
|
78
|
+
Connector.make_connection
|
79
|
+
StaticLoader.load_files(DEFAULT_IMPLEMENTATIONS_DIR_PATH)
|
80
|
+
dispatch_messages(Connector.execution_socket)
|
77
81
|
exit(0)
|
78
82
|
end
|
79
83
|
end
|
data/lib/log.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright 2018 ThoughtWorks, Inc.
|
2
|
+
|
3
|
+
# This file is part of Gauge-Ruby.
|
4
|
+
|
5
|
+
# Gauge-Ruby is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
|
10
|
+
# Gauge-Ruby is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with Gauge-Ruby. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
require 'logger'
|
19
|
+
|
20
|
+
module Gauge
|
21
|
+
Log = Logger.new(STDOUT)
|
22
|
+
module GaugeLog
|
23
|
+
def self.init()
|
24
|
+
Log.formatter = proc do |severity, datetime, progname, msg|
|
25
|
+
if ENV['IS_DAEMON']
|
26
|
+
"#{datetime.strftime('%H:%M:%S.%L')} #{msg}\n"
|
27
|
+
else
|
28
|
+
"#{msg}\n"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|