puppet-debugger 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.gitlab-ci.yml +30 -13
  4. data/.rubocop.yml +3 -1
  5. data/.rubocop_todo.yml +11 -2
  6. data/.ruby-version +1 -1
  7. data/CHANGELOG.md +9 -0
  8. data/DEVELOPMENT.md +6 -1
  9. data/Gemfile +14 -12
  10. data/Plugin_development.md +304 -0
  11. data/README.md +4 -7
  12. data/Rakefile +6 -5
  13. data/bin/pdb +1 -0
  14. data/lib/awesome_print/ext/awesome_puppet.rb +1 -0
  15. data/lib/plugins/puppet-debugger/input_responders/benchmark.rb +40 -0
  16. data/lib/plugins/puppet-debugger/input_responders/classes.rb +15 -0
  17. data/lib/plugins/puppet-debugger/input_responders/classification.rb +14 -0
  18. data/lib/plugins/puppet-debugger/input_responders/commands.rb +95 -0
  19. data/lib/plugins/puppet-debugger/input_responders/datatypes.rb +17 -0
  20. data/lib/plugins/puppet-debugger/input_responders/environment.rb +14 -0
  21. data/lib/plugins/puppet-debugger/input_responders/exit.rb +14 -0
  22. data/lib/plugins/puppet-debugger/input_responders/facterdb_filter.rb +16 -0
  23. data/lib/plugins/puppet-debugger/input_responders/facts.rb +15 -0
  24. data/lib/plugins/puppet-debugger/input_responders/functions.rb +15 -0
  25. data/lib/plugins/puppet-debugger/input_responders/help.rb +14 -0
  26. data/lib/plugins/puppet-debugger/input_responders/krt.rb +14 -0
  27. data/lib/{puppet-debugger/support → plugins/puppet-debugger/input_responders}/play.rb +37 -26
  28. data/lib/plugins/puppet-debugger/input_responders/reset.rb +21 -0
  29. data/lib/plugins/puppet-debugger/input_responders/resources.rb +22 -0
  30. data/lib/plugins/puppet-debugger/input_responders/set.rb +55 -0
  31. data/lib/plugins/puppet-debugger/input_responders/types.rb +35 -0
  32. data/lib/plugins/puppet-debugger/input_responders/vars.rb +18 -0
  33. data/lib/plugins/puppet-debugger/input_responders/whereami.rb +29 -0
  34. data/lib/puppet-debugger.rb +12 -1
  35. data/lib/puppet-debugger/cli.rb +38 -22
  36. data/lib/puppet-debugger/code/code_file.rb +16 -15
  37. data/lib/puppet-debugger/code/code_range.rb +1 -0
  38. data/lib/puppet-debugger/code/loc.rb +1 -0
  39. data/lib/puppet-debugger/debugger_code.rb +1 -0
  40. data/lib/puppet-debugger/hooks.rb +174 -0
  41. data/lib/puppet-debugger/input_responder_plugin.rb +45 -0
  42. data/lib/puppet-debugger/plugin_test_helper.rb +44 -0
  43. data/lib/puppet-debugger/support.rb +13 -9
  44. data/lib/puppet-debugger/support/compiler.rb +1 -0
  45. data/lib/puppet-debugger/support/environment.rb +2 -0
  46. data/lib/puppet-debugger/support/errors.rb +9 -0
  47. data/lib/puppet-debugger/support/facts.rb +2 -1
  48. data/lib/puppet-debugger/support/functions.rb +3 -1
  49. data/lib/puppet-debugger/support/loader.rb +2 -0
  50. data/lib/puppet-debugger/support/node.rb +1 -0
  51. data/lib/puppet-debugger/support/scope.rb +1 -0
  52. data/lib/puppet/application/debugger.rb +1 -0
  53. data/lib/version.rb +2 -1
  54. data/puppet-debugger.gemspec +20 -15
  55. data/run_container_test.sh +1 -1
  56. data/spec/environment_spec.rb +2 -1
  57. data/spec/facts_spec.rb +1 -0
  58. data/spec/hooks_spec.rb +341 -0
  59. data/spec/input_responder_plugin_spec.rb +45 -0
  60. data/spec/input_responders/help_spec.rb +17 -0
  61. data/spec/input_responders/krt_spec.rb +12 -0
  62. data/spec/input_responders/play_spec.rb +160 -0
  63. data/spec/pdb_spec.rb +1 -0
  64. data/spec/puppet/application/debugger_spec.rb +1 -2
  65. data/spec/puppet_debugger_spec.rb +49 -88
  66. data/spec/remote_node_spec.rb +3 -2
  67. data/spec/spec_helper.rb +7 -0
  68. data/spec/support_spec.rb +5 -116
  69. data/test_matrix.rb +2 -0
  70. metadata +65 -12
  71. data/Gemfile.lock +0 -95
  72. data/lib/puppet-debugger/support/input_responders.rb +0 -191
