ecomdev-chefspec 0.1.2 → 0.1.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/.gitignore +1 -0
- data/README.md +44 -0
- data/ecomdev-chefspec.gemspec +1 -0
- data/lib/ecomdev/chefspec/api/helpers/platform.rb +20 -0
- data/lib/ecomdev/chefspec/api/helpers/runner.rb +1 -137
- data/lib/ecomdev/chefspec/api/matchers/multiline_string.rb +12 -22
- data/lib/ecomdev/chefspec/api/stubs/file_system.rb +0 -1
- data/lib/ecomdev/chefspec/api.rb +3 -1
- data/lib/ecomdev/chefspec/helpers/platform.rb +184 -0
- data/lib/ecomdev/chefspec/helpers/runner_proxy.rb +137 -0
- data/lib/ecomdev/chefspec/helpers/string_matcher.rb +19 -0
- data/lib/ecomdev/chefspec/helpers.rb +9 -0
- data/lib/ecomdev/chefspec/resource/matcher/dsl.rb +3 -1
- data/lib/ecomdev/chefspec/version.rb +1 -1
- data/lib/ecomdev/chefspec.rb +1 -0
- data/spec/unit/api/helpers/platform_spec.rb +35 -0
- data/spec/unit/api/helpers/platforms.json +16 -0
- data/spec/unit/api/helpers/runner_spec.rb +1 -1
- data/spec/unit/helpers/platform_spec.rb +193 -0
- data/spec/unit/{api/helpers/runner/proxy_spec.rb → helpers/runner_proxy_spec.rb} +1 -1
- metadata +29 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee3608b8e1aab45a9fa3e0aa5a56321e1347c6a6
|
4
|
+
data.tar.gz: a625c05596c7a54a438c6861a4bdb04957227fb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 889c672f43c8afdfc5b61dc91b3235d415c5d038a27280c562ce772144ba8cbd6452ddc6464ed62ad2accf25db2cbda78e682e1743bb0f4d8ae7b17be0134eff
|
7
|
+
data.tar.gz: d4c0b510f3ab8fe4ab351658d8d55ea48d497fc9e6830225687542551f31ab5f6a1b97df8521a346133a340107e168aaa4b9e1ae2aa47f7d3ef567a5e2bf313e
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -29,6 +29,50 @@ Features:
|
|
29
29
|
* Berkshelf or Librarian path if any of those was included before
|
30
30
|
* `path/to/your/test/cookbooks`
|
31
31
|
* `another/path/to/your/test/cookbooks`
|
32
|
+
|
33
|
+
* Allows to create platform based test cases by calling `platform(*filters)` method. Platform file is a json of such a structure:
|
34
|
+
|
35
|
+
```javascript
|
36
|
+
{
|
37
|
+
"os": {
|
38
|
+
"ubuntu": ["10.04", "12.04", "13.10", "14.04"],
|
39
|
+
"debian": ["6.0.5", "7.2", "7.4"],
|
40
|
+
"freebsd": "9.2",
|
41
|
+
"centos": ["5.8","6.4", "6.5"],
|
42
|
+
"redhat": ["5.6", "6.3", "6.4"],
|
43
|
+
"fedora": ["18", "19", "20"]
|
44
|
+
},
|
45
|
+
"family": {
|
46
|
+
"debian": ["ubuntu", "debian"],
|
47
|
+
"rhel": ["redhat", "centos"],
|
48
|
+
"fedora": ["fedora"],
|
49
|
+
"freebsd": ["freebsd"]
|
50
|
+
}
|
51
|
+
}
|
52
|
+
```
|
53
|
+
|
54
|
+
By default this file is read from such location spec/platform.json. You can override it by specifying `EcomDev::ChefSpec::Helpers::Platform.platform_path='path/to/dir'` and `EcomDev::ChefSpec::Helpers::Platform.platform_file='file.json'` or directly by calling `platform_load(file, path)` in your test case body
|
55
|
+
|
56
|
+
When calling `platform` method you can pass a ruby block, so it will iterate over specified platforms:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
platform(:debian, :ubuntu) do |os, version|
|
60
|
+
context 'In scope of ' + os + ' ' + version do
|
61
|
+
it 'does crazy thing' do
|
62
|
+
# .. you test code
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
it will produce the following output:
|
69
|
+
|
70
|
+
```
|
71
|
+
In scope of ubuntu 14.04
|
72
|
+
does crazy things
|
73
|
+
In scope of debian 7.4
|
74
|
+
does crazy things
|
75
|
+
```
|
32
76
|
|
33
77
|
|
34
78
|
## Build Status
|
data/ecomdev-chefspec.gemspec
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
module RSpec::Core::MemoizedHelpers::ClassMethods
|
2
|
+
def platform_load(file = nil, path = nil)
|
3
|
+
@platform = EcomDev::ChefSpec::Helpers::Platform.new(file, path)
|
4
|
+
end
|
5
|
+
|
6
|
+
def platform(*args)
|
7
|
+
if @platform.nil?
|
8
|
+
platform_load
|
9
|
+
end
|
10
|
+
|
11
|
+
platforms = @platform.filter(*args)
|
12
|
+
unless block_given?
|
13
|
+
return platforms
|
14
|
+
end
|
15
|
+
|
16
|
+
platforms.each do |platform|
|
17
|
+
yield platform[:os].to_s, platform[:version].to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,143 +1,7 @@
|
|
1
1
|
module ChefSpec::API
|
2
2
|
module EcomDevHelpersRunner
|
3
3
|
def chef_run_proxy
|
4
|
-
RunnerProxy
|
5
|
-
end
|
6
|
-
|
7
|
-
class RunnerProxy
|
8
|
-
instance_methods.each { |m| undef_method m unless m =~ /(^instance_variable_get|^initialize|^__|^send$|^object_id$)/ }
|
9
|
-
|
10
|
-
def initialize(*args, &before_initialize)
|
11
|
-
@args = args
|
12
|
-
@constructor_block = before_initialize
|
13
|
-
@target = nil
|
14
|
-
@blocks = {
|
15
|
-
:before => {},
|
16
|
-
:block => {},
|
17
|
-
:after => {}
|
18
|
-
}
|
19
|
-
end
|
20
|
-
|
21
|
-
def options(options = {}, override = false)
|
22
|
-
if @args.length == 0
|
23
|
-
@args << options
|
24
|
-
return @args[0]
|
25
|
-
end
|
26
|
-
if override
|
27
|
-
@args[0] = options
|
28
|
-
else
|
29
|
-
@args[0] = options.merge(@args[0])
|
30
|
-
end
|
31
|
-
@args[0]
|
32
|
-
end
|
33
|
-
|
34
|
-
def before(method, instance_eval = true, &block)
|
35
|
-
define_proxy_block(method, :before, instance_eval, &block)
|
36
|
-
end
|
37
|
-
|
38
|
-
def after(method, instance_eval = true, &block)
|
39
|
-
define_proxy_block(method, :after, instance_eval, &block)
|
40
|
-
end
|
41
|
-
|
42
|
-
def block(method, instance_eval = true, &block)
|
43
|
-
define_proxy_block(method, :block, instance_eval, &block)
|
44
|
-
end
|
45
|
-
|
46
|
-
def proxy_blocks
|
47
|
-
@blocks
|
48
|
-
end
|
49
|
-
|
50
|
-
def runner
|
51
|
-
target
|
52
|
-
end
|
53
|
-
|
54
|
-
# Proxied chef runner
|
55
|
-
protected
|
56
|
-
def define_proxy_block(method, type, instance_eval = true, &block)
|
57
|
-
if block_given?
|
58
|
-
@blocks[type][method] ||= Array.new
|
59
|
-
@blocks[type][method] << {block: block, instance_eval: instance_eval}
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def block_for?(method, type)
|
64
|
-
@blocks.key?(type) && @blocks[type].key?(method) && @blocks[type][method].is_a?(Array)
|
65
|
-
end
|
66
|
-
|
67
|
-
def invoke_blocks(method, type, *args, &block)
|
68
|
-
blocks_to_exec = []
|
69
|
-
if block_given?
|
70
|
-
blocks_to_exec << {block: block, instance_eval: false, caller_block: true}
|
71
|
-
end
|
72
|
-
|
73
|
-
if block_for?(method, type)
|
74
|
-
blocks_to_exec.push(@blocks[type][method]).flatten!
|
75
|
-
end
|
76
|
-
|
77
|
-
blocks_to_exec.each do |info|
|
78
|
-
if info[:instance_eval]
|
79
|
-
target.instance_exec(*args, &info[:block])
|
80
|
-
else
|
81
|
-
calling_args = args.clone
|
82
|
-
unless info.key?(:caller_block) && info[:caller_block]
|
83
|
-
calling_args.unshift(target)
|
84
|
-
end
|
85
|
-
info[:block].call(*calling_args)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def method_missing(name, *args, &block)
|
91
|
-
invoke_blocks(name, :before, *args)
|
92
|
-
result = target.send(name, *args) do |*block_args|
|
93
|
-
invoke_blocks(name, :block, *block_args, &block)
|
94
|
-
end
|
95
|
-
args.unshift(result)
|
96
|
-
invoke_blocks(name, :after, *args)
|
97
|
-
result
|
98
|
-
end
|
99
|
-
|
100
|
-
def target
|
101
|
-
unless @target.nil?
|
102
|
-
return @target
|
103
|
-
end
|
104
|
-
block_args = nil
|
105
|
-
@target = ChefSpec::Runner.new(*@args) do |*args|
|
106
|
-
block_args = args
|
107
|
-
end
|
108
|
-
invoke_blocks(:initialize, :block, *block_args, &@constructor_block)
|
109
|
-
@target
|
110
|
-
end
|
111
|
-
|
112
|
-
class << self
|
113
|
-
def instance(*args, &block)
|
114
|
-
proxy = self.new(*args, &block)
|
115
|
-
proxy_calls.each do |call|
|
116
|
-
proxy.send(call[:method], *call[:args], &call[:block])
|
117
|
-
end
|
118
|
-
reset
|
119
|
-
proxy
|
120
|
-
end
|
121
|
-
|
122
|
-
def reset
|
123
|
-
proxy_calls([])
|
124
|
-
self
|
125
|
-
end
|
126
|
-
|
127
|
-
def method_missing(method, *args, &block)
|
128
|
-
proxy_calls.push({method: method, args: args, block: block})
|
129
|
-
self
|
130
|
-
end
|
131
|
-
|
132
|
-
private
|
133
|
-
def proxy_calls(calls=nil)
|
134
|
-
@calls ||= []
|
135
|
-
unless calls.nil?
|
136
|
-
@calls = calls
|
137
|
-
end
|
138
|
-
@calls
|
139
|
-
end
|
140
|
-
end
|
4
|
+
EcomDev::ChefSpec::Helpers::RunnerProxy
|
141
5
|
end
|
142
6
|
end
|
143
7
|
end
|
@@ -1,38 +1,28 @@
|
|
1
1
|
module ChefSpec::API
|
2
2
|
module EcomDevMatcherMultilineString
|
3
3
|
|
4
|
-
module Matcher
|
5
|
-
extend self
|
6
|
-
|
7
|
-
def matcher(expected)
|
8
|
-
RSpec::Matchers::BuiltIn::Match.new(expected)
|
9
|
-
end
|
10
|
-
|
11
|
-
def regexp(match, before ='', after = '')
|
12
|
-
unless match.is_a?(Regexp)
|
13
|
-
match = ::Regexp.escape(match).tr_s('\\ ', '\\s')
|
14
|
-
else
|
15
|
-
match = match.source
|
16
|
-
end
|
17
|
-
|
18
|
-
::Regexp.new(before + match + after, Regexp::MULTILINE)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
4
|
def start_line_with(match)
|
23
|
-
|
5
|
+
EcomDev::ChefSpec::Helpers::StringMatcher.instance_eval do
|
6
|
+
matcher(regexp(match, '^'))
|
7
|
+
end
|
24
8
|
end
|
25
9
|
|
26
10
|
def end_line_with(match)
|
27
|
-
|
11
|
+
EcomDev::ChefSpec::Helpers::StringMatcher.instance_eval do
|
12
|
+
matcher(regexp(match, '', '$'))
|
13
|
+
end
|
28
14
|
end
|
29
15
|
|
30
16
|
def contain_line(match)
|
31
|
-
|
17
|
+
EcomDev::ChefSpec::Helpers::StringMatcher.instance_eval do
|
18
|
+
matcher(regexp(match, '^.*', '.*$'))
|
19
|
+
end
|
32
20
|
end
|
33
21
|
|
34
22
|
def contain_full_line(match)
|
35
|
-
|
23
|
+
EcomDev::ChefSpec::Helpers::StringMatcher.instance_eval do
|
24
|
+
matcher(regexp(match, '^\s*', '\s*$'))
|
25
|
+
end
|
36
26
|
end
|
37
27
|
end
|
38
28
|
end
|
data/lib/ecomdev/chefspec/api.rb
CHANGED
@@ -4,4 +4,6 @@ require 'chefspec/api'
|
|
4
4
|
require_relative 'api/stubs/include_recipe'
|
5
5
|
require_relative 'api/stubs/file_system'
|
6
6
|
require_relative 'api/matchers/multiline_string'
|
7
|
-
require_relative '
|
7
|
+
require_relative 'helpers'
|
8
|
+
require_relative 'api/helpers/platform'
|
9
|
+
require_relative 'api/helpers/runner'
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module EcomDev::ChefSpec::Helpers
|
5
|
+
class Platform
|
6
|
+
def initialize(file = nil, path = nil)
|
7
|
+
@os = {}
|
8
|
+
@family = {}
|
9
|
+
|
10
|
+
path ||= self.class.platform_path
|
11
|
+
file ||= self.class.platform_file
|
12
|
+
|
13
|
+
json_path = File.join(path, file)
|
14
|
+
|
15
|
+
if File.readable?(json_path)
|
16
|
+
load_json(File.read(json_path))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def load_json(json_str)
|
21
|
+
json = JSON.load(json_str)
|
22
|
+
if json.key?('os') && json['os'].is_a?(Hash)
|
23
|
+
json['os'].each_pair do |os, version|
|
24
|
+
@os[os.to_sym] ||= Array.new
|
25
|
+
unless version.is_a?(Array)
|
26
|
+
version = [version]
|
27
|
+
end
|
28
|
+
version.flatten.map {|v| v.to_s }.each do |v|
|
29
|
+
@os[os.to_sym] << v unless @os[os.to_sym].include?(v)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if json.key?('family') && json['family'].is_a?(Hash)
|
35
|
+
json['family'].each_pair do |family, os|
|
36
|
+
@family[family.to_sym] ||= Array.new
|
37
|
+
unless os.is_a?(Array)
|
38
|
+
os = [os]
|
39
|
+
end
|
40
|
+
os.flatten.map { |v| v.to_sym }.select { |v| @os.key?(v) }.each do |v|
|
41
|
+
@family[family.to_sym] << v unless @family[family.to_sym].include?(v)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns list of available os versions filtered by conditions
|
48
|
+
#
|
49
|
+
# Each condition is treated as OR operation
|
50
|
+
# If no condition is specified all items are listed
|
51
|
+
# If TrueClass is supplied as one of the arguments it filters only last versions
|
52
|
+
#
|
53
|
+
def filter(*conditions)
|
54
|
+
latest = !conditions.select {|item| item === true }.empty?
|
55
|
+
filters = translate_conditions(conditions)
|
56
|
+
items = list(latest)
|
57
|
+
unless filters.empty?
|
58
|
+
items = items.select do |item|
|
59
|
+
!filters.select {|filter| match_filter(filter, item) }.empty?
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
items
|
64
|
+
end
|
65
|
+
|
66
|
+
def translate_conditions(conditions)
|
67
|
+
conditions.flatten!
|
68
|
+
previous_filter = previous_condition = nil
|
69
|
+
filters = []
|
70
|
+
|
71
|
+
reset_options = Proc.new do
|
72
|
+
previous_filter = previous_condition = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
conditions.each do |condition|
|
76
|
+
unless condition.is_a?(Hash)
|
77
|
+
if is_version(condition) && is_os(previous_condition) && previous_filter
|
78
|
+
previous_filter[:version] ||= Array.new
|
79
|
+
previous_filter[:version] << condition.to_s
|
80
|
+
elsif is_os(condition)
|
81
|
+
previous_filter = {os: condition.to_sym}
|
82
|
+
filters << previous_filter
|
83
|
+
else
|
84
|
+
reset_options.call
|
85
|
+
end
|
86
|
+
else
|
87
|
+
reset_options.call
|
88
|
+
if condition.key?(:family)
|
89
|
+
condition[:family] = [condition[:family]] unless condition[:family].is_a?(Array)
|
90
|
+
condition[:family].select {|family| @family.key?(family) }.each do |family|
|
91
|
+
@family[family].each { |os| filters << {os: os} }
|
92
|
+
end
|
93
|
+
elsif condition.key?(:os)
|
94
|
+
condition[:version] ||= []
|
95
|
+
condition[:version] = [condition[:version]] if condition[:version].is_a?(String)
|
96
|
+
filters << condition
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
filters
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns `true` if provided value is a version string
|
104
|
+
# @param string [String, Symbol] value to check
|
105
|
+
# @return [TrueClass, FalseClass]
|
106
|
+
def is_version(string)
|
107
|
+
return false if string === false || string === true || string.nil?
|
108
|
+
string.to_s.match(/^\d+[\d\.a-zA-Z\-_~]*/)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns `true` if provided value is a registered OS
|
112
|
+
# @param string [String, Symbol] value to check
|
113
|
+
# @return [TrueClass, FalseClass]
|
114
|
+
def is_os(string)
|
115
|
+
return false if string === false || string === true || string.nil?
|
116
|
+
@os.key?(string.to_sym)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns true if item matches filter conditions
|
120
|
+
# @param filter [Hash{Symbol => String, Symbol}]
|
121
|
+
# @param item [Hash{Symbol => String, Symbol}]
|
122
|
+
# @return [true, false]
|
123
|
+
def match_filter(filter, item)
|
124
|
+
filter.each_pair do |key, value|
|
125
|
+
unless item.key?(key)
|
126
|
+
return false
|
127
|
+
end
|
128
|
+
unless value.is_a?(Array)
|
129
|
+
return false if value != item[key]
|
130
|
+
else
|
131
|
+
return true if value.empty?
|
132
|
+
return false unless value.include?(item[key])
|
133
|
+
end
|
134
|
+
end
|
135
|
+
true
|
136
|
+
end
|
137
|
+
|
138
|
+
# Mark as private internal methods
|
139
|
+
private :is_os, :is_version, :translate_conditions, :match_filter
|
140
|
+
|
141
|
+
# Returns list of available os versions
|
142
|
+
# @param [TrueClass, FalseClass] latest specify if would like to receive only latest
|
143
|
+
# @return [Array<Hash{Symbol => String, Symbol}>] list of os versions in view of hash
|
144
|
+
def list(latest = false)
|
145
|
+
result = []
|
146
|
+
@os.map do |os, versions|
|
147
|
+
unless latest
|
148
|
+
versions.each { |version| result << {os: os, version: version}}
|
149
|
+
else
|
150
|
+
result << {os: os, version: versions.last} if versions.length > 0
|
151
|
+
end
|
152
|
+
end
|
153
|
+
result
|
154
|
+
end
|
155
|
+
|
156
|
+
class << self
|
157
|
+
def platform_path
|
158
|
+
@platform_path ||= RSpec.configuration.default_path
|
159
|
+
end
|
160
|
+
|
161
|
+
def platform_path=(value)
|
162
|
+
unless value.nil?
|
163
|
+
value = Pathname.new value
|
164
|
+
|
165
|
+
if value.relative? && value.to_path[0] != '~'
|
166
|
+
value = Pathname.new File.join(RSpec.configuration.default_path, value.to_path)
|
167
|
+
end
|
168
|
+
|
169
|
+
value = value.to_path
|
170
|
+
end
|
171
|
+
|
172
|
+
@platform_path = value
|
173
|
+
end
|
174
|
+
|
175
|
+
def platform_file
|
176
|
+
@platform_file ||= 'platform.json'
|
177
|
+
end
|
178
|
+
|
179
|
+
def platform_file=(value)
|
180
|
+
@platform_file = value
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module EcomDev::ChefSpec::Helpers
|
2
|
+
class RunnerProxy
|
3
|
+
instance_methods.each { |m| undef_method m unless m =~ /(^instance_variable_get|^initialize|^__|^send$|^object_id$)/ }
|
4
|
+
|
5
|
+
def initialize(*args, &before_initialize)
|
6
|
+
@args = args
|
7
|
+
@constructor_block = before_initialize
|
8
|
+
@target = nil
|
9
|
+
@blocks = {
|
10
|
+
:before => {},
|
11
|
+
:block => {},
|
12
|
+
:after => {}
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def options(options = {}, override = false)
|
17
|
+
if @args.length == 0
|
18
|
+
@args << options
|
19
|
+
return @args[0]
|
20
|
+
end
|
21
|
+
if override
|
22
|
+
@args[0] = options
|
23
|
+
else
|
24
|
+
@args[0] = options.merge(@args[0])
|
25
|
+
end
|
26
|
+
@args[0]
|
27
|
+
end
|
28
|
+
|
29
|
+
def before(method, instance_eval = true, &block)
|
30
|
+
define_proxy_block(method, :before, instance_eval, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def after(method, instance_eval = true, &block)
|
34
|
+
define_proxy_block(method, :after, instance_eval, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def block(method, instance_eval = true, &block)
|
38
|
+
define_proxy_block(method, :block, instance_eval, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def proxy_blocks
|
42
|
+
@blocks
|
43
|
+
end
|
44
|
+
|
45
|
+
def runner
|
46
|
+
target
|
47
|
+
end
|
48
|
+
|
49
|
+
# Proxied chef runner
|
50
|
+
protected
|
51
|
+
def define_proxy_block(method, type, instance_eval = true, &block)
|
52
|
+
if block_given?
|
53
|
+
@blocks[type][method] ||= Array.new
|
54
|
+
@blocks[type][method] << {block: block, instance_eval: instance_eval}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def block_for?(method, type)
|
59
|
+
@blocks.key?(type) && @blocks[type].key?(method) && @blocks[type][method].is_a?(Array)
|
60
|
+
end
|
61
|
+
|
62
|
+
def invoke_blocks(method, type, *args, &block)
|
63
|
+
blocks_to_exec = []
|
64
|
+
if block_given?
|
65
|
+
blocks_to_exec << {block: block, instance_eval: false, caller_block: true}
|
66
|
+
end
|
67
|
+
|
68
|
+
if block_for?(method, type)
|
69
|
+
blocks_to_exec.push(@blocks[type][method]).flatten!
|
70
|
+
end
|
71
|
+
|
72
|
+
blocks_to_exec.each do |info|
|
73
|
+
if info[:instance_eval]
|
74
|
+
target.instance_exec(*args, &info[:block])
|
75
|
+
else
|
76
|
+
calling_args = args.clone
|
77
|
+
unless info.key?(:caller_block) && info[:caller_block]
|
78
|
+
calling_args.unshift(target)
|
79
|
+
end
|
80
|
+
info[:block].call(*calling_args)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def method_missing(name, *args, &block)
|
86
|
+
invoke_blocks(name, :before, *args)
|
87
|
+
result = target.send(name, *args) do |*block_args|
|
88
|
+
invoke_blocks(name, :block, *block_args, &block)
|
89
|
+
end
|
90
|
+
args.unshift(result)
|
91
|
+
invoke_blocks(name, :after, *args)
|
92
|
+
result
|
93
|
+
end
|
94
|
+
|
95
|
+
def target
|
96
|
+
unless @target.nil?
|
97
|
+
return @target
|
98
|
+
end
|
99
|
+
block_args = nil
|
100
|
+
@target = ChefSpec::Runner.new(*@args) do |*args|
|
101
|
+
block_args = args
|
102
|
+
end
|
103
|
+
invoke_blocks(:initialize, :block, *block_args, &@constructor_block)
|
104
|
+
@target
|
105
|
+
end
|
106
|
+
|
107
|
+
class << self
|
108
|
+
def instance(*args, &block)
|
109
|
+
proxy = self.new(*args, &block)
|
110
|
+
proxy_calls.each do |call|
|
111
|
+
proxy.send(call[:method], *call[:args], &call[:block])
|
112
|
+
end
|
113
|
+
reset
|
114
|
+
proxy
|
115
|
+
end
|
116
|
+
|
117
|
+
def reset
|
118
|
+
proxy_calls([])
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
def method_missing(method, *args, &block)
|
123
|
+
proxy_calls.push({method: method, args: args, block: block})
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
def proxy_calls(calls=nil)
|
129
|
+
@calls ||= []
|
130
|
+
unless calls.nil?
|
131
|
+
@calls = calls
|
132
|
+
end
|
133
|
+
@calls
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module EcomDev::ChefSpec::Helpers
|
2
|
+
module StringMatcher
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def matcher(expected)
|
6
|
+
RSpec::Matchers::BuiltIn::Match.new(expected)
|
7
|
+
end
|
8
|
+
|
9
|
+
def regexp(match, before ='', after = '')
|
10
|
+
unless match.is_a?(::Regexp)
|
11
|
+
match = ::Regexp.escape(match).tr_s('\\ ', '\\s')
|
12
|
+
else
|
13
|
+
match = match.source
|
14
|
+
end
|
15
|
+
|
16
|
+
::Regexp.new(before + match + after, Regexp::MULTILINE)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module EcomDev::ChefSpec::Helpers
|
2
|
+
def self.helper(helper)
|
3
|
+
File.join(File.dirname(__FILE__), 'helpers', helper)
|
4
|
+
end
|
5
|
+
|
6
|
+
autoload :RunnerProxy, helper('runner_proxy')
|
7
|
+
autoload :StringMatcher, helper('string_matcher')
|
8
|
+
autoload :Platform, helper('platform')
|
9
|
+
end
|
data/lib/ecomdev/chefspec.rb
CHANGED
@@ -2,5 +2,6 @@ require 'chefspec' unless defined?(ChefSpec) # Require chef spec only if it is n
|
|
2
2
|
|
3
3
|
require_relative 'chefspec/version'
|
4
4
|
require_relative 'chefspec/configuration'
|
5
|
+
require_relative 'chefspec/helpers'
|
5
6
|
require_relative 'chefspec/resource/matcher'
|
6
7
|
require_relative 'chefspec/api'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
describe 'extends to base example group' do
|
2
|
+
platform_load('platforms.json', File.dirname(__FILE__))
|
3
|
+
|
4
|
+
platform(true) do |name, version|
|
5
|
+
context 'In scope of ' + name + ' ' + version + ' ' do
|
6
|
+
it 'does crazy things' do
|
7
|
+
expect(true).to eq(true)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
platform(:ubuntu, :debian) do |name, version|
|
13
|
+
context 'In filtered scope of ' + name + ' ' + version + ' ' do
|
14
|
+
it 'does another crazy things' do
|
15
|
+
expect(true).to eq(true)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
platform(family: [:debian]) do |name, version|
|
21
|
+
context 'In family filtered scope of ' + name + ' ' + version + ' ' do
|
22
|
+
it 'does another crazy things' do
|
23
|
+
expect(true).to eq(true)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
platform({family: [:debian]}, true) do |name, version|
|
29
|
+
context 'In family filtered scope of latest ' + name + '' do
|
30
|
+
it 'does another crazy things on version ' + version do
|
31
|
+
expect(true).to eq(true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
{
|
2
|
+
"os": {
|
3
|
+
"ubuntu": ["10.04", "12.04", "13.10", "14.04"],
|
4
|
+
"debian": ["6.0.5", "7.2", "7.4"],
|
5
|
+
"freebsd": ["9.2"],
|
6
|
+
"centos": ["5.8","6.4", "6.5"],
|
7
|
+
"redhat": ["5.6", "6.3", "6.4"],
|
8
|
+
"fedora": ["18", "19", "20"]
|
9
|
+
},
|
10
|
+
"family": {
|
11
|
+
"debian": ["ubuntu", "debian"],
|
12
|
+
"rhel": ["redhat", "centos"],
|
13
|
+
"fedora": ["fedora"],
|
14
|
+
"freebsd": ["freebsd"]
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,193 @@
|
|
1
|
+
describe EcomDev::ChefSpec::Helpers::Platform do
|
2
|
+
before (:each) do
|
3
|
+
described_class.platform_path = nil
|
4
|
+
described_class.platform_file = nil
|
5
|
+
end
|
6
|
+
|
7
|
+
let (:json_data) { '{"os": {"ubuntu": [14.01, 12.04], "debian": 6, "freebsd": 3.4}, "family": {"debian": ["ubuntu", "debian", "unknown"], "freebsd": ["freebsd"]}}' }
|
8
|
+
let (:os) { {ubuntu: %w(14.01 12.04), debian: %w(6), freebsd: %w(3.4)} }
|
9
|
+
let (:family) { {debian: [:ubuntu, :debian], freebsd: [:freebsd]}}
|
10
|
+
|
11
|
+
describe '#platform_path' do
|
12
|
+
it 'equals to rspec directory by default' do
|
13
|
+
expect(described_class.platform_path).to eq(RSpec.configuration.default_path)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'makes possible to read from default path' do
|
17
|
+
expect(File.directory?(described_class.platform_path)).to eq(true)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#platform_path=' do
|
22
|
+
it 'sets platform path to relative path within specs directory' do
|
23
|
+
described_class.platform_path = 'relative'
|
24
|
+
expect(described_class.platform_path).to eq(File.join(RSpec.configuration.default_path, 'relative'))
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'sets platform path to absolute path if absolute path value is specified' do
|
28
|
+
described_class.platform_path = '~/chefspec'
|
29
|
+
expect(described_class.platform_path).to eq('~/chefspec')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#platform_file' do
|
34
|
+
it 'equals to platform.json by default' do
|
35
|
+
expect(described_class.platform_file).to eq('platform.json')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#platform_file=' do
|
40
|
+
it 'equals to platform.json by default' do
|
41
|
+
described_class.platform_file = 'custom.json'
|
42
|
+
expect(described_class.platform_file).to eq('custom.json')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#initialize' do
|
47
|
+
it 'loads platform list from JSON file' do
|
48
|
+
expect(described_class).to receive(:platform_file).and_return('custom.json')
|
49
|
+
expect(described_class).to receive(:platform_path).and_return('/custom/files')
|
50
|
+
|
51
|
+
expect(File).to receive(:readable?).with(File.join('/custom/files', 'custom.json')).and_return(true)
|
52
|
+
expect(File).to receive(:read).with(File.join('/custom/files', 'custom.json'))
|
53
|
+
.and_return(json_data)
|
54
|
+
expect_any_instance_of(described_class).to receive(:load_json).with(json_data)
|
55
|
+
described_class.new
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'loads platform list from JSON file specified in arguments' do
|
59
|
+
expect(described_class).not_to receive(:platform_file)
|
60
|
+
expect(described_class).to receive(:platform_path).and_return('/custom/files')
|
61
|
+
|
62
|
+
expect(File).to receive(:readable?).with(File.join('/custom/files', 'custom.json')).and_return(true)
|
63
|
+
expect(File).to receive(:read).with(File.join('/custom/files', 'custom.json'))
|
64
|
+
.and_return(json_data)
|
65
|
+
expect_any_instance_of(described_class).to receive(:load_json).with(json_data)
|
66
|
+
described_class.new('custom.json')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'loads platform list from JSON file that is specified in arguments with path' do
|
70
|
+
expect(described_class).not_to receive(:platform_file)
|
71
|
+
expect(described_class).not_to receive(:platform_path)
|
72
|
+
|
73
|
+
expect(File).to receive(:readable?).with(File.join('/custom/files', 'custom.json')).and_return(true)
|
74
|
+
expect(File).to receive(:read).with(File.join('/custom/files', 'custom.json'))
|
75
|
+
.and_return(json_data)
|
76
|
+
expect_any_instance_of(described_class).to receive(:load_json).with(json_data)
|
77
|
+
described_class.new('custom.json', '/custom/files')
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'does not load any json file, if file is not readable' do
|
81
|
+
expect(described_class).to receive(:platform_file).and_return('custom.json')
|
82
|
+
expect(described_class).to receive(:platform_path).and_return('/custom/files')
|
83
|
+
|
84
|
+
expect(File).to receive(:readable?).with(File.join('/custom/files', 'custom.json')).and_return(false)
|
85
|
+
expect_any_instance_of(described_class).not_to receive(:load_json)
|
86
|
+
described_class.new
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '#load_json' do
|
91
|
+
it 'loads json structure into os and family properties' do
|
92
|
+
platform = described_class.new
|
93
|
+
|
94
|
+
expect(platform.instance_variable_get(:@os)).to be_instance_of(Hash).and be_empty
|
95
|
+
expect(platform.instance_variable_get(:@family)).to be_instance_of(Hash).and be_empty
|
96
|
+
|
97
|
+
platform.load_json(json_data)
|
98
|
+
|
99
|
+
expect(platform.instance_variable_get(:@os)).to eq(os)
|
100
|
+
expect(platform.instance_variable_get(:@family)).to eq(family)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '#filter' do
|
105
|
+
it 'it filters by os names in string' do
|
106
|
+
platform = described_class.new
|
107
|
+
platform.instance_variable_set(:@os, os)
|
108
|
+
platform.instance_variable_set(:@family, family)
|
109
|
+
|
110
|
+
expect(platform.filter('ubuntu', :debian)).to eq([{os: :ubuntu, version: '14.01'},
|
111
|
+
{os: :ubuntu, version: '12.04'},
|
112
|
+
{os: :debian, version: '6'}])
|
113
|
+
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'it filters by os name and version' do
|
118
|
+
platform = described_class.new
|
119
|
+
platform.instance_variable_set(:@os, os)
|
120
|
+
platform.instance_variable_set(:@family, family)
|
121
|
+
|
122
|
+
expect(platform.filter('debian', '6')).to eq([os: :debian, version: '6'])
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'it filters by os version hash' do
|
126
|
+
platform = described_class.new
|
127
|
+
platform.instance_variable_set(:@os, os)
|
128
|
+
platform.instance_variable_set(:@family, family)
|
129
|
+
|
130
|
+
expect(platform.filter(:os => :debian, :version => '6')).to eq([{os: :debian, version: '6'}])
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'accepts multiple hashes' do
|
134
|
+
platform = described_class.new
|
135
|
+
platform.instance_variable_set(:@os, os)
|
136
|
+
platform.instance_variable_set(:@family, family)
|
137
|
+
|
138
|
+
expect(platform.filter(
|
139
|
+
{:os => :debian, :version => '6'},
|
140
|
+
{:os => :freebsd}
|
141
|
+
)).to eq([{os: :debian, version: '6'}, {os: :freebsd, version: '3.4'}])
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'accepts multiple string symbol conditions' do
|
145
|
+
platform = described_class.new
|
146
|
+
platform.instance_variable_set(:@os, os)
|
147
|
+
platform.instance_variable_set(:@family, family)
|
148
|
+
|
149
|
+
expect(platform.filter(:debian, '6', 'freebsd')).to eq([{os: :debian, version: '6'}, {os: :freebsd, version: '3.4'}])
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
it 'it filters by os family' do
|
154
|
+
platform = described_class.new
|
155
|
+
platform.instance_variable_set(:@os, os)
|
156
|
+
platform.instance_variable_set(:@family, family)
|
157
|
+
|
158
|
+
expect(platform.filter(:family => :debian)).to eq([{os: :ubuntu, version: '14.01'},
|
159
|
+
{os: :ubuntu, version: '12.04'},
|
160
|
+
{os: :debian, version: '6'}])
|
161
|
+
|
162
|
+
expect(platform.filter(:family => [:debian, :freebsd])).to eq([{os: :ubuntu, version: '14.01'},
|
163
|
+
{os: :ubuntu, version: '12.04'},
|
164
|
+
{os: :debian, version: '6'},
|
165
|
+
{os: :freebsd, version: '3.4'}])
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#list' do
|
170
|
+
it 'it lists all os systems' do
|
171
|
+
platform = described_class.new
|
172
|
+
platform.instance_variable_set(:@os, os)
|
173
|
+
platform.instance_variable_set(:@family, family)
|
174
|
+
|
175
|
+
expect(platform.list).to eq([{os: :ubuntu, version: '14.01'},
|
176
|
+
{os: :ubuntu, version: '12.04'},
|
177
|
+
{os: :debian, version: '6'},
|
178
|
+
{os: :freebsd, version: '3.4'}])
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'it lists only latest os versions' do
|
183
|
+
platform = described_class.new
|
184
|
+
platform.instance_variable_set(:@os, os)
|
185
|
+
platform.instance_variable_set(:@family, family)
|
186
|
+
|
187
|
+
# NOTE: it should return last item of array, not latest version by number
|
188
|
+
expect(platform.list(true)).to eq([{os: :ubuntu, version: '12.04'},
|
189
|
+
{os: :debian, version: '6'},
|
190
|
+
{os: :freebsd, version: '3.4'}])
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
describe ChefSpec::
|
2
|
+
describe EcomDev::ChefSpec::Helpers::RunnerProxy do
|
3
3
|
it 'does not create any method unless method of runner is executed' do
|
4
4
|
runner_proxy = described_class.new
|
5
5
|
expect(runner_proxy.instance_variable_get('@target')).to be_nil
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ecomdev-chefspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Chepurnyi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chefspec
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -71,11 +85,16 @@ files:
|
|
71
85
|
- lib/ecomdev-chefspec.rb
|
72
86
|
- lib/ecomdev/chefspec.rb
|
73
87
|
- lib/ecomdev/chefspec/api.rb
|
88
|
+
- lib/ecomdev/chefspec/api/helpers/platform.rb
|
74
89
|
- lib/ecomdev/chefspec/api/helpers/runner.rb
|
75
90
|
- lib/ecomdev/chefspec/api/matchers/multiline_string.rb
|
76
91
|
- lib/ecomdev/chefspec/api/stubs/file_system.rb
|
77
92
|
- lib/ecomdev/chefspec/api/stubs/include_recipe.rb
|
78
93
|
- lib/ecomdev/chefspec/configuration.rb
|
94
|
+
- lib/ecomdev/chefspec/helpers.rb
|
95
|
+
- lib/ecomdev/chefspec/helpers/platform.rb
|
96
|
+
- lib/ecomdev/chefspec/helpers/runner_proxy.rb
|
97
|
+
- lib/ecomdev/chefspec/helpers/string_matcher.rb
|
79
98
|
- lib/ecomdev/chefspec/resource/matcher.rb
|
80
99
|
- lib/ecomdev/chefspec/resource/matcher/dsl.rb
|
81
100
|
- lib/ecomdev/chefspec/resource/matcher/helper.rb
|
@@ -83,12 +102,15 @@ files:
|
|
83
102
|
- lib/ecomdev/chefspec/stub/include_recipe.rb
|
84
103
|
- lib/ecomdev/chefspec/version.rb
|
85
104
|
- spec/spec_helper.rb
|
86
|
-
- spec/unit/api/helpers/
|
105
|
+
- spec/unit/api/helpers/platform_spec.rb
|
106
|
+
- spec/unit/api/helpers/platforms.json
|
87
107
|
- spec/unit/api/helpers/runner_spec.rb
|
88
108
|
- spec/unit/api/matchers/multiline_string_spec.rb
|
89
109
|
- spec/unit/api/stubs/file_system_spec.rb
|
90
110
|
- spec/unit/api/stubs/include_recipe_spec.rb
|
91
111
|
- spec/unit/configuration_spec.rb
|
112
|
+
- spec/unit/helpers/platform_spec.rb
|
113
|
+
- spec/unit/helpers/runner_proxy_spec.rb
|
92
114
|
- spec/unit/matcher/dsl_spec.rb
|
93
115
|
- spec/unit/matcher/helper_spec.rb
|
94
116
|
- spec/unit/matcher_spec.rb
|
@@ -120,12 +142,15 @@ specification_version: 4
|
|
120
142
|
summary: A collection of helpers for chef spec, to make easier writing recipe specs
|
121
143
|
test_files:
|
122
144
|
- spec/spec_helper.rb
|
123
|
-
- spec/unit/api/helpers/
|
145
|
+
- spec/unit/api/helpers/platform_spec.rb
|
146
|
+
- spec/unit/api/helpers/platforms.json
|
124
147
|
- spec/unit/api/helpers/runner_spec.rb
|
125
148
|
- spec/unit/api/matchers/multiline_string_spec.rb
|
126
149
|
- spec/unit/api/stubs/file_system_spec.rb
|
127
150
|
- spec/unit/api/stubs/include_recipe_spec.rb
|
128
151
|
- spec/unit/configuration_spec.rb
|
152
|
+
- spec/unit/helpers/platform_spec.rb
|
153
|
+
- spec/unit/helpers/runner_proxy_spec.rb
|
129
154
|
- spec/unit/matcher/dsl_spec.rb
|
130
155
|
- spec/unit/matcher/helper_spec.rb
|
131
156
|
- spec/unit/matcher_spec.rb
|