rspec-bash 0.1.1 → 0.2.0
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/.rubocop.yml +8 -0
- data/Gemfile +1 -0
- data/README.md +23 -0
- data/Rakefile +15 -4
- data/bin/bash_stub.sh +92 -0
- data/bin/bash_wrapper.sh.erb +12 -0
- data/bin/ruby_stub.rb +33 -0
- data/lib/rspec/bash.rb +5 -4
- data/lib/rspec/bash/command.rb +5 -0
- data/lib/rspec/bash/command/call_configuration.rb +76 -0
- data/lib/rspec/bash/command/call_configuration_manager.rb +24 -0
- data/lib/rspec/bash/command/call_log.rb +48 -0
- data/lib/rspec/bash/command/call_log_manager.rb +38 -0
- data/lib/rspec/bash/command/stubbed_command.rb +64 -0
- data/lib/rspec/bash/server.rb +3 -0
- data/lib/rspec/bash/server/bash_stub_marshaller.rb +19 -0
- data/lib/rspec/bash/server/ruby_stub_marshaller.rb +13 -0
- data/lib/rspec/bash/server/stub_server.rb +47 -0
- data/lib/rspec/bash/stubbed_env.rb +75 -54
- data/lib/rspec/bash/util/call_conf_argument_list_matcher.rb +5 -5
- data/lib/rspec/bash/util/call_log_argument_list_matcher.rb +1 -1
- data/lib/rspec/bash/wrapper.rb +4 -0
- data/lib/rspec/bash/wrapper/bash_stub_script.rb +15 -0
- data/lib/rspec/bash/wrapper/bash_wrapper.rb +54 -0
- data/lib/rspec/bash/wrapper/ruby_stub_script.rb +15 -0
- data/lib/rspec/bash/wrapper/stub_function.rb +36 -0
- data/rspec-bash.gemspec +2 -1
- data/spec/classes/command/call_configuration_manager_spec.rb +68 -0
- data/spec/classes/{call_configuration_spec.rb → command/call_configuration_spec.rb} +51 -114
- data/spec/classes/command/call_log_manager_spec.rb +83 -0
- data/spec/classes/{call_log_spec.rb → command/call_log_spec.rb} +23 -82
- data/spec/classes/command/stubbed_command_spec.rb +118 -0
- data/spec/classes/server/bash_stub_marshaller_spec.rb +38 -0
- data/spec/classes/server/ruby_stub_marshaller_spec.rb +31 -0
- data/spec/classes/server/stub_server_spec.rb +121 -0
- data/spec/classes/stubbed_env_spec.rb +141 -280
- data/spec/classes/util/call_conf_argument_list_matcher_spec.rb +17 -17
- data/spec/classes/util/call_log_argument_list_matcher_spec.rb +24 -18
- data/spec/classes/wrapper/bash_wrapper_spec.rb +37 -0
- data/spec/classes/wrapper/ruby_stub_script_spec.rb +204 -0
- data/spec/helper/string_file_io.rb +1 -1
- data/spec/integration/call_log/called_with_args_spec.rb +8 -4
- data/spec/integration/call_log/called_with_no_args_spec.rb +1 -1
- data/spec/integration/call_log/stdin_spec.rb +10 -4
- data/spec/integration/edge_cases_spec.rb +34 -0
- data/spec/integration/matchers/be_called_with_arguments_spec.rb +12 -13
- data/spec/integration/matchers/be_called_with_no_arguments_spec.rb +6 -7
- data/spec/integration/stubbed_command/outputs_spec.rb +111 -91
- data/spec/integration/stubbed_command/returns_exitstatus_spec.rb +46 -37
- data/spec/integration/stubbed_env/execute_with_env_vars_spec.rb +3 -4
- data/spec/integration/stubbed_env/execute_with_path_spec.rb +6 -7
- data/spec/integration/stubbed_env/execute_with_stub_wrapper_spec.rb +4 -12
- data/spec/integration/stubbed_env/override_spec.rb +354 -0
- data/spec/integration/wrapper/bash_stub_script_spec.rb +383 -0
- data/spec/integration/wrapper/bash_wrapper_spec.rb +48 -0
- data/spec/scripts/function_library.sh +9 -1
- data/spec/spec_helper.rb +2 -0
- metadata +65 -21
- data/bin/function_override.sh.erb +0 -7
- data/bin/function_override_wrapper.sh.erb +0 -19
- data/bin/stub.rb.erb +0 -56
- data/lib/rspec/bash/call_configuration.rb +0 -62
- data/lib/rspec/bash/call_log.rb +0 -71
- data/lib/rspec/bash/stubbed_command.rb +0 -88
- data/spec/classes/stub_spec.rb +0 -510
- data/spec/classes/stubbed_command_spec.rb +0 -134
- data/spec/integration/assert_called_spec.rb +0 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module Rspec
|
4
|
+
module Bash
|
5
|
+
class StubbedCommand
|
6
|
+
attr_reader :command, :arguments
|
7
|
+
|
8
|
+
def initialize(command, call_log_manager, call_conf_manager)
|
9
|
+
@command = command
|
10
|
+
@arguments = []
|
11
|
+
@call_log_manager = call_log_manager
|
12
|
+
@call_conf_manager = call_conf_manager
|
13
|
+
end
|
14
|
+
|
15
|
+
def with_args(*args)
|
16
|
+
@arguments = args
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def called?
|
21
|
+
@call_log_manager.called_with_args?(@command, @arguments)
|
22
|
+
end
|
23
|
+
|
24
|
+
def called_with_no_args?
|
25
|
+
@call_log_manager.called_with_no_args?(@command)
|
26
|
+
end
|
27
|
+
|
28
|
+
def called_with_args?(*args)
|
29
|
+
@call_log_manager.called_with_args?(@command, args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def call_count(*args)
|
33
|
+
@call_log_manager.call_count(@command, args)
|
34
|
+
end
|
35
|
+
|
36
|
+
def stdin
|
37
|
+
@call_log_manager.stdin_for_args(@command, @arguments)
|
38
|
+
end
|
39
|
+
|
40
|
+
def returns_exitstatus(exitcode)
|
41
|
+
@call_conf_manager.set_exitcode(@command, exitcode, @arguments)
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def outputs(contents, to: :stdout)
|
46
|
+
@call_conf_manager.add_output(@command, contents, to, @arguments)
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def call_log
|
51
|
+
@call_log_manager.call_log(@command)
|
52
|
+
end
|
53
|
+
|
54
|
+
def inspect
|
55
|
+
if @arguments.any?
|
56
|
+
"<Stubbed #{@call_configuration.command.inspect} " \
|
57
|
+
"args: #{@arguments.join(' ').inspect}>"
|
58
|
+
else
|
59
|
+
"<Stubbed #{@call_configuration.command.inspect}>"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'sparsify'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Rspec
|
5
|
+
module Bash
|
6
|
+
class BashStubMarshaller
|
7
|
+
def unmarshal(message_to_unmarshal)
|
8
|
+
object_to_unflatten = JSON.parse(message_to_unmarshal)
|
9
|
+
unflattened_object = Sparsify.unsparse(object_to_unflatten)
|
10
|
+
JSON.parse(JSON.dump(unflattened_object), symbolize_names: true)
|
11
|
+
end
|
12
|
+
|
13
|
+
def marshal(object_to_marshal)
|
14
|
+
object_to_dump = Sparsify.sparse(object_to_marshal, sparse_array: true)
|
15
|
+
JSON.pretty_generate(object_to_dump, indent: '', space: '')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Rspec
|
4
|
+
module Bash
|
5
|
+
class StubServer
|
6
|
+
def initialize(call_log_manager, call_conf_manager, stub_marshaller)
|
7
|
+
@call_log_manager = call_log_manager
|
8
|
+
@call_conf_manager = call_conf_manager
|
9
|
+
@stub_marshaller = stub_marshaller
|
10
|
+
end
|
11
|
+
|
12
|
+
def start(tcp_server)
|
13
|
+
Thread.new do
|
14
|
+
accept_loop(tcp_server)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def accept_loop(tcp_server, loop_forever = true)
|
19
|
+
loop do
|
20
|
+
tcp_socket = tcp_server.accept
|
21
|
+
message = process(tcp_socket.read)
|
22
|
+
tcp_socket.write(message)
|
23
|
+
tcp_socket.close
|
24
|
+
break unless loop_forever
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def process(client_message)
|
29
|
+
client_call = @stub_marshaller.unmarshal(client_message)
|
30
|
+
server_conf = process_stub_call(client_call)
|
31
|
+
@stub_marshaller.marshal(server_conf)
|
32
|
+
end
|
33
|
+
|
34
|
+
def process_stub_call(stub_call)
|
35
|
+
@call_log_manager.add_log(
|
36
|
+
stub_call[:command],
|
37
|
+
stub_call[:stdin],
|
38
|
+
stub_call[:args]
|
39
|
+
)
|
40
|
+
@call_conf_manager.get_best_call_conf(
|
41
|
+
stub_call[:command],
|
42
|
+
stub_call[:args]
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -1,97 +1,118 @@
|
|
1
1
|
require 'tmpdir'
|
2
2
|
require 'English'
|
3
3
|
require 'open3'
|
4
|
+
require 'tempfile'
|
4
5
|
|
5
6
|
module Rspec
|
6
|
-
# Define stubbed environment to set and assert expectations
|
7
7
|
module Bash
|
8
|
-
def create_stubbed_env
|
9
|
-
StubbedEnv.
|
8
|
+
def create_stubbed_env(stub_type =
|
9
|
+
ENV.fetch('RSPEC_BASH_STUB_TYPE', StubbedEnv::BASH_STUB).to_sym)
|
10
|
+
|
11
|
+
StubbedEnv.new(stub_type)
|
10
12
|
end
|
11
13
|
|
12
|
-
# A shell environment that can manipulate behaviour
|
13
|
-
# of executables
|
14
14
|
class StubbedEnv
|
15
|
-
|
15
|
+
RUBY_STUB = :ruby_stub
|
16
|
+
BASH_STUB = :bash_stub
|
16
17
|
|
17
|
-
def initialize
|
18
|
-
|
19
|
-
at_exit { cleanup }
|
18
|
+
def initialize(stub_type)
|
19
|
+
start_stub_server(stub_type)
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
23
|
-
|
22
|
+
def start_stub_server(stub_type)
|
23
|
+
tcp_server = create_tcp_server
|
24
|
+
stub_server = create_stub_server(stub_type)
|
25
|
+
stub_server.start(tcp_server)
|
24
26
|
end
|
25
27
|
|
26
28
|
def stub_command(command)
|
27
|
-
|
28
|
-
|
29
|
+
check_if_command_is_allowed(command)
|
30
|
+
add_override_for_command(command)
|
31
|
+
create_stubbed_command(command)
|
29
32
|
end
|
30
33
|
|
31
34
|
def execute(command, env_vars = {})
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
multiline_script
|
36
|
-
)
|
37
|
-
|
38
|
-
Open3.capture3(env_vars, full_command)
|
35
|
+
script_runner = "source #{command}"
|
36
|
+
script_wrapper = wrap_script(script_runner)
|
37
|
+
execute_script(env_vars, script_wrapper)
|
39
38
|
end
|
40
39
|
|
41
40
|
def execute_function(script, command, env_vars = {})
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
#{command}
|
46
|
-
multiline_script
|
47
|
-
)
|
48
|
-
|
49
|
-
Open3.capture3(env_vars, full_command)
|
41
|
+
script_runner = "source #{script}\n#{command}"
|
42
|
+
script_wrapper = wrap_script(script_runner)
|
43
|
+
execute_script(env_vars, script_wrapper)
|
50
44
|
end
|
51
45
|
|
52
46
|
def execute_inline(command_string, env_vars = {})
|
53
|
-
|
54
|
-
|
55
|
-
|
47
|
+
temp_command_file = Tempfile.new('inline-')
|
48
|
+
temp_command_path = temp_command_file.path
|
49
|
+
write_file(temp_command_path, command_string)
|
50
|
+
stdout, stderr, status = execute(temp_command_path, env_vars)
|
51
|
+
delete_file(temp_command_path)
|
52
|
+
[stdout, stderr, status]
|
56
53
|
end
|
57
54
|
|
58
55
|
private
|
59
56
|
|
60
|
-
|
61
|
-
|
62
|
-
|
57
|
+
STUB_MARSHALLER_MAPPINGS = {
|
58
|
+
RUBY_STUB => RubyStubMarshaller,
|
59
|
+
BASH_STUB => BashStubMarshaller
|
60
|
+
}.freeze
|
61
|
+
STUB_SCRIPT_MAPPINGS = {
|
62
|
+
RUBY_STUB => RubyStubScript,
|
63
|
+
BASH_STUB => BashStubScript
|
64
|
+
}.freeze
|
65
|
+
DISALLOWED_COMMANDS = %w(/usr/bin/env bash readonly function).freeze
|
66
|
+
|
67
|
+
def create_tcp_server
|
68
|
+
tcp_server = TCPServer.new('localhost', 0)
|
69
|
+
@stub_server_port = tcp_server.addr[1]
|
70
|
+
tcp_server
|
71
|
+
end
|
63
72
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
)
|
68
|
-
|
73
|
+
def create_stub_server(stub_type)
|
74
|
+
stub_marshaller = STUB_MARSHALLER_MAPPINGS[stub_type].new
|
75
|
+
stub_script_class = STUB_SCRIPT_MAPPINGS[stub_type]
|
76
|
+
@stub_wrapper = BashWrapper.new(@stub_server_port)
|
77
|
+
@stub_function = StubFunction.new(@stub_server_port, stub_script_class)
|
78
|
+
@call_log_manager = CallLogManager.new
|
79
|
+
@call_conf_manager = CallConfigurationManager.new
|
80
|
+
StubServer.new(@call_log_manager, @call_conf_manager, stub_marshaller)
|
81
|
+
end
|
69
82
|
|
70
|
-
|
83
|
+
def create_stubbed_command(command)
|
84
|
+
StubbedCommand.new(
|
85
|
+
command,
|
86
|
+
@call_log_manager,
|
87
|
+
@call_conf_manager
|
88
|
+
)
|
71
89
|
end
|
72
90
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
wrapped_error_path_binding_for_template = "#{@dir}/errors"
|
91
|
+
def execute_script(env_vars, script)
|
92
|
+
Open3.capture3(env_vars, script)
|
93
|
+
end
|
77
94
|
|
78
|
-
|
79
|
-
|
80
|
-
|
95
|
+
def wrap_script(script)
|
96
|
+
@stub_wrapper.wrap_script(script)
|
97
|
+
end
|
81
98
|
|
82
|
-
|
99
|
+
def delete_file(file_path)
|
100
|
+
File.delete(file_path)
|
83
101
|
end
|
84
102
|
|
85
|
-
def
|
86
|
-
|
103
|
+
def write_file(file_path, contents)
|
104
|
+
File.write(file_path, contents)
|
87
105
|
end
|
88
106
|
|
89
|
-
def
|
90
|
-
|
107
|
+
def check_if_command_is_allowed(command)
|
108
|
+
error_message = "Not able to stub command #{command}. Reserved for use by test wrapper."
|
109
|
+
|
110
|
+
raise(error_message) if DISALLOWED_COMMANDS.include? command
|
91
111
|
end
|
92
112
|
|
93
|
-
def
|
94
|
-
|
113
|
+
def add_override_for_command(command)
|
114
|
+
function_override = @stub_function.script(command).chomp
|
115
|
+
@stub_wrapper.add_override(function_override)
|
95
116
|
end
|
96
117
|
end
|
97
118
|
end
|
@@ -13,19 +13,19 @@ module Rspec
|
|
13
13
|
@expected_call_conf_list = call_conf_list
|
14
14
|
end
|
15
15
|
|
16
|
-
def args_match?(
|
17
|
-
!get_call_conf_matches(
|
16
|
+
def args_match?(call_arguments)
|
17
|
+
!get_call_conf_matches(call_arguments).empty?
|
18
18
|
end
|
19
19
|
|
20
|
-
def get_best_call_conf(
|
21
|
-
get_call_conf_matches(
|
20
|
+
def get_best_call_conf(call_arguments)
|
21
|
+
get_call_conf_matches(call_arguments).sort_by do |call_conf|
|
22
22
|
[
|
23
23
|
call_conf[:args].length
|
24
24
|
]
|
25
25
|
end.last || {}
|
26
26
|
end
|
27
27
|
|
28
|
-
def get_call_conf_matches(
|
28
|
+
def get_call_conf_matches(call_arguments)
|
29
29
|
@expected_call_conf_list.select do |expected_call_conf|
|
30
30
|
@expected_args = remap_argument_matchers(expected_call_conf[:args])
|
31
31
|
parent_args_match?(*call_arguments)
|
@@ -9,7 +9,7 @@ module Rspec
|
|
9
9
|
alias parent_args_match? args_match?
|
10
10
|
alias parent_initialize initialize
|
11
11
|
|
12
|
-
def initialize(
|
12
|
+
def initialize(expected_args = [any_args])
|
13
13
|
expected_args = expected_args.empty? ? [any_args] : expected_args
|
14
14
|
parent_initialize(*expected_args)
|
15
15
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Rspec
|
2
|
+
module Bash
|
3
|
+
class BashStubScript
|
4
|
+
def self.path
|
5
|
+
File.join(project_root, 'bin', 'bash_stub.sh')
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.project_root
|
9
|
+
File.expand_path(
|
10
|
+
File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', '..', '..')
|
11
|
+
)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Rspec
|
2
|
+
module Bash
|
3
|
+
class BashWrapper
|
4
|
+
def initialize(port)
|
5
|
+
@port = port
|
6
|
+
@override_list = []
|
7
|
+
at_exit { cleanup }
|
8
|
+
end
|
9
|
+
|
10
|
+
def wrap_script(script)
|
11
|
+
wrapper_template = ERB.new(File.new(wrapper_input_path).read, nil, '%')
|
12
|
+
File.open(wrapper_output_path, 'w') do |file|
|
13
|
+
file.write(wrapper_template.result(binding))
|
14
|
+
end
|
15
|
+
File.chmod(0755, wrapper_output_path)
|
16
|
+
|
17
|
+
wrapper_output_path
|
18
|
+
end
|
19
|
+
|
20
|
+
def wrapper_input_path
|
21
|
+
File.join(project_root, 'bin', 'bash_wrapper.sh.erb')
|
22
|
+
end
|
23
|
+
|
24
|
+
def wrapper_output_path
|
25
|
+
File.join(Dir.tmpdir, "wrapper-#{@port}.sh")
|
26
|
+
end
|
27
|
+
|
28
|
+
def stderr_output_path
|
29
|
+
File.join(Dir.tmpdir, "stderr-#{@port}.tmp")
|
30
|
+
end
|
31
|
+
|
32
|
+
def cleanup
|
33
|
+
remove_file_system_path wrapper_output_path
|
34
|
+
remove_file_system_path stderr_output_path
|
35
|
+
end
|
36
|
+
|
37
|
+
def remove_file_system_path(path)
|
38
|
+
FileUtils.remove_entry_secure(path) if Pathname.new(path).exist?
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_override(override)
|
42
|
+
@override_list << override
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def project_root
|
48
|
+
File.expand_path(
|
49
|
+
File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', '..', '..')
|
50
|
+
)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Rspec
|
2
|
+
module Bash
|
3
|
+
class RubyStubScript
|
4
|
+
def self.path
|
5
|
+
File.join(project_root, 'bin', 'ruby_stub.rb')
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.project_root
|
9
|
+
File.expand_path(
|
10
|
+
File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', '..', '..')
|
11
|
+
)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|