test-kitchen 0.7.0 → 1.0.0.alpha.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +20 -0
- data/.travis.yml +11 -0
- data/.yardopts +3 -0
- data/Gemfile +13 -0
- data/Guardfile +11 -0
- data/LICENSE +15 -0
- data/README.md +131 -0
- data/Rakefile +69 -0
- data/bin/kitchen +9 -4
- data/features/cli.feature +17 -0
- data/features/cli_init.feature +156 -0
- data/features/support/env.rb +14 -0
- data/lib/kitchen/busser.rb +166 -0
- data/lib/kitchen/chef_data_uploader.rb +156 -0
- data/lib/kitchen/cli.rb +540 -0
- data/lib/kitchen/collection.rb +55 -0
- data/lib/kitchen/color.rb +46 -0
- data/lib/kitchen/config.rb +223 -0
- data/lib/kitchen/driver/base.rb +180 -0
- data/lib/kitchen/driver/dummy.rb +81 -0
- data/lib/kitchen/driver/ssh_base.rb +192 -0
- data/lib/kitchen/driver.rb +42 -0
- data/lib/kitchen/errors.rb +52 -0
- data/lib/kitchen/instance.rb +327 -0
- data/lib/kitchen/instance_actor.rb +42 -0
- data/lib/kitchen/loader/yaml.rb +105 -0
- data/lib/kitchen/logger.rb +145 -0
- data/{cookbooks/test-kitchen/libraries/helpers.rb → lib/kitchen/logging.rb} +13 -9
- data/lib/kitchen/manager.rb +45 -0
- data/lib/kitchen/metadata_chopper.rb +52 -0
- data/lib/kitchen/platform.rb +61 -0
- data/lib/kitchen/rake_tasks.rb +59 -0
- data/lib/kitchen/shell_out.rb +65 -0
- data/lib/kitchen/state_file.rb +88 -0
- data/lib/kitchen/suite.rb +76 -0
- data/lib/kitchen/thor_tasks.rb +62 -0
- data/lib/kitchen/util.rb +79 -0
- data/{cookbooks/test-kitchen/recipes/erlang.rb → lib/kitchen/version.rb} +9 -6
- data/lib/kitchen.rb +98 -0
- data/lib/vendor/hash_recursive_merge.rb +74 -0
- data/spec/kitchen/collection_spec.rb +80 -0
- data/spec/kitchen/color_spec.rb +54 -0
- data/spec/kitchen/config_spec.rb +201 -0
- data/spec/kitchen/driver/dummy_spec.rb +191 -0
- data/spec/kitchen/instance_spec.rb +162 -0
- data/spec/kitchen/loader/yaml_spec.rb +243 -0
- data/spec/kitchen/platform_spec.rb +48 -0
- data/spec/kitchen/state_file_spec.rb +122 -0
- data/spec/kitchen/suite_spec.rb +64 -0
- data/spec/spec_helper.rb +47 -0
- data/templates/plugin/driver.rb.erb +23 -0
- data/templates/plugin/license_apachev2.erb +15 -0
- data/templates/plugin/license_gplv2.erb +18 -0
- data/templates/plugin/license_gplv3.erb +16 -0
- data/templates/plugin/license_mit.erb +22 -0
- data/templates/plugin/license_reserved.erb +5 -0
- data/templates/plugin/version.rb.erb +12 -0
- data/test-kitchen.gemspec +44 -0
- metadata +290 -82
- data/config/Cheffile +0 -47
- data/config/Kitchenfile +0 -39
- data/config/Vagrantfile +0 -114
- data/cookbooks/test-kitchen/attributes/default.rb +0 -25
- data/cookbooks/test-kitchen/metadata.rb +0 -27
- data/cookbooks/test-kitchen/recipes/chef.rb +0 -19
- data/cookbooks/test-kitchen/recipes/compat.rb +0 -39
- data/cookbooks/test-kitchen/recipes/default.rb +0 -51
- data/cookbooks/test-kitchen/recipes/ruby.rb +0 -29
- data/lib/test-kitchen/cli/destroy.rb +0 -36
- data/lib/test-kitchen/cli/init.rb +0 -37
- data/lib/test-kitchen/cli/platform_list.rb +0 -37
- data/lib/test-kitchen/cli/project_info.rb +0 -44
- data/lib/test-kitchen/cli/ssh.rb +0 -36
- data/lib/test-kitchen/cli/status.rb +0 -36
- data/lib/test-kitchen/cli/test.rb +0 -68
- data/lib/test-kitchen/cli.rb +0 -282
- data/lib/test-kitchen/dsl.rb +0 -63
- data/lib/test-kitchen/environment.rb +0 -166
- data/lib/test-kitchen/platform.rb +0 -79
- data/lib/test-kitchen/project/base.rb +0 -159
- data/lib/test-kitchen/project/cookbook.rb +0 -97
- data/lib/test-kitchen/project/cookbook_copy.rb +0 -58
- data/lib/test-kitchen/project/ruby.rb +0 -37
- data/lib/test-kitchen/project/supported_platforms.rb +0 -75
- data/lib/test-kitchen/project.rb +0 -23
- data/lib/test-kitchen/runner/base.rb +0 -154
- data/lib/test-kitchen/runner/openstack/dsl.rb +0 -39
- data/lib/test-kitchen/runner/openstack/environment.rb +0 -141
- data/lib/test-kitchen/runner/openstack.rb +0 -147
- data/lib/test-kitchen/runner/vagrant.rb +0 -95
- data/lib/test-kitchen/runner.rb +0 -21
- data/lib/test-kitchen/scaffold.rb +0 -88
- data/lib/test-kitchen/ui.rb +0 -73
- data/lib/test-kitchen/version.rb +0 -21
- data/lib/test-kitchen.rb +0 -34
@@ -0,0 +1,76 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2012, Fletcher Nichol
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
module Kitchen
|
20
|
+
|
21
|
+
# A Chef run_list and attribute hash that will be used in a convergence
|
22
|
+
# integration.
|
23
|
+
#
|
24
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
25
|
+
class Suite
|
26
|
+
|
27
|
+
# @return [String] logical name of this suite
|
28
|
+
attr_reader :name
|
29
|
+
|
30
|
+
# @return [Array] Array of Chef run_list items
|
31
|
+
attr_reader :run_list
|
32
|
+
|
33
|
+
# @return [Hash] Hash of Chef node attributes
|
34
|
+
attr_reader :attributes
|
35
|
+
|
36
|
+
# @return [Array] Array of names of excluded platforms
|
37
|
+
attr_reader :excludes
|
38
|
+
|
39
|
+
# @return [String] local path to the suite's data bags, or nil if one does
|
40
|
+
# not exist
|
41
|
+
attr_reader :data_bags_path
|
42
|
+
|
43
|
+
# @return [String] local path to the suite's roles, or nil if one does
|
44
|
+
# not exist
|
45
|
+
attr_reader :roles_path
|
46
|
+
|
47
|
+
# Constructs a new suite.
|
48
|
+
#
|
49
|
+
# @param [Hash] options configuration for a new suite
|
50
|
+
# @option options [String] :name logical name of this suit (**Required**)
|
51
|
+
# @option options [String] :run_list Array of Chef run_list items
|
52
|
+
# (**Required**)
|
53
|
+
# @option options [Hash] :attributes Hash of Chef node attributes
|
54
|
+
# @option options [String] :excludes Array of names of excluded platforms
|
55
|
+
# @option options [String] :data_bags_path path to data bags
|
56
|
+
# @option options [String] :roles_path path to roles
|
57
|
+
def initialize(options = {})
|
58
|
+
validate_options(options)
|
59
|
+
|
60
|
+
@name = options[:name]
|
61
|
+
@run_list = options[:run_list]
|
62
|
+
@attributes = options[:attributes] || Hash.new
|
63
|
+
@excludes = options[:excludes] || Array.new
|
64
|
+
@data_bags_path = options[:data_bags_path]
|
65
|
+
@roles_path = options[:roles_path]
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def validate_options(opts)
|
71
|
+
[:name, :run_list].each do |k|
|
72
|
+
raise ClientError, "Suite#new requires option :#{k}" if opts[k].nil?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2012, Fletcher Nichol
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require 'thor'
|
20
|
+
|
21
|
+
require 'kitchen'
|
22
|
+
|
23
|
+
module Kitchen
|
24
|
+
|
25
|
+
# Kitchen Thor task generator.
|
26
|
+
#
|
27
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
28
|
+
class ThorTasks < Thor
|
29
|
+
|
30
|
+
namespace :kitchen
|
31
|
+
|
32
|
+
# Creates Kitchen Thor tasks and allows the callee to configure it.
|
33
|
+
#
|
34
|
+
# @yield [self] gives itself to the block
|
35
|
+
def initialize(*args)
|
36
|
+
super
|
37
|
+
@config = Kitchen::Config.new
|
38
|
+
@config.supervised = false
|
39
|
+
Kitchen.logger = Kitchen.default_file_logger
|
40
|
+
yield self if block_given?
|
41
|
+
define
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :config
|
47
|
+
|
48
|
+
def define
|
49
|
+
config.instances.each do |instance|
|
50
|
+
self.class.desc instance.name, "Run #{instance.name} test instance"
|
51
|
+
self.class.send(:define_method, instance.name.gsub(/-/, '_')) do
|
52
|
+
instance.test(:always)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
self.class.desc "all", "Run all test instances"
|
57
|
+
self.class.send(:define_method, :all) do
|
58
|
+
config.instances.each { |i| invoke i.name.gsub(/-/, '_') }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/kitchen/util.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2012, Fletcher Nichol
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
module Kitchen
|
20
|
+
|
21
|
+
# Stateless utility methods used in different contexts. Essentially a mini
|
22
|
+
# PassiveSupport library.
|
23
|
+
module Util
|
24
|
+
|
25
|
+
def self.to_camel_case(str)
|
26
|
+
str.split('_').map { |w| w.capitalize }.join
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.to_snake_case(str)
|
30
|
+
str.split('::').
|
31
|
+
last.
|
32
|
+
gsub(/([A-Z+])([A-Z][a-z])/, '\1_\2').
|
33
|
+
gsub(/([a-z\d])([A-Z])/, '\1_\2').
|
34
|
+
downcase
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.to_logger_level(symbol)
|
38
|
+
return nil unless [:debug, :info, :warn, :error, :fatal].include?(symbol)
|
39
|
+
|
40
|
+
Logger.const_get(symbol.to_s.upcase)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.from_logger_level(const)
|
44
|
+
case const
|
45
|
+
when Logger::DEBUG then :debug
|
46
|
+
when Logger::INFO then :info
|
47
|
+
when Logger::WARN then :warn
|
48
|
+
when Logger::ERROR then :error
|
49
|
+
else :fatal
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.symbolized_hash(obj)
|
54
|
+
if obj.is_a?(Hash)
|
55
|
+
obj.inject({}) { |h, (k, v)| h[k.to_sym] = symbolized_hash(v) ; h }
|
56
|
+
elsif obj.is_a?(Array)
|
57
|
+
obj.inject([]) { |a, v| a << symbolized_hash(v) ; a }
|
58
|
+
else
|
59
|
+
obj
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.stringified_hash(obj)
|
64
|
+
if obj.is_a?(Hash)
|
65
|
+
obj.inject({}) { |h, (k, v)| h[k.to_s] = symbolized_hash(v) ; h }
|
66
|
+
elsif obj.is_a?(Array)
|
67
|
+
obj.inject([]) { |a, v| a << symbolized_hash(v) ; a }
|
68
|
+
else
|
69
|
+
obj
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.duration(total)
|
74
|
+
minutes = (total / 60).to_i
|
75
|
+
seconds = (total - (minutes * 60))
|
76
|
+
"(%dm%.2fs)" % [minutes, seconds]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -1,19 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
#
|
2
|
-
# Author::
|
3
|
-
#
|
4
|
-
#
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2012, Fletcher Nichol
|
5
6
|
#
|
6
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
8
|
# you may not use this file except in compliance with the License.
|
8
9
|
# You may obtain a copy of the License at
|
9
10
|
#
|
10
|
-
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
12
|
#
|
12
13
|
# Unless required by applicable law or agreed to in writing, software
|
13
14
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
15
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
16
|
# See the License for the specific language governing permissions and
|
16
17
|
# limitations under the License.
|
17
|
-
#
|
18
18
|
|
19
|
-
|
19
|
+
module Kitchen
|
20
|
+
|
21
|
+
VERSION = "1.0.0.alpha.0"
|
22
|
+
end
|
data/lib/kitchen.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2012, Fletcher Nichol
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require 'celluloid'
|
20
|
+
require 'pathname'
|
21
|
+
require 'thread'
|
22
|
+
|
23
|
+
require 'kitchen/errors'
|
24
|
+
require 'kitchen/logger'
|
25
|
+
require 'kitchen/logging'
|
26
|
+
require 'kitchen/shell_out'
|
27
|
+
require 'kitchen/util'
|
28
|
+
|
29
|
+
require 'kitchen/busser'
|
30
|
+
require 'kitchen/chef_data_uploader'
|
31
|
+
require 'kitchen/color'
|
32
|
+
require 'kitchen/collection'
|
33
|
+
require 'kitchen/config'
|
34
|
+
require 'kitchen/driver'
|
35
|
+
require 'kitchen/driver/base'
|
36
|
+
require 'kitchen/driver/ssh_base'
|
37
|
+
require 'kitchen/instance'
|
38
|
+
require 'kitchen/instance_actor'
|
39
|
+
require 'kitchen/loader/yaml'
|
40
|
+
require 'kitchen/manager'
|
41
|
+
require 'kitchen/metadata_chopper'
|
42
|
+
require 'kitchen/platform'
|
43
|
+
require 'kitchen/state_file'
|
44
|
+
require 'kitchen/suite'
|
45
|
+
require 'kitchen/version'
|
46
|
+
|
47
|
+
module Kitchen
|
48
|
+
|
49
|
+
class << self
|
50
|
+
|
51
|
+
attr_accessor :logger
|
52
|
+
attr_accessor :crashes
|
53
|
+
attr_accessor :mutex
|
54
|
+
|
55
|
+
# Returns the root path of the Kitchen gem source code.
|
56
|
+
#
|
57
|
+
# @return [Pathname] root path of gem
|
58
|
+
def source_root
|
59
|
+
@source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
|
60
|
+
end
|
61
|
+
|
62
|
+
def crashes?
|
63
|
+
! crashes.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
def default_logger
|
67
|
+
Logger.new(:stdout => STDOUT, :level => env_log)
|
68
|
+
end
|
69
|
+
|
70
|
+
def default_file_logger
|
71
|
+
logfile = File.expand_path(File.join(".kitchen", "logs", "kitchen.log"))
|
72
|
+
Logger.new(:stdout => STDOUT, :logdev => logfile, :level => env_log)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def env_log
|
78
|
+
level = ENV['KITCHEN_LOG'] && ENV['KITCHEN_LOG'].downcase.to_sym
|
79
|
+
level = Util.to_logger_level(level) unless level.nil?
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Default log level verbosity
|
84
|
+
DEFAULT_LOG_LEVEL = :info
|
85
|
+
end
|
86
|
+
|
87
|
+
# Initialize the base logger and use that for Celluloid's logger
|
88
|
+
Kitchen.logger = Kitchen.default_logger
|
89
|
+
Celluloid.logger = Kitchen.logger
|
90
|
+
|
91
|
+
# Setup a collection of instance crash exceptions for error reporting
|
92
|
+
Kitchen.crashes = []
|
93
|
+
Celluloid.exception_handler do |exception|
|
94
|
+
Kitchen.logger.debug("An instance crashed because of #{exception.inspect}")
|
95
|
+
Kitchen.mutex.synchronize { Kitchen.crashes << exception }
|
96
|
+
end
|
97
|
+
|
98
|
+
Kitchen.mutex = Mutex.new
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#
|
2
|
+
# = Hash Recursive Merge
|
3
|
+
#
|
4
|
+
# Merges a Ruby Hash recursively, Also known as deep merge.
|
5
|
+
# Recursive version of Hash#merge and Hash#merge!.
|
6
|
+
#
|
7
|
+
# Category:: Ruby
|
8
|
+
# Package:: Hash
|
9
|
+
# Author:: Simone Carletti <weppos@weppos.net>
|
10
|
+
# Copyright:: 2007-2008 The Authors
|
11
|
+
# License:: MIT License
|
12
|
+
# Link:: http://www.simonecarletti.com/
|
13
|
+
# Source:: http://gist.github.com/gists/6391/
|
14
|
+
#
|
15
|
+
module HashRecursiveMerge
|
16
|
+
|
17
|
+
#
|
18
|
+
# Recursive version of Hash#merge!
|
19
|
+
#
|
20
|
+
# Adds the contents of +other_hash+ to +hsh+,
|
21
|
+
# merging entries in +hsh+ with duplicate keys with those from +other_hash+.
|
22
|
+
#
|
23
|
+
# Compared with Hash#merge!, this method supports nested hashes.
|
24
|
+
# When both +hsh+ and +other_hash+ contains an entry with the same key,
|
25
|
+
# it merges and returns the values from both arrays.
|
26
|
+
#
|
27
|
+
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
|
28
|
+
# h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
|
29
|
+
# h1.rmerge!(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
|
30
|
+
#
|
31
|
+
# Simply using Hash#merge! would return
|
32
|
+
#
|
33
|
+
# h1.merge!(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
|
34
|
+
#
|
35
|
+
def rmerge!(other_hash)
|
36
|
+
merge!(other_hash) do |key, oldval, newval|
|
37
|
+
oldval.class == self.class ? oldval.rmerge!(newval) : newval
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Recursive version of Hash#merge
|
43
|
+
#
|
44
|
+
# Compared with Hash#merge!, this method supports nested hashes.
|
45
|
+
# When both +hsh+ and +other_hash+ contains an entry with the same key,
|
46
|
+
# it merges and returns the values from both arrays.
|
47
|
+
#
|
48
|
+
# Compared with Hash#merge, this method provides a different approch
|
49
|
+
# for merging nasted hashes.
|
50
|
+
# If the value of a given key is an Hash and both +other_hash+ abd +hsh
|
51
|
+
# includes the same key, the value is merged instead replaced with
|
52
|
+
# +other_hash+ value.
|
53
|
+
#
|
54
|
+
# h1 = {"a" => 100, "b" => 200, "c" => {"c1" => 12, "c2" => 14}}
|
55
|
+
# h2 = {"b" => 254, "c" => {"c1" => 16, "c3" => 94}}
|
56
|
+
# h1.rmerge(h2) #=> {"a" => 100, "b" => 254, "c" => {"c1" => 16, "c2" => 14, "c3" => 94}}
|
57
|
+
#
|
58
|
+
# Simply using Hash#merge would return
|
59
|
+
#
|
60
|
+
# h1.merge(h2) #=> {"a" => 100, "b" = >254, "c" => {"c1" => 16, "c3" => 94}}
|
61
|
+
#
|
62
|
+
def rmerge(other_hash)
|
63
|
+
r = {}
|
64
|
+
merge(other_hash) do |key, oldval, newval|
|
65
|
+
r[key] = oldval.class == self.class ? oldval.rmerge(newval) : newval
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
class Hash
|
73
|
+
include HashRecursiveMerge
|
74
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2012, Fletcher Nichol
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require_relative '../spec_helper'
|
20
|
+
require 'ostruct'
|
21
|
+
|
22
|
+
require 'kitchen/collection'
|
23
|
+
|
24
|
+
describe Kitchen::Collection do
|
25
|
+
|
26
|
+
let(:collection) do
|
27
|
+
Kitchen::Collection.new([
|
28
|
+
obj('one'), obj('two', 'a'), obj('two', 'b'), obj('three')
|
29
|
+
])
|
30
|
+
end
|
31
|
+
|
32
|
+
it "transparently wraps an Array" do
|
33
|
+
collection.must_be_instance_of Array
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#get" do
|
37
|
+
|
38
|
+
it "returns a single object by its name" do
|
39
|
+
collection.get('three').must_equal obj('three')
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns the first occurance of an object by its name" do
|
43
|
+
collection.get('two').must_equal obj('two', 'a')
|
44
|
+
end
|
45
|
+
|
46
|
+
it "returns nil if an object cannot be found by its name" do
|
47
|
+
collection.get('nope').must_be_nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#get_all" do
|
52
|
+
|
53
|
+
it "returns a Collection of objects whose name matches the regex" do
|
54
|
+
result = collection.get_all(/(one|three)/)
|
55
|
+
result.size.must_equal 2
|
56
|
+
result[0].must_equal obj('one')
|
57
|
+
result[1].must_equal obj('three')
|
58
|
+
result.get_all(/one/).size.must_equal 1
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns an empty Collection if on matches are found" do
|
62
|
+
result = collection.get_all(/noppa/)
|
63
|
+
result.must_equal []
|
64
|
+
result.get("nahuh").must_be_nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#as_name" do
|
69
|
+
|
70
|
+
it "returns an Array of names as strings" do
|
71
|
+
collection.as_names.must_equal %w{one two two three}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def obj(name, extra = nil)
|
78
|
+
OpenStruct.new(:name => name, :extra => extra)
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Author:: Fletcher Nichol (<fnichol@nichol.ca>)
|
4
|
+
#
|
5
|
+
# Copyright (C) 2013, Fletcher Nichol
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
|
19
|
+
require_relative '../spec_helper'
|
20
|
+
|
21
|
+
require 'kitchen/color'
|
22
|
+
|
23
|
+
describe Kitchen::Color do
|
24
|
+
|
25
|
+
describe ".escape" do
|
26
|
+
|
27
|
+
it "returns an empty string if name is nil" do
|
28
|
+
Kitchen::Color.escape(nil).must_equal ""
|
29
|
+
end
|
30
|
+
|
31
|
+
it "returns an empty string if name is not in the ANSI hash" do
|
32
|
+
Kitchen::Color.escape(:puce).must_equal ""
|
33
|
+
end
|
34
|
+
|
35
|
+
it "returns an ansi escape sequence string for cyan" do
|
36
|
+
Kitchen::Color.escape(:cyan).must_equal "\e[36m"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns an ansi escape sequence string for reset" do
|
40
|
+
Kitchen::Color.escape(:reset).must_equal "\e[0m"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".colorize" do
|
45
|
+
|
46
|
+
it "returns an ansi escaped string colored yellow" do
|
47
|
+
Kitchen::Color.colorize("hello", :yellow).must_equal "\e[33mhello\e[0m"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "returns an unescaped string if color is not in the ANSI hash" do
|
51
|
+
Kitchen::Color.colorize("double", :rainbow).must_equal "double"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|