@@ -0,0 +1,45 @@
1
+ require 'singleton'
2
+ require 'puppet-debugger/support/errors'
3
+ require 'forwardable'
4
+
5
+ module PuppetDebugger
6
+ class InputResponderPlugin
7
+ include Singleton
8
+ extend Forwardable
9
+ attr_accessor :debugger
10
+ def_delegators :debugger, :scope, :node, :environment,
11
+ :add_hook, :handle_input, :delete_hook, :function_map
12
+ def_delegators :scope, :compiler, :catalog
13
+ def_delegators :node, :facts
14
+
15
+ def self.command_words
16
+ self::COMMAND_WORDS
17
+ end
18
+
19
+ def self.summary
20
+ self::SUMMARY
21
+ end
22
+
23
+ def self.command_group
24
+ self::COMMAND_GROUP
25
+ end
26
+
27
+ def self.details
28
+ { words: command_words, summary: summary, group: command_group }
29
+ end
30
+
31
+ # @param args [Array[String]] - an array of arguments to pass to the plugin command
32
+ # @param debugger PuppetDebugger::Cli - an instance of the PuppetDebugger::Cli object
33
+ # @return the output of the plugin command
34
+ def self.execute(args = [], debugger)
35
+ instance.debugger = debugger
36
+ instance.run(args)
37
+ end
38
+
39
+ # @param args [Array[String]] - an array of arguments to pass to the plugin command
40
+ # @return the output of the plugin command
41
+ def run(args = [])
42
+ raise NotImplementedError
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,44 @@
1
+ # https://relishapp.com/rspec/rspec-core/v/3-6/docs/example-groups/shared-examples
2
+ RSpec.shared_examples "plugin_tests" do |parameter|
3
+ let(:plugin) do
4
+ instance = PuppetDebugger::InputResponders::Commands.plugin_from_command(subject.to_s).instance
5
+ instance.debugger = debugger
6
+ instance
7
+ end
8
+
9
+ let(:output) do
10
+ StringIO.new
11
+ end
12
+
13
+ let(:debugger) do
14
+ PuppetDebugger::Cli.new({ out_buffer: output }.merge(options))
15
+ end
16
+
17
+ let(:options) do
18
+ {}
19
+ end
20
+
21
+ it "commands contant is an array" do
22
+ expect(plugin.class::COMMAND_WORDS).to be_a(Array)
23
+ end
24
+
25
+ it "commands must contain at least one word" do
26
+ expect(plugin.class::COMMAND_WORDS.count).to be > 0
27
+ end
28
+
29
+ it "summary must be a string" do
30
+ expect(plugin.class::SUMMARY).to be_a(String)
31
+ end
32
+
33
+ it 'implements run' do
34
+ expect{plugin.run([])}.not_to raise_error(NotImplementedError)
35
+ end
36
+
37
+ it 'be looked up via any command words' do
38
+ plugin.class::COMMAND_WORDS.each do |word|
39
+ actual = PuppetDebugger::InputResponders::Commands.plugin_from_command(word.to_s).instance.class
40
+ expect(actual).to eq(plugin.class)
41
+ end
42
+ end
43
+
44
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'puppet/pops'
3
4
  require 'facterdb'
4
5
  require 'tempfile'
