gauge-ruby 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|