puppet-debugger 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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