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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -0
  3. data/Gemfile +1 -0
  4. data/README.md +23 -0
  5. data/Rakefile +15 -4
  6. data/bin/bash_stub.sh +92 -0
  7. data/bin/bash_wrapper.sh.erb +12 -0
  8. data/bin/ruby_stub.rb +33 -0
  9. data/lib/rspec/bash.rb +5 -4
  10. data/lib/rspec/bash/command.rb +5 -0
  11. data/lib/rspec/bash/command/call_configuration.rb +76 -0
  12. data/lib/rspec/bash/command/call_configuration_manager.rb +24 -0
  13. data/lib/rspec/bash/command/call_log.rb +48 -0
  14. data/lib/rspec/bash/command/call_log_manager.rb +38 -0
  15. data/lib/rspec/bash/command/stubbed_command.rb +64 -0
  16. data/lib/rspec/bash/server.rb +3 -0
  17. data/lib/rspec/bash/server/bash_stub_marshaller.rb +19 -0
  18. data/lib/rspec/bash/server/ruby_stub_marshaller.rb +13 -0
  19. data/lib/rspec/bash/server/stub_server.rb +47 -0
  20. data/lib/rspec/bash/stubbed_env.rb +75 -54
  21. data/lib/rspec/bash/util/call_conf_argument_list_matcher.rb +5 -5
  22. data/lib/rspec/bash/util/call_log_argument_list_matcher.rb +1 -1
  23. data/lib/rspec/bash/wrapper.rb +4 -0
  24. data/lib/rspec/bash/wrapper/bash_stub_script.rb +15 -0
  25. data/lib/rspec/bash/wrapper/bash_wrapper.rb +54 -0
  26. data/lib/rspec/bash/wrapper/ruby_stub_script.rb +15 -0
  27. data/lib/rspec/bash/wrapper/stub_function.rb +36 -0
  28. data/rspec-bash.gemspec +2 -1
  29. data/spec/classes/command/call_configuration_manager_spec.rb +68 -0
  30. data/spec/classes/{call_configuration_spec.rb → command/call_configuration_spec.rb} +51 -114
  31. data/spec/classes/command/call_log_manager_spec.rb +83 -0
  32. data/spec/classes/{call_log_spec.rb → command/call_log_spec.rb} +23 -82
  33. data/spec/classes/command/stubbed_command_spec.rb +118 -0
  34. data/spec/classes/server/bash_stub_marshaller_spec.rb +38 -0
  35. data/spec/classes/server/ruby_stub_marshaller_spec.rb +31 -0
  36. data/spec/classes/server/stub_server_spec.rb +121 -0
  37. data/spec/classes/stubbed_env_spec.rb +141 -280
  38. data/spec/classes/util/call_conf_argument_list_matcher_spec.rb +17 -17
  39. data/spec/classes/util/call_log_argument_list_matcher_spec.rb +24 -18
  40. data/spec/classes/wrapper/bash_wrapper_spec.rb +37 -0
  41. data/spec/classes/wrapper/ruby_stub_script_spec.rb +204 -0
  42. data/spec/helper/string_file_io.rb +1 -1
  43. data/spec/integration/call_log/called_with_args_spec.rb +8 -4
  44. data/spec/integration/call_log/called_with_no_args_spec.rb +1 -1
  45. data/spec/integration/call_log/stdin_spec.rb +10 -4
  46. data/spec/integration/edge_cases_spec.rb +34 -0
  47. data/spec/integration/matchers/be_called_with_arguments_spec.rb +12 -13
  48. data/spec/integration/matchers/be_called_with_no_arguments_spec.rb +6 -7
  49. data/spec/integration/stubbed_command/outputs_spec.rb +111 -91
  50. data/spec/integration/stubbed_command/returns_exitstatus_spec.rb +46 -37
  51. data/spec/integration/stubbed_env/execute_with_env_vars_spec.rb +3 -4
  52. data/spec/integration/stubbed_env/execute_with_path_spec.rb +6 -7
  53. data/spec/integration/stubbed_env/execute_with_stub_wrapper_spec.rb +4 -12
  54. data/spec/integration/stubbed_env/override_spec.rb +354 -0
  55. data/spec/integration/wrapper/bash_stub_script_spec.rb +383 -0
  56. data/spec/integration/wrapper/bash_wrapper_spec.rb +48 -0
  57. data/spec/scripts/function_library.sh +9 -1
  58. data/spec/spec_helper.rb +2 -0
  59. metadata +65 -21
  60. data/bin/function_override.sh.erb +0 -7
  61. data/bin/function_override_wrapper.sh.erb +0 -19
  62. data/bin/stub.rb.erb +0 -56
  63. data/lib/rspec/bash/call_configuration.rb +0 -62
  64. data/lib/rspec/bash/call_log.rb +0 -71
  65. data/lib/rspec/bash/stubbed_command.rb +0 -88
  66. data/spec/classes/stub_spec.rb +0 -510
  67. data/spec/classes/stubbed_command_spec.rb +0 -134
  68. 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,3 @@