@@ -17,8 +18,6 @@ module PuppetDebugger
17
18
  include PuppetDebugger::Support::Functions
18
19
  include PuppetDebugger::Support::Node
19
20
  include PuppetDebugger::Support::Loader
20
- include PuppetDebugger::Support::InputResponders
21
- include PuppetDebugger::Support::Play
22
21
 
23
22
  # parses the error type into a more useful error message defined in errors.rb
24
23
  # returns new error object or the original if error cannot be parsed
@@ -45,6 +44,10 @@ module PuppetDebugger
45
44
  end
46
45
  end
47
46
 
47
+ def static_responder_list
48
+ PuppetDebugger::InputResponders::Commands.command_list
49
+ end
50
+
48
51
  # this is the lib directory of this gem
49
52
  # in order to load any puppet functions from this gem we need to add the lib path
50
53
  # of this gem
@@ -61,10 +64,6 @@ module PuppetDebugger
61
64
  end
62
65
  end
63
66
 
64
- def keyword_expression
65
- @keyword_expression ||= Regexp.new(/^exit|^:set|^play|^classification|^facts|^vars|^functions|^whereami|^classes|^resources|^krt|^environment|^reset|^help/)
66
- end
67
-
68
67
  def known_resource_types
69
68
  res = {
70
69
  hostclasses: scope.environment.known_resource_types.hostclasses.keys,
@@ -136,20 +135,25 @@ module PuppetDebugger
136
135
  # because the repl is not a module we leave the modname blank
137
136
  scope.environment.known_resource_types.import_ast(ast, '')
138
137
 
139
- if $benchmark
138
+ exec_hook :before_eval, '', self, self
139
+ if bench
140
140
  result = nil
141
141
  time = Benchmark.realtime do
142
142
  result = parser.evaluate_string(scope, input, File.expand_path(file))
143
143
  end
144
- [result, "Time elapsed #{(time*1000).round(2)} ms"]
144
+ out = [result, "Time elapsed #{(time * 1000).round(2)} ms"]
145
145
  else
146
- parser.evaluate_string(scope, input, File.expand_path(file))
146
+ out = parser.evaluate_string(scope, input, File.expand_path(file))
147
147
  end
148
+ exec_hook :after_eval, out, self, self
149
+ out
148
150
  end
149
151
  end
150
152
 
151
153
  def puppet_lib_dir
152
154
  # returns something like "/Library/Ruby/Gems/2.0.0/gems/puppet-4.2.2/lib/puppet.rb"
155
+ # "/Users/adam/.rbenv/versions/2.2.6/lib/ruby/gems/2.2.0/gems/puppet-4.9.4/lib"
156
+
153
157
  # this is only useful when returning a namespace with the functions
154
158
  @puppet_lib_dir ||= File.dirname(Puppet.method(:[]).source_location.first)
155
159
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'tempfile'
3
4
 
4
5
  module PuppetDebugger
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module PuppetDebugger
3
4
  module Support
4
5
  module Environment
@@ -7,6 +8,7 @@ module PuppetDebugger
7
8
  def puppet_environment
8
9
  @puppet_environment ||= create_environment
9
10
  end
11
+ alias :environment :puppet_environment
10
12
 
11
13
  # returns an array of module directories, generally this is the only place
12
14
  # to look for puppet code by default. This is read from the puppet configuration
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module PuppetDebugger
3
4
  module Exception
4
5
  class Error < StandardError
@@ -8,9 +9,17 @@ module PuppetDebugger
8
9
  end
9
10
  end
10
11
 
12
+
13
+
11
14
  class FatalError < Error
12
15
  end
13
16
 
17
+ class InvalidCommand < Error
18
+ def message
19
+ data[:message]
20
+ end
21
+ end
22
+
14
23
  class ConnectError < Error
15
24
  def message
16
25
  out = <<-EOF
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module PuppetDebugger
3
4
  module Support
4
5
  module Facts
@@ -46,7 +47,7 @@ module PuppetDebugger
46
47
  node_facts = FacterDB.get_facts(dynamic_facterdb_filter).first
47
48
  if node_facts.nil?
48
49
  message = <<-EOS
49
- Using filter: #{facterdb_filter}
50
+ Using filter: #{dynamic_facterdb_filter}
50
51
  Bad FacterDB filter, please change the filter so it returns a result set.
51
52
  See https://github.com/camptocamp/facterdb/#with-a-string-filter
52
53
  EOS
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module PuppetDebugger
3
4
  module Support
4
5
  module Functions
@@ -43,7 +44,8 @@ module PuppetDebugger
43
44
  obj = {}
44
45
  name = File.basename(file, '.rb')
45
46
  obj[:name] = name
46
- obj[:parent] = mod_finder.match(file)[1]
47
+ # return the last matched in cases where rbenv might be involved
48
+ obj[:parent] = file.scan(mod_finder).flatten.last
47
49
  @functions["#{obj[:parent]}::#{name}"] = obj
48
50
  end
49
51
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module PuppetDebugger
3
4
  module Support
5
+ # the Loader module wraps a few puppet loader functions
4
6
  module Loader
5
7
  def create_loader(environment)
6
8
  Puppet::Pops::Loaders.new(environment)
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'puppet/indirector/node/rest'
3
4
 
4
5
  module PuppetDebugger
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module PuppetDebugger
3
4
  module Support
4
5
  module Scope
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'puppet/application'
3
4
  require 'optparse'
4
5
  require 'puppet/util/command_line'
data/lib/version.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module PuppetDebugger
3
- VERSION = '0.6.1'
4
+ VERSION = '0.7.0'
4
5
  end
@@ -1,37 +1,42 @@
1
1
 
2
+ # frozen_string_literal: true
3
+
2
4
  require './lib/version'
3
5
  require 'date'
4
6
 
5
7
  Gem::Specification.new do |s|
6
- s.name = "puppet-debugger"
8
+ s.name = 'puppet-debugger'
7
9
  s.version = PuppetDebugger::VERSION
8
10
  s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|resources|local_test_results|pec)/}) }
