executable_mock 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/executable_mock/registry.rb +23 -0
- data/lib/executable_mock/stdlib.rb +6 -0
- data/lib/executable_mock/template.rb.erb +34 -0
- data/lib/executable_mock/version.rb +5 -0
- data/lib/executable_mock.rb +106 -0
- metadata +51 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 89e54f80480d9ec55c308051dea7c922eedfc79d0a1e8f8a33e7437d72890a28
|
4
|
+
data.tar.gz: 841ee2e10667111ff0b8ef3e40506b4960e17f408f139805140181884c66eb92
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f2085220dcf0c2e981ec4be0e30d5777fab09cd80a65d462737ec153c4f0aa29d67ddbb6ad2229281ad50c0c75ac9b88f25f74affb3b65d11dfbcca358e0f5c6
|
7
|
+
data.tar.gz: d6b243b5cbc886e1badf5ba30d617e7c7d7ee335b9e1388ed7cf2a946bda4ac5b532360e13fe0d98217b71f1579d5d243a28e6bf3ff063dc6f7ba56dac673003
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ExecutableMock
|
4
|
+
module Registry
|
5
|
+
def self.included(klass)
|
6
|
+
klass.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def registry
|
11
|
+
Thread.current[:executable_mocks] ||= Set.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def register_self
|
16
|
+
self.class.registry << self
|
17
|
+
end
|
18
|
+
|
19
|
+
def deregister_self
|
20
|
+
self.class.registry.delete(self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!<%= ruby_bin %>
|
2
|
+
|
3
|
+
# Mappings
|
4
|
+
|
5
|
+
mappings = <%= mappings %>
|
6
|
+
argv_key = ARGV.join(" ")
|
7
|
+
unless mappings.key?(argv_key)
|
8
|
+
File.open("<%= call_error_log_file_path %>", "w") do |file|
|
9
|
+
file.write("The executable `<%= name %>` does not support these args:\n#{argv_key}")
|
10
|
+
file.write("\n\nSupported:\n<%= mappings.keys.join("\n") %>")
|
11
|
+
end
|
12
|
+
puts "ExecutableMock: Unsupported arguments"
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
# Counter cache handler
|
17
|
+
|
18
|
+
call_count = nil
|
19
|
+
File.open("<%= counter_cache_path %>", "a+") do |file|
|
20
|
+
file.rewind
|
21
|
+
counter_cache = Marshal.load(file.read)
|
22
|
+
call_count = counter_cache[argv_key]
|
23
|
+
counter_cache[argv_key] += 1
|
24
|
+
file.truncate(0)
|
25
|
+
file.write(Marshal.dump(counter_cache))
|
26
|
+
end
|
27
|
+
|
28
|
+
# Output
|
29
|
+
|
30
|
+
if mappings[argv_key].is_a?(Array)
|
31
|
+
print mappings[argv_key].fetch(call_count)
|
32
|
+
else
|
33
|
+
print mappings[argv_key]
|
34
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "executable_mock/stdlib"
|
4
|
+
require "executable_mock/registry"
|
5
|
+
|
6
|
+
class ExecutableMock
|
7
|
+
include Registry
|
8
|
+
TEMPLATE_PATH = File.expand_path("executable_mock/template.rb.erb", __dir__)
|
9
|
+
TEMPLATE = ERB.new(File.read(TEMPLATE_PATH))
|
10
|
+
Error = Class.new(StandardError)
|
11
|
+
|
12
|
+
attr_reader :file_path, :path_setup
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def generate(name, mappings, ruby_bin: RbConfig.ruby, directory: Dir.mktmpdir)
|
16
|
+
instance = new(name, mappings, ruby_bin: ruby_bin, directory: directory)
|
17
|
+
|
18
|
+
yield(instance).tap do |result|
|
19
|
+
instance.finalize(result)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def finalize_all
|
24
|
+
registry.each(&:finalize)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(name, mappings, ruby_bin: RbConfig.ruby, directory: Dir.mktmpdir)
|
29
|
+
@mappings = mappings
|
30
|
+
@ruby_bin = ruby_bin
|
31
|
+
@name = name
|
32
|
+
@file_path = File.join(directory, name)
|
33
|
+
@path_setup = %(PATH="#{directory}:$PATH")
|
34
|
+
@call_error_log_file_path = Tempfile.new.path
|
35
|
+
|
36
|
+
write_executable
|
37
|
+
register_self
|
38
|
+
end
|
39
|
+
|
40
|
+
def finalize(command_result = nil)
|
41
|
+
called_argvs_map = Marshal.load(File.read(counter_cache_path)) # rubocop:disable Security/MarshalLoad
|
42
|
+
check_call_error_log_file
|
43
|
+
check_uncalled_argvs(called_argvs_map)
|
44
|
+
check_mismatched_argvs_calls(called_argvs_map)
|
45
|
+
rescue Error
|
46
|
+
puts command_result if command_result
|
47
|
+
raise
|
48
|
+
ensure
|
49
|
+
FileUtils.rm_f([@file_path, @call_error_log_file_path, counter_cache_path])
|
50
|
+
deregister_self
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_call_error_log_file
|
54
|
+
return unless File.size?(@call_error_log_file_path)
|
55
|
+
|
56
|
+
raise(Error, File.read(@call_error_log_file_path))
|
57
|
+
end
|
58
|
+
|
59
|
+
def check_uncalled_argvs(argvs_map)
|
60
|
+
uncalled_argvs = argvs_map.select { |_, v| v.zero? }
|
61
|
+
|
62
|
+
raise(Error, <<~MESSAGE) if uncalled_argvs.any?
|
63
|
+
The following argvs were not called:
|
64
|
+
#{uncalled_argvs.keys.join("\n")}
|
65
|
+
MESSAGE
|
66
|
+
end
|
67
|
+
|
68
|
+
def check_mismatched_argvs_calls(argvs_map)
|
69
|
+
mismatched_argvs_calls = @mappings.select do |argv, outputs|
|
70
|
+
outputs.is_a?(Array) && outputs.size != argvs_map[argv]
|
71
|
+
end
|
72
|
+
|
73
|
+
raise(Error, <<~MESSAGE) if mismatched_argvs_calls.any?
|
74
|
+
The following argvs were not called the correct number of times:
|
75
|
+
#{mismatched_argvs_calls.inspect}
|
76
|
+
MESSAGE
|
77
|
+
end
|
78
|
+
|
79
|
+
def counter_cache_path
|
80
|
+
@counter_cache_path ||= begin
|
81
|
+
Tempfile.new.path.tap do |file_path|
|
82
|
+
data = Marshal.dump(@mappings.transform_values { 0 })
|
83
|
+
File.open(file_path, "w") do |file|
|
84
|
+
file.write(data)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def write_executable
|
91
|
+
bindings = {
|
92
|
+
ruby_bin: @ruby_bin,
|
93
|
+
mappings: @mappings,
|
94
|
+
name: @name,
|
95
|
+
call_error_log_file_path: @call_error_log_file_path,
|
96
|
+
counter_cache_path: counter_cache_path
|
97
|
+
}
|
98
|
+
|
99
|
+
executable_contents = TEMPLATE.result_with_hash(bindings)
|
100
|
+
|
101
|
+
File.open(@file_path, "w") do |file|
|
102
|
+
file.write(executable_contents)
|
103
|
+
end
|
104
|
+
File.chmod(0o755, @file_path)
|
105
|
+
end
|
106
|
+
end
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: executable_mock
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christophe Maximin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-12-03 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: PLACEHOLDER
|
14
|
+
email:
|
15
|
+
- christophe.maximin@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/executable_mock.rb
|
21
|
+
- lib/executable_mock/registry.rb
|
22
|
+
- lib/executable_mock/stdlib.rb
|
23
|
+
- lib/executable_mock/template.rb.erb
|
24
|
+
- lib/executable_mock/version.rb
|
25
|
+
homepage: https://github.com/christophemaximin/executable_mock
|
26
|
+
licenses: []
|
27
|
+
metadata:
|
28
|
+
homepage_uri: https://github.com/christophemaximin/executable_mock
|
29
|
+
source_code_uri: https://github.com/christophemaximin/executable_mock
|
30
|
+
changelog_uri: https://github.com/christophemaximin/executable_mock/CHANGELOG.md
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.6'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 3.0.0.beta2
|
48
|
+
signing_key:
|
49
|
+
specification_version: 4
|
50
|
+
summary: PLACEHOLDER
|
51
|
+
test_files: []
|