1
+ require 'rspec/bash/server/bash_stub_marshaller'
2
+ require 'rspec/bash/server/ruby_stub_marshaller'
3
+ require 'rspec/bash/server/stub_server'
@@ -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,13 @@
1
+ module Rspec
2
+ module Bash
3
+ class RubyStubMarshaller
4
+ def unmarshal(message_to_unmarshal)
5
+ Marshal.load(message_to_unmarshal)
6
+ end
7
+
8
+ def marshal(object_to_marshal)
9
+ Marshal.dump(object_to_marshal)
10
+ end
11
+ end
12
+ end
13
+ 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.new
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
- attr_reader :dir
15
+ RUBY_STUB = :ruby_stub
16
+ BASH_STUB = :bash_stub
16
17
 
17
- def initialize
18
- @dir = Dir.mktmpdir
19
- at_exit { cleanup }
18
+ def initialize(stub_type)
19
+ start_stub_server(stub_type)
20
20
  end
21
21
 
22
- def cleanup
23
- FileUtils.remove_entry_secure @dir if Pathname.new(@dir).exist?
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
- write_function_override_file_for_command command
28
- StubbedCommand.new command, @dir
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
- full_command = get_wrapped_execution_with_function_overrides(
33
- <<-multiline_script
34
- source #{command}
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
- full_command = get_wrapped_execution_with_function_overrides(
43
- <<-multiline_script
44
- source #{script}
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
- temp_command_path = Dir::Tmpname.make_tmpname("#{@dir}/inline-", nil)
54
- File.write(temp_command_path, command_string)
55
- execute(temp_command_path, env_vars)
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
- def write_function_override_file_for_command(command)
61
- function_command_binding_for_template = command
62
- function_command_path_binding_for_template = File.join(@dir, command)
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
- function_override_file_path = File.join(@dir, "#{command}_overrides.sh")
65
- function_override_file_template = ERB.new(
66
- File.new(function_override_template_path).read, nil, '%'
67
- )
68
- function_override_file_content = function_override_file_template.result(binding)
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
- File.write(function_override_file_path, function_override_file_content)
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 get_wrapped_execution_with_function_overrides(execution_snippet)
74
- execution_binding_for_template = execution_snippet
75
- function_override_path_binding_for_template = "#{@dir}/*_overrides.sh"
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
- function_override_wrapper_template = ERB.new(
79
- File.new(function_override_wrapper_template_path).read, nil, '%'
80
- )
95
+ def wrap_script(script)
96
+ @stub_wrapper.wrap_script(script)
97
+ end
81
98
 
82
- function_override_wrapper_template.result(binding)
99
+ def delete_file(file_path)
100
+ File.delete(file_path)
83
101
  end
84
102
 
85
- def function_override_template_path
86
- project_root.join('bin', 'function_override.sh.erb')
103
+ def write_file(file_path, contents)
104
+ File.write(file_path, contents)
87
105
  end
88
106
 
89
- def function_override_wrapper_template_path
90
- project_root.join('bin', 'function_override_wrapper.sh.erb')
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 project_root
94
- Pathname.new(File.dirname(File.expand_path(__FILE__))).join('..', '..', '..')
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?(*call_arguments)
17
- !get_call_conf_matches(*call_arguments).empty?
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(*call_arguments)
21
- get_call_conf_matches(*call_arguments).sort_by do |call_conf|
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(*call_arguments)
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(*expected_args)
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,4 @@
1
+ require 'rspec/bash/wrapper/bash_stub_script'
2
+ require 'rspec/bash/wrapper/ruby_stub_script'
3
+ require 'rspec/bash/wrapper/bash_wrapper'
4
+ require 'rspec/bash/wrapper/stub_function'
@@ -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