9
- s.bindir = "bin"
10
- s.executables = ["pdb"]
11
+ s.bindir = 'bin'
12
+ s.executables = ['pdb']
11
13
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
12
14
  # to allow pushing to a single host or delete this section to allow pushing to any host.
13
15
  if s.respond_to?(:metadata)
14
16
  s.metadata['allowed_push_host'] = 'https://rubygems.org'
15
17
  else
16
- raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
18
+ raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
17
19
  end
18
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
19
- s.require_paths = ["lib"]
20
- s.authors = ["Corey Osman"]
20
+ s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
21
+ s.require_paths = ['lib']
22
+ s.authors = ['Corey Osman']
21
23
  s.date = DateTime.now.strftime('%Y-%m-%d')
22
- s.description = "A interactive command line tool for evaluating and debugging the puppet language"
23
- s.email = "corey@nwops.io"
24
+ s.description = 'A interactive command line tool for evaluating and debugging the puppet language'
25
+ s.email = 'corey@nwops.io'
24
26
  s.extra_rdoc_files = [
25
- "CHANGELOG.md",
26
- "LICENSE.txt",
27
- "README.md"
27
+ 'CHANGELOG.md',
28
+ 'LICENSE.txt',
29
+ 'README.md'
28
30
  ]
29
31
  s.homepage = "http://github.com/nwops/puppet-debugger"
30
32
  s.licenses = ["MIT"]
31
33
  s.rubygems_version = "2.4.5.1"
32
34
  s.summary = "A repl based debugger for the puppet language"
35
+ s.add_runtime_dependency(%q<pluginator>, ["~> 1.4.1"])
33
36
  s.add_runtime_dependency(%q<puppet>, [">= 3.8"])
34
- s.add_runtime_dependency(%q<facterdb>, [">= 0.3.8"])
35
- s.add_runtime_dependency(%q<awesome_print>, ["~> 1.6"])
37
+ s.add_runtime_dependency(%q<facterdb>, ["~> 0.3.1"])
38
+ s.add_runtime_dependency(%q<awesome_print>, ["~> 1.7"])
36
39
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
37
- end
40
+ s.add_development_dependency(%q<rspec>, ["~> 3.6"])
41
+
42
+ end
@@ -2,7 +2,7 @@
2
2
  export PATH=$PATH:/usr/local/bundle
