jamie 0.1.0.alpha17 → 0.1.0.alpha18

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.
data/.travis.yml CHANGED
@@ -1,3 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
+ notifications:
5
+ irc:
6
+ channels:
7
+ - "irc.freenode.org#jamie-ci"
8
+ use_notice: true
9
+ skip_join: true
data/Gemfile CHANGED
@@ -2,6 +2,12 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
+ group :development do
6
+ gem 'rb-inotify', :require => false
7
+ gem 'rb-fsevent', :require => false
8
+ gem 'rb-fchange', :require => false
9
+ end
10
+
5
11
  group :test do
6
12
  gem 'rake', '~> 0.9'
7
13
  gem 'pry'
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'minitest' do
2
+ watch(%r|^spec/(.*)_spec\.rb|)
3
+ watch(%r|^lib/(.*)([^/]+)\.rb|) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
4
+ watch(%r|^spec/spec_helper\.rb|) { "spec" }
5
+ end
data/README.md CHANGED
@@ -5,28 +5,16 @@
5
5
 
6
6
  A Chef convergence integration test harness.
7
7
 
8
- ## Installation
8
+ ## A Note
9
9
 
10
- Add this line to your application's Gemfile:
10
+ This project is currently in rapid development which means frequent releases,
11
+ potential for massive refactorings (that could be API breaking), and minimal
12
+ to no documentation. This will change as the project transitions to be used in
13
+ production environments.
11
14
 