3
3
  gem install bundler > /dev/null
4
4
  gem update --system > /dev/null
5
- bundle install --no-color --without development
5
+ bundle update
6
6
  bundle exec puppet module install puppetlabs-stdlib
7
7
  echo "Running tests, output to ${OUT_DIR}/results.txt"
8
8
  bundle exec rspec --out "${OUT_DIR}/results.txt" --format documentation
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
  require 'stringio'
4
5
  describe 'environment' do
5
6
  let(:output) do
6
- StringIO.new('', 'w')
7
+ StringIO.new
7
8
  end
8
9
 
9
10
  let(:debugger) do
data/spec/facts_spec.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
 
4
5
  describe 'facts' do
@@ -0,0 +1,341 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe PuppetDebugger::Hooks do
4
+ before do
5
+ @hooks = PuppetDebugger::Hooks.new
6
+ end
7
+
8
+ let(:output) do
9
+ StringIO.new
10
+ end
11
+
12
+ let(:debugger) do
13
+ PuppetDebugger::Cli.new(out_buffer: output)
14
+ end
15
+
16
+ describe "adding a new hook" do
17
+ it 'should not execute hook while adding it' do
18
+ run = false
19
+ @hooks.add_hook(:test_hook, :my_name) { run = true }
20
+ expect(run).to eq false
21
+ end
22
+
23
+ it 'should not allow adding of a hook with a duplicate name' do
24
+ @hooks.add_hook(:test_hook, :my_name) {}
25
+
26
+ expect { @hooks.add_hook(:test_hook, :my_name) {} }.to raise_error ArgumentError
27
+ end
28
+
29
+ it 'should create a new hook with a block' do
30
+ @hooks.add_hook(:test_hook, :my_name) { }
31
+ expect(@hooks.hook_count(:test_hook)).to eq 1
32
+ end
33
+
34
+ it 'should create a new hook with a callable' do
35
+ @hooks.add_hook(:test_hook, :my_name, proc { })
36
+ expect(@hooks.hook_count(:test_hook)).to eq 1
37
+ end
38
+
39
+ it 'should use block if given both block and callable' do
40
+ run = false
41
+ foo = false
42
+ @hooks.add_hook(:test_hook, :my_name, proc { foo = true }) { run = true }
43
+ expect(@hooks.hook_count(:test_hook)).to eq 1
44
+ @hooks.exec_hook(:test_hook)
45
+ expect(run).to eq true
46
+ expect(foo).to eq false
47
+ end
48
+
49
+ it 'should raise if not given a block or any other object' do
50
+ expect { @hooks.add_hook(:test_hook, :my_name) }.to raise_error ArgumentError
51
+ end
52
+
53
+ it 'should create multiple hooks for an event' do
54
+ @hooks.add_hook(:test_hook, :my_name) {}
55
+ @hooks.add_hook(:test_hook, :my_name2) {}
56
+ expect(@hooks.hook_count(:test_hook)).to eq 2
57
+ end
58
+
59
+ it 'should return a count of 0 for an empty hook' do
60
+ expect(@hooks.hook_count(:test_hook)).to eq 0
61
+ end
62
+ end
63
+
64
+ describe "PuppetDebugger::Hooks#merge" do
65
+ describe "merge!" do
66
+ it 'should merge in the PuppetDebugger::Hooks' do
67
+ h1 = PuppetDebugger::Hooks.new.add_hook(:test_hook, :testing) {}
68
+ h2 = PuppetDebugger::Hooks.new
69
+
70
+ h2.merge!(h1)
71
+ expect(h2.get_hook(:test_hook, :testing)).to eq h1.get_hook(:test_hook, :testing)
72
+ end
73
+
74
+ it 'should not share merged elements with original' do
75
+ h1 = PuppetDebugger::Hooks.new.add_hook(:test_hook, :testing) {}
76
+ h2 = PuppetDebugger::Hooks.new
77
+
78
+ h2.merge!(h1)
79
+ h2.add_hook(:test_hook, :testing2) {}
80
+ expect(h2.get_hook(:test_hook, :testing2)).not_to eq h1.get_hook(:test_hook, :testing2)
81
+ end
82
+
83
+ it 'should NOT overwrite hooks belonging to shared event in receiver' do
84
+ h1 = PuppetDebugger::Hooks.new.add_hook(:test_hook, :testing) {}
85
+ callable = proc {}
86
+ h2 = PuppetDebugger::Hooks.new.add_hook(:test_hook, :testing2, callable)
87
+
88
+ h2.merge!(h1)
89
+ expect(h2.get_hook(:test_hook, :testing2)).to eq callable
90
+ end
91
+
92
+ it 'should overwrite identical hook in receiver' do
93
+ callable1 = proc { :one }
94
+ h1 = PuppetDebugger::Hooks.new.add_hook(:test_hook, :testing, callable1)
95
+ callable2 = proc { :two }
96
+ h2 = PuppetDebugger::Hooks.new.add_hook(:test_hook, :testing, callable2)
97
+
98
+ h2.merge!(h1)
99
+ expect(h2.get_hook(:test_hook, :testing)).to eq callable1
100
+ expect(h2.hook_count(:test_hook)).to eq 1
101
+ end
102
+
103
+ it 'should preserve hook order' do
104
+ name = ""
105
+ h1 = PuppetDebugger::Hooks.new
106
+ h1.add_hook(:test_hook, :testing3) { name << "h" }
107
+ h1.add_hook(:test_hook, :testing4) { name << "n" }
108
+
109
+ h2 = PuppetDebugger::Hooks.new
110
+ h2.add_hook(:test_hook, :testing1) { name << "j" }
111
+ h2.add_hook(:test_hook, :testing2) { name << "o" }
112
+
113
+ h2.merge!(h1)
114
+ h2.exec_hook(:test_hook)
115
+
116
+ expect(name).to eq "john"
117
+ end
118
+
119
+ describe "merge" do
120
+ it 'should return a fresh, independent instance' do
121
+ h1 = PuppetDebugger::Hooks.new.add_hook(:test_hook, :testing) {}
122
+ h2 = PuppetDebugger::Hooks.new
123
+
124
+ h3 = h2.merge(h1)
125
+ expect(h3).not_to eq h1
126
+ expect(h3).not_to eq h2
127
+ end
128
+
129
+ it 'should contain hooks from original instance' do
130
+ h1 = PuppetDebugger::Hooks.new.add_hook(:test_hook, :testing) {}
131
+ h2 = PuppetDebugger::Hooks.new.add_hook(:test_hook2, :testing) {}
132
+
133
+ h3 = h2.merge(h1)
134
+ expect(h3.get_hook(:test_hook, :testing)).to eq h1.get_hook(:test_hook, :testing)
135
+ expect(h3.get_hook(:test_hook2, :testing)).to eq h2.get_hook(:test_hook2, :testing)
136
+ end
137
+
138
+ it 'should not affect original instances when new hooks are added' do
139
+ h1 = PuppetDebugger::Hooks.new.add_hook(:test_hook, :testing) {}
140
+ h2 = PuppetDebugger::Hooks.new.add_hook(:test_hook2, :testing) {}
141
+
142
+ h3 = h2.merge(h1)
143
+ h3.add_hook(:test_hook3, :testing) {}
144
+
145
+ expect(h1.get_hook(:test_hook3, :testing)).to eq nil
146
+ expect(h2.get_hook(:test_hook3, :testing)).to eq nil
147
+ end
148
+ end
149
+
150
+ end
151
+ end
152
+
153
+ describe "dupping a PuppetDebugger::Hooks instance" do
154
+ it 'should share hooks with original' do
155
+ @hooks.add_hook(:test_hook, :testing) do
156
+ :none_such
157
+ end
158
+
159
+ hooks_dup = @hooks.dup
160
+ expect(hooks_dup.get_hook(:test_hook, :testing)).to eq @hooks.get_hook(:test_hook, :testing)
161
+ end
162
+
163
+ it 'adding a new event to dupped instance should not affect original' do
164
+ @hooks.add_hook(:test_hook, :testing) { :none_such }
165
+ hooks_dup = @hooks.dup
166
+
167
+ hooks_dup.add_hook(:other_test_hook, :testing) { :okay_man }
168
+
169
+ expect(hooks_dup.get_hook(:other_test_hook, :testing)).not_to eq @hooks.get_hook(:other_test_hook, :testing)
170
+ end
171
+
172
+ it 'adding a new hook to dupped instance should not affect original' do
173
+ @hooks.add_hook(:test_hook, :testing) { :none_such }
174
+ hooks_dup = @hooks.dup
175
+
176
+ hooks_dup.add_hook(:test_hook, :testing2) { :okay_man }
177
+
178
+ expect(hooks_dup.get_hook(:test_hook, :testing2)).not_to eq @hooks.get_hook(:test_hook, :testing2)
179
+ end
180
+
181
+ end
182
+
183
+ describe "getting hooks" do
184
+ describe "get_hook" do
185
+ it 'should return the correct requested hook' do
186
+ run = false
187
+ fun = false
188
+ @hooks.add_hook(:test_hook, :my_name) { run = true }
189
+ @hooks.add_hook(:test_hook, :my_name2) { fun = true }
190
+ @hooks.get_hook(:test_hook, :my_name).call
191
+ expect(run).to eq true
192
+ expect(fun).to eq false
193
+ end
194
+
195
+ it 'should return nil if hook does not exist' do
196
+ expect(@hooks.get_hook(:test_hook, :my_name)).to eq nil
197
+ end
198
+ end
199
+
200
+ describe "get_hooks" do
201
+ it 'should return a hash of hook names/hook functions for an event' do
202
+ hook1 = proc { 1 }
203
+ hook2 = proc { 2 }
204
+ @hooks.add_hook(:test_hook, :my_name1, hook1)
205
+ @hooks.add_hook(:test_hook, :my_name2, hook2)
206
+ hash = @hooks.get_hooks(:test_hook)
207
+ expect(hash.size).to eq 2
208
+ expect(hash[:my_name1]).to eq hook1
209
+ expect(hash[:my_name2]).to eq hook2
210
+ end
211
+
212
+ it 'should return an empty hash if no hooks defined' do
213
+ expect(@hooks.get_hooks(:test_hook)).to eq({})
214
+ end
215
+ end
216
+ end
217
+
218
+ describe "clearing all hooks for an event" do
219
+ it 'should clear all hooks' do
220
+ @hooks.add_hook(:test_hook, :my_name) { }
221
+ @hooks.add_hook(:test_hook, :my_name2) { }
222
+ @hooks.add_hook(:test_hook, :my_name3) { }
223
+ @hooks.clear_event_hooks(:test_hook)
224
+ expect(@hooks.hook_count(:test_hook)).to eq 0
225
+ end
226
+ end
227
+
228
+ describe "deleting a hook" do
229
+ it 'should successfully delete a hook' do
230
+ @hooks.add_hook(:test_hook, :my_name) {}
231
+ @hooks.delete_hook(:test_hook, :my_name)
232
+ expect(@hooks.hook_count(:test_hook)).to eq 0
233
+ end
234
+
235
+ it 'should return the deleted hook' do
236
+ run = false
237
+ @hooks.add_hook(:test_hook, :my_name) { run = true }
238
+ @hooks.delete_hook(:test_hook, :my_name).call
239
+ expect(run).to eq true
240
+ end
241
+
242
+ it 'should return nil if hook does not exist' do
243
+ expect(@hooks.delete_hook(:test_hook, :my_name)).to eq nil
244
+ end
245
+ end
246
+
247
+ describe "executing a hook" do
248
+ it 'should execute block hook' do
249
+ run = false
250
+ @hooks.add_hook(:test_hook, :my_name) { run = true }
251
+ @hooks.exec_hook(:test_hook)
252
+ expect(run).to eq true
253
+ end
254
+
255
+ it 'should execute proc hook' do
256
+ run = false
257
+ @hooks.add_hook(:test_hook, :my_name, proc { run = true })
258
+ @hooks.exec_hook(:test_hook)
259
+ expect(run).to eq true
260
+ end
261
+
262
+ it 'should execute a general callable hook' do
263
+ callable = Object.new.tap do |obj|
264
+ obj.instance_variable_set(:@test_var, nil)
265
+ class << obj
266
+ attr_accessor :test_var
267
+ def call() @test_var = true; end
268
+ end
269
+ end
270
+
271
+ @hooks.add_hook(:test_hook, :my_name, callable)
272
+ @hooks.exec_hook(:test_hook)
273
+ expect(callable.test_var).to eq true
274
+ end
275
+
276
+ it 'should execute all hooks for an event if more than one is defined' do
277
+ x = nil
278
+ y = nil
279
+ @hooks.add_hook(:test_hook, :my_name1) { y = true }
280
+ @hooks.add_hook(:test_hook, :my_name2) { x = true }
281
+ @hooks.exec_hook(:test_hook)
282
+ expect(x).to eq true
283
+ expect(y).to eq true
284
+ end
285
+
286
+ it 'should execute hooks in order' do
287
+ array = []
288
+ @hooks.add_hook(:test_hook, :my_name1) { array << 1 }
289
+ @hooks.add_hook(:test_hook, :my_name2) { array << 2 }
290
+ @hooks.add_hook(:test_hook, :my_name3) { array << 3 }
291
+ @hooks.exec_hook(:test_hook)
292
+ expect(array).to eq [1, 2, 3]
293
+ end
294
+
295
+ it 'return value of exec_hook should be that of last executed hook' do
296
+ @hooks.add_hook(:test_hook, :my_name1) { 1 }
297
+ @hooks.add_hook(:test_hook, :my_name2) { 2 }
298
+ @hooks.add_hook(:test_hook, :my_name3) { 3 }
299
+ expect(@hooks.exec_hook(:test_hook)).to eq 3
300
+ end
301
+
302
+ it 'should add exceptions to the errors array' do
303
+ @hooks.add_hook(:test_hook, :foo1) { raise 'one' }
304
+ @hooks.add_hook(:test_hook, :foo2) { raise 'two' }
305
+ @hooks.add_hook(:test_hook, :foo3) { raise 'three' }
306
+ @hooks.exec_hook(:test_hook)
307
+ expect(@hooks.errors.map(&:message)).to eq ['one', 'two', 'three']
308
+ end
309
+
310
+ it 'should return the last exception raised as the return value' do
311
+ @hooks.add_hook(:test_hook, :foo1) { raise 'one' }
312
+ @hooks.add_hook(:test_hook, :foo2) { raise 'two' }
313
+ @hooks.add_hook(:test_hook, :foo3) { raise 'three' }
314
+ expect(@hooks.exec_hook(:test_hook)).to eq @hooks.errors.last
315
+ end
316
+ end
317
+
318
+ describe "anonymous hooks" do
319
+ it 'should allow adding of hook without a name' do
320
+ @hooks.add_hook(:test_hook, nil) {}
321
+ expect(@hooks.hook_count(:test_hook)).to eq 1
322
+ end
323
+
324
+ it 'should only allow one anonymous hook to exist' do
325
+ @hooks.add_hook(:test_hook, nil) { }
326
+ @hooks.add_hook(:test_hook, nil) { }
327
+ expect(@hooks.hook_count(:test_hook)).to eq 1
328
+ end
329
+
330
+ it 'should execute most recently added anonymous hook' do
331
+ x = nil
332
+ y = nil
333
+ @hooks.add_hook(:test_hook, nil) { y = 1 }
334
+ @hooks.add_hook(:test_hook, nil) { x = 2 }
335
+ @hooks.exec_hook(:test_hook)
336
+ expect(y).to eq nil
337
+ expect(x).to eq 2
338
+ end
339
+ end
340
+
341
+ end