12
- gem 'jamie'
15
+ Despite the warnings above, if you are still interested, please get in touch
16
+ via freenode/IRC (#jamie),
17
+ Twitter ([@fnichol](https://twitter.com/fnichol)),
18
+ or Email ([fnichol@nichol.ca](mailto:fnichol@nichol.ca)).
13
19
 
14
- And then execute:
15
-
16
- $ bundle
17
-
18
- Or install it yourself as:
19
-
20
- $ gem install jamie
21
-
22
- ## Usage
23
-
24
- TODO: Write usage instructions here
25
-
26
- ## Contributing
27
-
28
- 1. Fork it
29
- 2. Create your feature branch (`git checkout -b my-new-feature`)
30
- 3. Commit your changes (`git commit -am 'Add some feature'`)
31
- 4. Push to the branch (`git push origin my-new-feature`)
32
- 5. Create new Pull Request
20
+ For everyone else, watch [this space](https://github.com/jamie-ci/jamie).
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'cane/rake_task'
3
+ require 'rake/testtask'
3
4
  require 'tailor/rake_task'
4
5
 
5
6
  desc "Run cane to check quality metrics"
@@ -21,7 +22,16 @@ Tailor::RakeTask.new
21
22
 
22
23
  desc "Display LOC stats"
23
24
  task :stats do
25
+ puts "\n## Production Code Stats"
24
26
  sh "countloc -r lib/jamie lib/jamie.rb"
27
+ puts "\n## Test Code Stats"
28
+ sh "countloc -r spec"
25
29
  end
26
30
 
27
- task :default => [ :cane, :tailor ]
31
+ Rake::TestTask.new do |t|
32
+ t.libs.push "lib"
33
+ t.test_files = FileList['spec/**/*_spec.rb']
34
+ t.verbose = true
35
+ end
36
+
37
+ task :default => [ :test, :cane, :tailor, :stats ]
data/bin/jamie CHANGED
@@ -6,6 +6,7 @@
6
6
  Signal.trap("INT") { exit 1 }
7
7
 
8
8
  $:.unshift File.join(File.dirname(__FILE__), %w{.. lib})
9
+ require 'rubygems'
9
10
  require 'jamie/cli'
10
11
 
11
12
  Jamie::CLI.start
data/jamie.gemspec CHANGED
@@ -10,21 +10,28 @@ Gem::Specification.new do |gem|
10
10
  gem.email = ["fnichol@nichol.ca"]
11
11
  gem.description = %q{A Chef convergence integration test harness}
12
12
  gem.summary = gem.description
13
- gem.homepage = ""
13
+ gem.homepage = "https://github.com/jamie-ci/jamie"
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
16
16
  gem.executables = %w(jamie)
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
+ gem.required_ruby_version = ">= 1.9.1"
21
+
20
22
  gem.add_dependency 'thor'
21
23
  gem.add_dependency 'net-ssh'
22
24
  gem.add_dependency 'net-scp'
23
25
  gem.add_dependency 'mixlib-shellout'
24
26
 
27
+ gem.add_development_dependency 'minitest'
28
+ gem.add_development_dependency 'fakefs'
29
+ gem.add_development_dependency 'guard-minitest'
30
+
25
31
  gem.add_development_dependency 'yard'
26
32
  gem.add_development_dependency 'maruku'
27
33
  gem.add_development_dependency 'cane'
28
34
  gem.add_development_dependency 'tailor'
35
+ gem.add_development_dependency 'simplecov'
29
36
  gem.add_development_dependency 'countloc'
30
37
  end
data/lib/jamie/cli.rb CHANGED
@@ -1,4 +1,20 @@
1
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.
2
18
 
3
19
  require 'ostruct'
4
20
  require 'thor'
@@ -8,6 +24,8 @@ require 'jamie'
8
24
  module Jamie
9
25
 
10
26
  # The command line runner for Jamie.
27
+ #
28
+ # @author Fletcher Nichol <fnichol@nichol.ca>
11
29
  class CLI < Thor
12
30
 
13
31
  include Thor::Actions
@@ -157,6 +175,8 @@ module Jamie
157
175
 
158
176
  # A project initialization generator, to help prepare a cookbook project for
159
177
  # testing with Jamie.
178
+ #
179
+ # @author Fletcher Nichol <fnichol@nichol.ca>
160
180
  class InitGenerator < Thor
161
181
 
162
182
  include Thor::Actions
@@ -275,6 +295,8 @@ module Jamie
275
295
  end
276
296
 
277
297
  # A generator to create a new Jamie driver plugin.
298
+ #
299
+ # @author Fletcher Nichol <fnichol@nichol.ca>
278
300
  class NewPluginGenerator < Thor
279
301
 
280
302
  include Thor::Actions
@@ -406,6 +428,8 @@ module Jamie
406
428
  end
407
429
 
408
430
  # Renders an ERB template with a hash of template variables.
431
+ #
432
+ # @author Fletcher Nichol <fnichol@nichol.ca>
409
433
  class TemplateRenderer < OpenStruct
410
434
 
411
435
  def self.render(template, data = {})
@@ -23,6 +23,8 @@ module Jamie
23
23
  module Driver
24
24
 
25
25
  # Dummy driver for Jamie.
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
26
28
  class Dummy < Jamie::Driver::Base
27
29
 
28
30
  def create(instance, state)
@@ -1,4 +1,20 @@
1
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.
2
18
 
3
19
  require 'rake/tasklib'
4
20
 
@@ -7,6 +23,8 @@ require 'jamie'
7
23
  module Jamie
8
24
 
9
25
  # Jamie Rake task generator.
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
10
28
  class RakeTasks < ::Rake::TaskLib
11
29
 
12
30
  # Creates Jamie Rake tasks and allows the callee to configure it.
@@ -26,7 +44,7 @@ module Jamie
26
44
  namespace "jamie" do
27
45
  config.instances.each do |instance|
28
46
  desc "Run #{instance.name} test instance"
29
- task instance.name { instance.test }
47
+ task instance.name { instance.test(:always) }
30
48
  end
31
49
 
32
50
  desc "Run all test instances"
@@ -1,4 +1,20 @@
1
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.
2
18
 
3
19
  require 'thor'
4
20
 
@@ -7,6 +23,8 @@ require 'jamie'
7
23
  module Jamie
8
24
 
9
25
  # Jamie Thor task generator.
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
10
28
  class ThorTasks < Thor
11
29
 
12
30
  namespace :jamie
@@ -29,7 +47,7 @@ module Jamie
29
47
  config.instances.each do |instance|
30
48
  self.class.desc instance.name, "Run #{instance.name} test instance"
31
49
  self.class.send(:define_method, instance.name.gsub(/-/, '_')) do
32
- instance.test
50
+ instance.test(:always)
33
51
  end
34
52
  end
35
53
 
data/lib/jamie/version.rb CHANGED
@@ -1,6 +1,22 @@
1
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.
2
18
 
3
19
  module Jamie
4
20
 
5
- VERSION = "0.1.0.alpha17"
21
+ VERSION = "0.1.0.alpha18"
6
22
  end
data/lib/jamie.rb CHANGED
@@ -1,4 +1,20 @@
1
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.
2
18
 
3
19
  require 'base64'
4
20
  require 'delegate'
@@ -29,6 +45,8 @@ module Jamie
29
45
 
30
46
  # Base configuration class for Jamie. This class exposes configuration such
31
47
  # as the location of the Jamie YAML file, instances, log_levels, etc.
48
+ #
49
+ # @author Fletcher Nichol <fnichol@nichol.ca>
32
50
  class Config
33
51
 
34
52
  attr_writer :yaml_file
@@ -97,6 +115,8 @@ module Jamie
97
115
  # Delegate class which adds the ability to find single and multiple
98
116
  # objects by their #name in an Array. Hey, it's better than monkey-patching
99
117
  # Array, right?
118
+ #
119
+ # @author Fletcher Nichol <fnichol@nichol.ca>
100
120
  class Collection < SimpleDelegator
101
121
 
102
122
  # Returns a single object by its name, or nil if none are found.
@@ -141,6 +161,7 @@ module Jamie
141
161
 
142
162
  def new_platform(hash)
143
163
  mpc = merge_platform_config(hash)
164
+ mpc['driver_config'] ||= Hash.new
144
165
  mpc['driver_config']['jamie_root'] = File.dirname(yaml_file)
145
166
  mpc['driver'] = new_driver(mpc['driver_plugin'], mpc['driver_config'])
146
167
  Platform.new(mpc)
@@ -220,6 +241,8 @@ module Jamie
220
241
 
221
242
  # A Chef run_list and attribute hash that will be used in a convergence
222
243
  # integration.
244
+ #
245
+ # @author Fletcher Nichol <fnichol@nichol.ca>
223
246
  class Suite
224
247
 
225
248
  # @return [String] logical name of this suite
@@ -261,7 +284,7 @@ module Jamie
261
284
 
262
285
  def validate_options(opts)
263
286
  %w(name run_list).each do |k|
264
- raise ArgumentError, "Attribute '#{attr}' is required." if opts[k].nil?
287
+ raise ArgumentError, "Attribute '#{k}' is required." if opts[k].nil?
265
288
  end
266
289
  end
267
290
  end
@@ -269,6 +292,8 @@ module Jamie
269
292
  # A target operating system environment in which convergence integration
270
293
  # will take place. This may represent a specific operating system, version,
271
294
  # and machine architecture.
295
+ #
296
+ # @author Fletcher Nichol <fnichol@nichol.ca>
272
297
  class Platform
273
298
 
274
299
  # @return [String] logical name of this platform
@@ -307,7 +332,7 @@ module Jamie
307
332
 
308
333
  def validate_options(opts)
309
334
  %w(name driver).each do |k|
310
- raise ArgumentError, "Attribute '#{attr}' is required." if opts[k].nil?
335
+ raise ArgumentError, "Attribute '#{k}' is required." if opts[k].nil?
311
336
  end
312
337
  end
313
338
  end
@@ -315,6 +340,8 @@ module Jamie
315
340
  # An instance of a suite running on a platform. A created instance may be a
316
341
  # local virtual machine, cloud instance, container, or even a bare metal
317
342
  # server, which is determined by the platform's driver.
343
+ #
344
+ # @author Fletcher Nichol <fnichol@nichol.ca>
318
345
  class Instance
319
346
 
320
347
  # @return [Suite] the test suite configuration
@@ -331,6 +358,8 @@ module Jamie
331
358
  # @param suite [Suite] a suite
332
359
  # @param platform [Platform] a platform
333
360
  def initialize(suite, platform)
361
+ validate_options(suite, platform)
362
+
334
363
  @suite = suite
335
364
  @platform = platform
336
365
  @jr = Jr.new(@suite.name)
@@ -440,6 +469,11 @@ module Jamie
440
469
 
441
470
  private
442
471
 
472
+ def validate_options(suite, platform)
473
+ raise ArgumentError, "Attribute 'suite' is required." if suite.nil?
474
+ raise ArgumentError, "Attribute 'platform' is required." if platform.nil?
475
+ end
476
+
443
477
  def transition_to(desired)
444
478
  FSM.actions(last_action, desired).each do |transition|
445
479
  send("#{transition}_action")
@@ -517,6 +551,8 @@ module Jamie
517
551
 
518
552
  # The simplest finite state machine pseudo-implementation needed to manage
519
553
  # an Instance.
554
+ #
555
+ # @author Fletcher Nichol <fnichol@nichol.ca>
520
556
  class FSM
521
557
 
522
558
  # Returns an Array of all transitions to bring an Instance from its last
@@ -555,6 +591,8 @@ module Jamie
555
591
  # Command string generator to interface with Jamie Runner (jr). The
556
592
  # commands that are generated are safe to pass to an SSH command or as an
557
593
  # unix command argument (escaped in single quotes).
594
+ #
595
+ # @author Fletcher Nichol <fnichol@nichol.ca>
558
596
  class Jr
559
597
 
560
598
  # Constructs a new jr command generator, given a suite name.
@@ -711,6 +749,8 @@ module Jamie
711
749
 
712
750
  # Mixin that wraps a command shell out invocation, providing a #run_command
713
751
  # method.
752
+ #
753
+ # @author Fletcher Nichol <fnichol@nichol.ca>
714
754
  module ShellOut
715
755
 
716
756
  # Wrapped exception for any interally raised shell out commands.
@@ -764,6 +804,8 @@ module Jamie
764
804
  # Base class for a driver. A driver is responsible for carrying out the
765
805
  # lifecycle activities of an instance, such as creating, converging, and
766
806
  # destroying an instance.
807
+ #
808
+ # @author Fletcher Nichol <fnichol@nichol.ca>
767
809
  class Base
768
810
 
769
811
  include ShellOut
@@ -842,6 +884,8 @@ module Jamie
842
884
  # A subclass must implement the following methods:
843
885
  # * #create(instance, state)
844
886
  # * #destroy(instance, state)
887
+ #
888
+ # @author Fletcher Nichol <fnichol@nichol.ca>
845
889
  class SSHBase < Base
846
890
 
847
891
  def create(instance, state)
@@ -973,6 +1017,8 @@ module Jamie
973
1017
 
974
1018
  # Uploads Chef asset files such as dna.json, data bags, and cookbooks to an
975
1019
  # instance over SSH.
1020
+ #
1021
+ # @author Fletcher Nichol <fnichol@nichol.ca>
976
1022
  class ChefDataUploader
977
1023
 
978
1024
  include ShellOut
@@ -0,0 +1,486 @@
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 'simplecov'
20
+ SimpleCov.adapters.define 'gem' do
21
+ command_name 'Specs'
22
+
23
+ add_filter '/spec/'
24
+ add_filter '/lib/vendor/'
25
+
26
+ add_group 'Libraries', '/lib/'
27
+ end
28
+ SimpleCov.start 'gem'
29
+
30
+ require 'fakefs/spec_helpers'
31
+ require 'minitest/autorun'
32
+
33
+ require 'jamie'
34
+
35
+ # Nasty hack to redefine IO.read in terms of File#read for fakefs
36
+ class IO
37
+ def self.read(*args)
38
+ File.open(args[0], "rb") { |f| f.read(args[1]) }
39
+ end
40
+ end
41
+
42
+ describe Jamie::Config do
43
+ include FakeFS::SpecHelpers
44
+
45
+ let(:config) { Jamie::Config.new("/tmp/.jamie.yml") }
46
+
47
+ describe "#platforms" do
48
+
49
+ it "returns platforms loaded from a jamie.yml" do
50
+ stub_yaml!({'platforms' => [
51
+ { 'name' => 'one', 'driver_plugin' => 'dummy' },
52
+ { 'name' => 'two', 'driver_plugin' => 'dummy' },
53
+ ]})
54
+ config.platforms.size.must_equal 2
55
+ config.platforms[0].name.must_equal 'one'
56
+ config.platforms[1].name.must_equal 'two'
57
+ end
58
+
59
+ it "returns an empty Array if no platforms are given" do
60
+ stub_yaml!({})
61
+ config.platforms.must_equal []
62
+ end
63
+
64
+ it "returns a platform containing a driver instance" do
65
+ stub_yaml!({'platforms' => [
66
+ { 'name' => 'platform', 'driver_plugin' => 'dummy' }
67
+ ]})
68
+ config.platforms.first.driver.must_be_instance_of Jamie::Driver::Dummy
69
+ end
70
+
71
+ it "returns a platform with a driver initialized with jamie_root" do
72
+ stub_yaml!({'platforms' => [
73
+ { 'name' => 'platform', 'driver_plugin' => 'dummy' }
74
+ ]})
75
+ config.platforms.first.driver['jamie_root'].must_equal "/tmp"
76
+ end
77
+
78
+ it "returns a platform with a driver initialized with passed in config" do
79
+ stub_yaml!({'platforms' => [
80
+ { 'name' => 'platform', 'driver_plugin' => 'dummy',
81
+ 'driver_config' => { 'foo' => 'bar' }
82
+ }
83
+ ]})
84
+ config.platforms.first.driver['foo'].must_equal "bar"
85
+ end
86
+ end
87
+
88
+ describe "#suites" do
89
+
90
+ it "returns suites loaded from a jamie.yml" do
91
+ stub_yaml!({'suites' => [
92
+ { 'name' => 'one', 'run_list' => [] },
93
+ { 'name' => 'two', 'run_list' => [] },
94
+ ]})
95
+ config.suites.size.must_equal 2
96
+ config.suites[0].name.must_equal 'one'
97
+ config.suites[1].name.must_equal 'two'
98
+ end
99
+
100
+ it "returns an empty Array if no suites are given" do
101
+ stub_yaml!({})
102
+ config.suites.must_equal []
103
+ end
104
+
105
+ it "returns a suite with nil for data_bags_path by default" do
106
+ stub_yaml!({'suites' => [ { 'name' => 'one', 'run_list' => [] } ]})
107
+ config.suites.first.data_bags_path.must_be_nil
108
+ end
109
+
110
+ it "retuns a suite with a common data_bags_path set" do
111
+ stub_yaml!({'suites' => [ { 'name' => 'one', 'run_list' => [] } ]})
112
+ config.test_base_path = "/tmp/base"
113
+ FileUtils.mkdir_p "/tmp/base/data_bags"
114
+ config.suites.first.data_bags_path.must_equal "/tmp/base/data_bags"
115
+ end
116
+
117
+ it "retuns a suite with a suite-specific data_bags_path set" do
118
+ stub_yaml!({'suites' => [ { 'name' => 'cool', 'run_list' => [] } ]})
119
+ config.test_base_path = "/tmp/base"
120
+ FileUtils.mkdir_p "/tmp/base/cool/data_bags"
121
+ config.suites.first.data_bags_path.must_equal "/tmp/base/cool/data_bags"
122
+ end
123
+
124
+ it "returns a suite with nil for roles_path by default" do
125
+ stub_yaml!({'suites' => [ { 'name' => 'one', 'run_list' => [] } ]})
126
+ config.suites.first.roles_path.must_be_nil
127
+ end
128
+
129
+ it "returns a suite with a common roles_path set" do
130
+ stub_yaml!({'suites' => [ { 'name' => 'one', 'run_list' => [] } ]})
131
+ config.test_base_path = "/tmp/base"
132
+ FileUtils.mkdir_p "/tmp/base/roles"
133
+ config.suites.first.roles_path.must_equal "/tmp/base/roles"
134
+ end
135
+
136
+ it "returns a suite with a suite-specific roles_path set" do
137
+ stub_yaml!({'suites' => [ { 'name' => 'mysuite', 'run_list' => [] } ]})
138
+ config.test_base_path = "/tmp/base"
139
+ FileUtils.mkdir_p "/tmp/base/mysuite/roles"
140
+ config.suites.first.roles_path.must_equal "/tmp/base/mysuite/roles"
141
+ end
142
+ end
143
+
144
+ describe "#instances" do
145
+
146
+ it "returns instances loaded from a jamie.yml" do
147
+ stub_yaml!({
148
+ 'platforms' => [
149
+ { 'name' => 'p1', 'driver_plugin' => 'dummy' },
150
+ { 'name' => 'p2', 'driver_plugin' => 'dummy' },
151
+ ],
152
+ 'suites' => [
153
+ { 'name' => 's1', 'run_list' => [] },
154
+ { 'name' => 's2', 'run_list' => [] },
155
+ ]
156
+ })
157
+ config.instances.size.must_equal 4
158
+ config.instances.map { |i| i.name }.must_equal %w{s1-p1 s1-p2 s2-p1 s2-p2}
159
+ end
160
+ end
161
+
162
+ describe "jamie.local.yml" do
163
+
164
+ it "merges in configuration with jamie.yml" do
165
+ stub_yaml!(".jamie.yml", {
166
+ 'platforms' => [ { 'name' => 'p1', 'driver_plugin' => 'dummy' } ]
167
+ })
168
+ stub_yaml!(".jamie.local.yml", {
169
+ 'driver_config' => { 'foo' => 'bar' }
170
+ })
171
+ config.platforms.first.driver['foo'].must_equal 'bar'
172
+ end
173
+
174
+ it "merges over configuration in jamie.yml" do
175
+ stub_yaml!(".jamie.yml", {
176
+ 'driver_config' => { 'foo' => 'nope' },
177
+ 'platforms' => [ { 'name' => 'p1', 'driver_plugin' => 'dummy' } ]
178
+ })
179
+ stub_yaml!(".jamie.local.yml", {
180
+ 'driver_config' => { 'foo' => 'bar' }
181
+ })
182
+ config.platforms.first.driver['foo'].must_equal 'bar'
183
+ end
184
+ end
185
+
186
+ describe "erb filtering" do
187
+
188
+ it "evaluates jamie.yml through erb before loading" do
189
+ FileUtils.mkdir_p "/tmp"
190
+ File.open("/tmp/.jamie.yml", "wb") do |f|
191
+ f.write <<-YAML.gsub(/^ {10}/, '')
192
+ ---
193
+ driver_plugin: dummy
194
+ platforms:
195
+ - name: <%= "AHH".downcase + "choo" %>
196
+ YAML
197
+ end
198
+ config.platforms.first.name.must_equal "ahhchoo"
199
+ end
200
+
201
+ it "evaluates jamie.local.yml through erb before loading" do
202
+ stub_yaml!({
203
+ 'platforms' => [ { 'name' => 'p1', 'driver_plugin' => 'dummy' } ]
204
+ })
205
+ FileUtils.mkdir_p "/tmp"
206
+ File.open("/tmp/.jamie.local.yml", "wb") do |f|
207
+ f.write <<-YAML.gsub(/^ {10}/, '')
208
+ ---
209
+ driver_config:
210
+ <% %w{noodle mushroom}.each do |kind| %>
211
+ <%= kind %>: soup
212
+ <% end %>
213
+ YAML
214
+ end
215
+ config.platforms.first.driver['noodle'].must_equal "soup"
216
+ config.platforms.first.driver['mushroom'].must_equal "soup"
217
+ end
218
+ end
219
+
220
+ describe "#log_level" do
221
+
222
+ it "returns a default log_level of info" do
223
+ config.log_level.must_equal :info
224
+ end
225
+
226
+ it "returns an overridden log_level" do
227
+ config.log_level = :error
228
+ config.log_level.must_equal :error
229
+ end
230
+ end
231
+
232
+ private
233
+
234
+ def stub_yaml!(name = ".jamie.yml", hash)
235
+ FileUtils.mkdir_p "/tmp"
236
+ File.open("/tmp/#{name}", "wb") { |f| f.write(hash.to_yaml) }
237
+ end
238
+ end
239
+
240
+ describe Jamie::Config::Collection do
241
+
242
+ let(:collection) do
243
+ Jamie::Config::Collection.new([
244
+ obj('one'), obj('two', 'a'), obj('two', 'b'), obj('three')
245
+ ])
246
+ end
247
+
248
+ it "transparently wraps an Array" do
249
+ collection.must_be_instance_of Array
250
+ end
251
+
252
+ describe "#get" do
253
+
254
+ it "returns a single object by its name" do
255
+ collection.get('three').must_equal obj('three')
256
+ end
257
+
258
+ it "returns the first occurance of an object by its name" do
259
+ collection.get('two').must_equal obj('two', 'a')
260
+ end
261
+
262
+ it "returns nil if an object cannot be found by its name" do
263
+ collection.get('nope').must_be_nil
264
+ end
265
+ end
266
+
267
+ describe "#get_all" do
268
+
269
+ it "returns a Collection of objects whose name matches the regex" do
270
+ result = collection.get_all(/(one|three)/)
271
+ result.size.must_equal 2
272
+ result[0].must_equal obj('one')
273
+ result[1].must_equal obj('three')
274
+ result.get_all(/one/).size.must_equal 1
275
+ end
276
+
277
+ it "returns an empty Collection if on matches are found" do
278
+ result = collection.get_all(/noppa/)
279
+ result.must_equal []
280
+ result.get("nahuh").must_be_nil
281
+ end
282
+ end
283
+
284
+ describe "#as_name" do
285
+
286
+ it "returns an Array of names as strings" do
287
+ collection.as_names.must_equal %w{one two two three}
288
+ end
289
+ end
290
+
291
+ private
292
+
293
+ def obj(name, extra = nil)
294
+ OpenStruct.new(:name => name, :extra => extra)
295
+ end
296
+ end
297
+
298
+ describe Jamie::Suite do
299
+
300
+ let(:opts) do ; { 'name' => 'suitezy', 'run_list' => [ 'doowah' ] } ; end
301
+ let(:suite) { Jamie::Suite.new(opts) }
302
+
303
+ it "raises an ArgumentError if name is missing" do
304
+ opts.delete('name')
305
+ proc { Jamie::Suite.new(opts) }.must_raise ArgumentError
306
+ end
307
+
308
+ it "raises an ArgumentError if run_list is missing" do
309
+ opts.delete('run_list')
310
+ proc { Jamie::Suite.new(opts) }.must_raise ArgumentError
311
+ end
312
+
313
+ it "returns an empty Hash given no attributes" do
314
+ suite.attributes.must_equal Hash.new
315
+ end
316
+
317
+ it "returns nil given no data_bags_path" do
318
+ suite.data_bags_path.must_be_nil
319
+ end
320
+
321
+ it "returns nil given no roles_path" do
322
+ suite.roles_path.must_be_nil
323
+ end
324
+
325
+ it "returns attributes from constructor" do
326
+ opts.merge!({ 'attributes' => { 'a' => 'b' }, 'data_bags_path' => 'crazy',
327
+ 'roles_path' => 'town' })
328
+ suite.name.must_equal 'suitezy'
329
+ suite.run_list.must_equal [ 'doowah' ]
330
+ suite.attributes.must_equal({ 'a' => 'b' })
331
+ suite.data_bags_path.must_equal 'crazy'
332
+ suite.roles_path.must_equal 'town'
333
+ end
334
+ end
335
+
336
+ describe Jamie::Platform do
337
+
338
+ let(:opts) do ; { 'name' => 'plata', 'driver' => 'imadriver' } ; end
339
+ let(:platform) { Jamie::Platform.new(opts) }
340
+
341
+ it "raises an ArgumentError if name is missing" do
342
+ opts.delete('name')
343
+ proc { Jamie::Platform.new(opts) }.must_raise ArgumentError
344
+ end
345
+
346
+ it "raises an ArgumentError if driver is missing" do
347
+ opts.delete('driver')
348
+ proc { Jamie::Platform.new(opts) }.must_raise ArgumentError
349
+ end
350
+
351
+ it "returns an empty Array given no run_list" do
352
+ platform.run_list.must_equal []
353
+ end
354
+
355
+ it "returns an empty Hash given no attributes" do
356
+ platform.attributes.must_equal Hash.new
357
+ end
358
+
359
+ it "returns attributes from constructor" do
360
+ opts.merge!({ 'run_list' => [ 'a', 'b' ], 'attributes' => { 'c' => 'd' }})
361
+ platform.name.must_equal 'plata'
362
+ platform.driver.must_equal 'imadriver'
363
+ platform.run_list.must_equal [ 'a', 'b' ]
364
+ platform.attributes.must_equal({ 'c' => 'd' })
365
+ end
366
+ end
367
+
368
+ describe Jamie::Instance do
369
+
370
+ let(:suite) do
371
+ Jamie::Suite.new({ 'name' => 'suite',
372
+ 'run_list' => 'suite_list', 'attributes' => { 's' => 'ss' } })
373
+ end
374
+
375
+ let(:platform) do
376
+ Jamie::Platform.new({ 'name' => 'platform', 'driver' => 'driver',
377
+ 'run_list' => 'platform_list', 'attributes' => { 'p' => 'pp' } })
378
+ end
379
+
380
+ let(:instance) { Jamie::Instance.new(suite, platform) }
381
+
382
+ it "raises an ArgumentError if suite is missing" do
383
+ proc { Jamie::Instance.new(nil, platform) }.must_raise ArgumentError
384
+ end
385
+
386
+ it "raises an ArgumentError if platform is missing" do
387
+ proc { Jamie::Instance.new(suite, nil) }.must_raise ArgumentError
388
+ end
389
+
390
+ it "returns suite" do
391
+ instance.suite.must_equal suite
392
+ end
393
+
394
+ it "returns platform" do
395
+ instance.platform.must_equal platform
396
+ end
397
+
398
+ it "returns an instance of Jr" do
399
+ instance.jr.must_be_instance_of Jamie::Jr
400
+ end
401
+
402
+ describe "#name" do
403
+
404
+ def combo(suite_name, platform_name)
405
+ Jamie::Instance.new(
406
+ Jamie::Suite.new({ 'name' => suite_name, 'run_list' => [] }),
407
+ Jamie::Platform.new({ 'name' => platform_name, 'driver' => 'd' })
408
+ )
409
+ end
410
+
411
+ it "combines the suite and platform names with a dash" do
412
+ combo('suite', 'platform').name.must_equal "suite-platform"
413
+ end
414
+
415
+ it "squashes periods" do
416
+ combo('suite.ness', 'platform').name.must_equal "suiteness-platform"
417
+ combo('suite', 'platform.s').name.must_equal "suite-platforms"
418
+ combo('s.s.', '.p.p').name.must_equal "ss-pp"
419
+ end
420
+
421
+ it "transforms underscores to dashes" do
422
+ combo('suite_ness', 'platform').name.must_equal "suite-ness-platform"
423
+ combo('suite', 'platform-s').name.must_equal "suite-platform-s"
424
+ combo('_s__s_', 'pp_').name.must_equal "-s--s--pp-"
425
+ end
426
+ end
427
+
428
+ describe "#run_list" do
429
+
430
+ def combo(suite_list, platform_list)
431
+ Jamie::Instance.new(
432
+ Jamie::Suite.new({ 'name' => 'suite', 'run_list' => suite_list }),
433
+ Jamie::Platform.new({ 'name' => 'platform', 'driver' => 'd',
434
+ 'run_list' => platform_list })
435
+ )
436
+ end
437
+
438
+ it "combines the platform then suite run_lists" do
439
+ combo(%w{s1 s2}, %w{p1 p2}).run_list.must_equal %w{p1 p2 s1 s2}
440
+ end
441
+
442
+ it "uses the suite run_list only when platform run_list is empty" do
443
+ combo(%w{sa sb}, nil).run_list.must_equal %w{sa sb}
444
+ end
445
+
446
+ it "returns an emtpy Array if both run_lists are empty" do
447
+ combo([], nil).run_list.must_equal []
448
+ end
449
+ end
450
+
451
+ describe "#attributes" do
452
+
453
+ def combo(suite_attrs, platform_attrs)
454
+ Jamie::Instance.new(
455
+ Jamie::Suite.new({ 'name' => 'suite', 'run_list' => [],
456
+ 'attributes' => suite_attrs }),
457
+ Jamie::Platform.new({ 'name' => 'platform', 'driver' => 'd',
458
+ 'attributes' => platform_attrs })
459
+ )
460
+ end
461
+
462
+ it "merges suite and platform hashes together" do
463
+ combo(
464
+ { 'suite' => { 's1' => 'sv1' } },
465
+ { 'suite' => { 'p1' => 'pv1' }, 'platform' => 'pp' }
466
+ ).attributes.must_equal({
467
+ 'suite' => { 's1' => 'sv1', 'p1' => 'pv1' },
468
+ 'platform' => 'pp'
469
+ })
470
+ end
471
+
472
+ it "merges suite values over platform values" do
473
+ combo(
474
+ { 'common' => { 'c1' => 'xxx' } },
475
+ { 'common' => { 'c1' => 'cv1', 'c2' => 'cv2' } },
476
+ ).attributes.must_equal({
477
+ 'common' => { 'c1' => 'xxx', 'c2' => 'cv2' }
478
+ })
479
+ end
480
+ end
481
+
482
+ it "#dna combines attributes with the run_list" do
483
+ instance.dna.must_equal({ 's' => 'ss', 'p' => 'pp',
484
+ 'run_list' => [ 'platform_list', 'suite_list' ] })
485
+ end
486
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jamie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.alpha17
4
+ version: 0.1.0.alpha18
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-27 00:00:00.000000000 Z
12
+ date: 2012-12-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
@@ -75,6 +75,54 @@ dependencies:
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: minitest
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: fakefs
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: guard-minitest
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
78
126
  - !ruby/object:Gem::Dependency
79
127
  name: yard
80
128
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +187,22 @@ dependencies:
139
187
  - - ! '>='
140
188
  - !ruby/object:Gem::Version
141
189
  version: '0'
190
+ - !ruby/object:Gem::Dependency
191
+ name: simplecov
192
+ requirement: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ! '>='
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ type: :development
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ! '>='
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
142
206
  - !ruby/object:Gem::Dependency
143
207
  name: countloc
144
208
  requirement: !ruby/object:Gem::Requirement
@@ -167,6 +231,7 @@ files:
167
231
  - .travis.yml
168
232
  - .yardopts
169
233
  - Gemfile
234
+ - Guardfile
170
235
  - LICENSE
171
236
  - README.md
172
237
  - Rakefile
@@ -179,6 +244,7 @@ files:
179
244
  - lib/jamie/thor_tasks.rb
180
245
  - lib/jamie/version.rb
181
246
  - lib/vendor/hash_recursive_merge.rb
247
+ - spec/jamie_spec.rb
182
248
  - templates/plugin/driver.rb.erb
183
249
  - templates/plugin/license_apachev2.erb
184
250
  - templates/plugin/license_gplv2.erb
@@ -186,7 +252,7 @@ files:
186
252
  - templates/plugin/license_mit.erb
187
253
  - templates/plugin/license_reserved.erb
188
254
  - templates/plugin/version.rb.erb
189
- homepage: ''
255
+ homepage: https://github.com/jamie-ci/jamie
190
256
  licenses: []
191
257
  post_install_message:
192
258
  rdoc_options: []
@@ -197,10 +263,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
197
263
  requirements:
198
264
  - - ! '>='
199
265
  - !ruby/object:Gem::Version
200
- version: '0'
201
- segments:
202
- - 0
203
- hash: -4096747563693358273
266
+ version: 1.9.1
204
267
  required_rubygems_version: !ruby/object:Gem::Requirement
205
268
  none: false
206
269
  requirements:
@@ -213,5 +276,6 @@ rubygems_version: 1.8.24
213
276
  signing_key:
214
277
  specification_version: 3
215
278
  summary: A Chef convergence integration test harness
216
- test_files: []
279
+ test_files:
280
+ - spec/jamie_spec.rb
217
281
  has_rdoc: